次のコードについて考えてみます。
abstract class ExampleClass
{
public static function regularStaticFunction()
{
return static::abstractStaticFunction();
}
abstract protected static function abstractStaticFunction();
}
ExampleClass::regularStaticFunction();
PhpStorm IDEは、abstractStaticFunction
の宣言に次のような警告を表示します。
PHPの厳格な標準:静的関数 'abstractStaticFunction'は抽象的であってはなりません。
静的関数は抽象的であってはなりません。
ただし、PHPは、このクラスを解析するときにプログラムの実行を継続し、以下を出力します。
PHPの厳格な標準:静的関数ExampleClass :: abstractStaticFunction()は、7行目のphpシェルコードで抽象化されるべきではありません
PHPは抽象クラスで静的関数の呼び出しを許可するため、抽象クラスで抽象静的関数を定義することはできないはずです。
抽象静的関数が無意味なのに、インタプリタによってPHPで許可されるのはなぜですか?
これは この答え by Mark Amery からの良い説明です:
PHPバグレポート53081 、
static::foo()
構文の追加により、抽象静的メソッドが合理的かつ有用になったため、警告を削除するよう求められました。 Rasmus Lerdorf(PHPの作成者)は、リクエストに偽物のラベルを付けることから始め、警告を正当化するために長い一連の悪い推論を行います。そして、最後に、この交換が行われます。ジョルジオ
知ってるけど:
abstract class cA { //static function A(){self::B();} error, undefined method static function A(){static::B();} // good abstract static function B(); } class cB extends cA { static function B(){echo "ok";} } cB::A();
ラスムス
そうです、まさにそれが機能するはずです。
ジョルジオ
しかし、それは許可されていません:(
ラスムス
許可されていないものは何ですか?
abstract class cA { static function A(){static::B();} abstract static function B(); } class cB extends cA { static function B(){echo "ok";} } cB::A();
これは正常に機能します。明らかにself :: B()を呼び出すことはできませんが、static :: B()は問題ありません。
Rasmusによる、彼の例のコードは「正常に機能する」という主張は誤りです。ご存知のように、厳密モードの警告がスローされます。彼は厳密モードをオンにせずにテストしていたと思います。とにかく、混乱したラスムスは、リクエストを誤って「偽物」として閉じたままにしました。
そしてそれが警告がまだ言語にある理由です。これは完全に満足のいく説明ではないかもしれません-あなたはおそらく警告の合理的な正当化があったことを期待してここに来ました。残念ながら、現実の世界では、合理的な意思決定からではなく、ありふれた間違いや悪い推論から選択が生まれることがあります。これは単にその時の1つです。
幸いなことに、推定可能なNikitaPopovはPHP 7の一部として PHP RFC:E_STRICT通知の再分類 の言語から警告を削除しました。最終的には正気が優勢になりました。 PHP 7がリリースされました。私たちは皆、楽しく使用できます
abstract static
このばかげた警告を受けずに。
抽象静的関数は無意味ではありません!実際、それらは、私の感性では、JavaやC#のように、それらを持たない言語で頼らなければならないものよりも単純でクリーンないくつかのデザインを可能にします。
例を見てみましょう。 2つのAPI間でいくつかのビジネスオブジェクトを同期する必要がある、ある種のありふれたエンタープライズビジネスアプリケーションを作成しているとします。これらのAPIには、相互にマッピングできるオブジェクトモデルがありますが、使用する名前とシリアル化形式は異なります。
PHPでは、抽象静的メソッドのおかげで、これらのビジネスオブジェクトタイプの抽象基本クラスを次のように定義できます...
abstract class ApiObject {
/** The REST resource URL for this object type in the Foo API. */
abstract static function fooApiResourceUrl();
/** The REST resource URL for this object type in the Bar API. */
abstract static function barApiResourceUrl();
/** Given an XML response from the Foo API representing an object of this
type, construct an instance. */
abstract static function fromFooXml($xml);
/** Given a JSON response from the Bar API representing an object of this
type, construct an instance. */
abstract static function fromBarJson($json);
/** Serialize this object in the XML format that the Foo API understands */
abstract function toFooXml();
/** Serialize this object as JSON that the Bar API understands */
abstract function toBarJson();
}
...そして、私が作成するすべての具体的なサブクラスは保証であり、2つのAPIのいずれかからフェッチして逆シリアル化するか、シリアル化していずれかのAPIに送信するために必要なすべての情報を提供します。その後、次のようなコードを記述できます。
// Ensure that all instances of these types that exist in the Foo API also
// exist in the Bar API:
$classesToSync = ['Widget', 'Frobnicator', 'Lead', 'Invoice'];
foreach ($classesToSync as $apiObjectClass) {
$fooObjXmls = httpGetRequest($apiObjectClass::fooApiResourceUrl());
foreach ($fooObjXmls as $fooObjXml) {
$fooObj = $apiObjectClass::fromFooXml($fooObjXml);
$json = $fooObj->toBarJson();
httpPutRequest($apiObjectClass::barApiResourceUrl(), $json);
}
}
上記のプログラムを書くために厳密に必要静的メソッドを抽象化しますか?番号;すべてのモデルクラスを、JSONまたはXML表現からのインスタンス化を担当する対応するファクトリクラスとペアにするなど、使用できるパターンは他にもあります。しかし、そのような設計は、私が上に示したものよりも複雑です。
それで、なぜそれらが許可されるのかに対する答えは、それらがなければ不可能であるいくつかの素晴らしく単純なパターンを可能にするので、それらが便利であるということです。もちろん、引数もありますに対してそれらは存在し、そのうちの1つは質問で与えられました-抽象クラスの静的メソッドが呼び出し可能であることを考えると、クラスの抽象静的メソッドを公開するのは醜いです。そのような考慮事項は特に説得力があるとは思いませんが、たとえそうだとしても、それらと抽象静的メソッドが提供するユーティリティとの間にはトレードオフがあり、PHPメンテナはおそらくそれらを比較検討し、抽象静的メソッドを存在させる側を選択しました。