負けた
追記:静的無名関数を追加して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.遅延静的束縛
静的メンバにアクセスする際に使う特別なクラスに
self と static があります
- 
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
ただし self や static はバインドできる
<?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 は難しい
まとまってない
