負けた
追記:静的無名関数を追加して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
は難しい
まとまってない