5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PHPの「static」は「4種類」??

Last updated at Posted at 2020-01-19

JavaScriptの「this」は「4種類」??

負けた

追記:静的無名関数を追加してJavaScriptに並びました!

1.静的メンバ

静的プロパティ、静的メソッドを定義するために使うパターン

<?php

class Example
{
    /**
     * @var string
     */
    protected static $staticProperty = '静的プロパティ';

    /**
     * @return string
     */
    final public static function getStaticProperty(): string
    {
        return self::$staticProperty;
    }
}


echo Example::getStaticProperty();
// 静的プロパティ

一番よく使うパターン

クラス自体に持たせるメンバ
インスタンス化しないで使う

静的メンバには :: (スコープ演算子)を使ってアクセスする
スコープ演算子の左辺は クラス 、右辺は 静的メンバ

2.静的変数

  • コンパイルされる時に 1度だけ 初期化される
    • 同スコープ内で複数回初期化される場合 最後に初期化される値 になる
    • 変数 や、インスタンス を使って初期化 できない
  • スコープが外れてもガベコレされない
    • 再度初期化された時、前回の値で初期化される
  • メソッドの中で使うと静的プロパティと同じ挙動をする

最初の一度のみ 0 で初期化され
スコープが外れてもガベコレされず
前回の値が入っていることがわかる例

<?php

/**
 * カウントアップする
 */
function countUp(): int
{
    static $cnt = 0;
    return ++$cnt;
}

echo countUp();
// 1
echo countUp();
// 2
echo countUp();
// 3

参照を切ってもガベコレされない
同スコープ内で複数回初期化される場合 最後に初期化された値になる
再度宣言された時、前回の値で初期化される例

<?php

/**
 * 第1引数で指定した名前の変数が存在する場合、その値をechoします
 * 存在しない場合は「参照ないよ」とechoします
 *
 * @param string $valName
 * @return void
 */
function echoIfExists(string $valName): void
{
    echo $GLOBALS[$valName] ?? '参照ないよ', PHP_EOL;
}

// 静的変数を初期化する(1回目)
static $staticVal = '静的変数_1';
echoIfExists('staticVal');
// 静的変数_2
// ↑1じゃなく2で初期化されている

$staticVal = '値を書き換える';

// 参照消す(普通の変数ならガベコレされる)
unset($staticVal);
echoIfExists('staticVal');
// 参照無いよ

// 静的変数を初期化する(2回目)
static $staticVal = '静的変数_2';
echoIfExists('staticVal');
// 値を書き換える
// ↑前回の値で初期化されている

変数を使って初期化できない例

<?php

$val = '適当な変数';

// 変数で初期化を試みる
static $staticVal = $val;
// PHP Fatal error:  Constant expression contains invalid operations in ...

変数を使っても定義できる

<?php

$val = '変数';
define('MY_CONST', $val); // 変数を定数にする
static $staticVal = MY_CONST;
echo $staticVal;
// 変数

インスタンスを使って初期化できない例

<?php

// インスタンスで初期化を試みる
static $staticVal = new class {};
// PHP Fatal error:  Constant expression contains invalid operations in ...

もちろんクロージャもインスタンスなのでだめ

<?php

// クロージャで初期化を試みる
static $staticVal = function () {};
// PHP Fatal error:  Constant expression contains invalid operations in ...

ユーザー定義定数を使って初期化できない例

<?php

static $staticVal = '普通に初期化';
// PHP Warning:  Use of undefined constant MY_CONST - assumed 'MY_CONST'

$val = '変数';
define('MY_CONST', $val); // 変数を定数にする
static $staticVal = MY_CONST;

同スコープ内で複数回初期化される場合 最後に初期化された値 になる ため
MY_CONST で初期化しようとするけど
最初の初期化のタイミングには MY_CONST は存在しないのでエラーになる

メソッドの中で使うと静的プロパティと大体同じ挙動をする例

PHPの静的変数 (static変数) の挙動まとめ

静的プロパティとメソッド内の静的変数の違いは
参照を切れるか外からアクセスできるか

プロパティは unset() できないけど
静的変数は unset() できる

静的プロパティはメンバなので Reflection を使えば
private であってもアクセスすることができる
静的変数はローカルスコープなので絶対にアクセスできない

3.遅延静的束縛

静的メンバにアクセスする際に使う特別なクラスに
selfstatic があります

  • self はそのメソッドが定義してあるクラス自身
    • コンパイルされるときに決定(束縛)される
  • static はそのメソッドが呼び出されたクラス自身
    • ランタイムで決定(束縛)される

名前の通り遅延して束縛されるわけですね

具体的にどういう違いがあるかというと、継承したときに違いが現れます

<?php

class Base
{
    /**
     * selfが指すクラスをechoする
     *
     * @return void
     */
    public static function echoSelfClass(): void
    {
        echo self::class;
    }

    /**
     * staticが指すクラスをechoする
     *
     * @return void
     */
    public static function echoStaticClass(): void
    {
        echo static::class;
    }
}

class Sample extends Base {}

Sample::echoSelfClass();
// Base
Sample::echoStaticClass();
// Sample

4.静的無名関数

インスタンスをバインドできない無名関数
静的無名関数の中で $this を使おうとするとエラーになる

<?php

class Hoge
{
    public function __toString(): string
    {
        return 'hoge';
    }

    public function closure()
    {
        $closure = function () {
            echo $this, PHP_EOL;
        };
        $closure();
    }

    public function staticClosure()
    {
        $staticClosure = static function () {
            echo $this, PHP_EOL;        
        };
        $staticClosure();
    }
}

$h = new Hoge();
$h->closure();
// hoge
$h->staticClosure();
// Fatal error: Uncaught Error: Using $this when not in object context in

また静的無名関数にインスタンスをバインドしようとしてもエラーになる

$closure = static function () {};
$closure->bindTo(new class {});
// Warning: Cannot bind an instance to a static closure in

ただし selfstatic はバインドできる

<?php

class Hoge
{
    protected static $hoge = 'hoge';

    public function staticClosure()
    {
        $staticClosure = static function () {
            echo self::$hoge, PHP_EOL, static::$hoge, PHP_EOL;        
        };
        $staticClosure();
    }
}

class Fuga extends Hoge
{
    protected static $hoge = 'fuga';
}


$h = new Fuga();
$h->staticClosure();
// hoge
// fuga

まとめ

PHPの static は難しい

まとまってない

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?