web-dev-qa-db-ja.com

なぜPHP 5.2+は抽象静的クラスメソッドを許可しないのですか?

PHP 5.2で厳格な警告を有効にした後、当初は厳格な警告なしで作成されたプロジェクトから、多数の厳格な標準警告が表示されました。

厳格な基準静的関数Program :: getSelectSQL()Program.class.incでは抽象的ではない

問題の関数は抽象親クラスProgramに属し、TVProgramなどの子クラスに実装する必要があるため、抽象静的と宣言されています。

私はこの変更への参照を見つけました here

抽象静的クラス関数を削除しました。見落としのため、PHP 5.0.xおよび5.1.xでは、クラスで抽象静的関数を使用できました。 PHP 5.2.xの時点では、インターフェースのみがそれらを保持できます。

私の質問は次のとおりです。PHPに抽象的な静的関数が存在してはならない理由を誰かが明確に説明できますか。

120

静的メソッドは、それらを宣言したクラスに属します。クラスを拡張する場合、同じ名前の静的メソッドを作成できますが、実際には静的抽象メソッドを実装していません。

静的メソッドでクラスを拡張する場合も同様です。そのクラスを拡張して同じシグネチャの静的メソッドを作成する場合、実際にはスーパークラスの静的メソッドをオーバーライドしているわけではありません

[〜#〜] edit [〜#〜](2009年9月16日)
これに関する更新。実行中PHP 5.3、良かれ悪しかれ、抽象静的が戻ってきた。詳細は http://php.net/lsb を参照)

[〜#〜]修正[〜#〜](by philfreo)
abstract staticはPHP 5.3、 [〜#〜] lsb [〜#〜] では許可されていませんが、関連していますが異なります。

76

長くて悲しい話です。

PHP 5.2が最初にこの警告を導入したとき、 late static bindings はまだ言語にありませんでした。遅延静的バインディングに精通していない場合は、このようなコードが期待どおりに機能しないことに注意してください。

<?php

abstract class ParentClass {
    static function foo() {
        echo "I'm gonna do bar()";
        self::bar();
    }

    abstract static function bar();
}

class ChildClass extends ParentClass {
    static function bar() {
        echo "Hello, World!";
    }
}

ChildClass::foo();

ストリクトモードの警告は別として、上記のコードは機能しません。 self::bar()foo()呼び出しは、bar()ParentClassのメソッドとして呼び出された場合でも、ChildClassfoo()メソッドを明示的に参照します。 strictモードをオフにしてこのコードを実行しようとすると、「PHP致命的エラー:抽象メソッドParentClass :: bar()を呼び出せません」が表示されます。 。

これを考えると、PHP 5.2の抽象静的メソッドは役に立たなかった。抽象メソッドを使用することの全体のポイントは、どの実装を呼び出すかわからないままメソッドを呼び出すコードを記述できることです。異なる子クラスの実装。しかし、PHP 5.2は、それが呼び出される子クラスの静的メソッドを呼び出す親クラスのメソッドを作成するための明確な方法を提供しないため、この抽象静的メソッドの使用は不可能です。したがって、PHP 5.2でのabstract staticの使用は不適切なコードであり、おそらくselfキーワードの動作の誤解に触発されたものです。これについて警告を投げることは完全に合理的でした。

しかし、PHP 5.3は、staticキーワードを介してメソッドが呼び出されたクラスを参照する機能に追加されました(メソッドが常にあったクラスを参照するselfキーワードとは異なりますdefined)。上記の例でself::bar()static::bar()に変更すると、PHP 5.3以降で正常に動作します。 self vs staticの詳細については、 New self vs. new static を参照してください。

Staticキーワードを追加すると、abstract staticが警告をスローする明確な引数がなくなりました。後期静的バインディングの主な目的は、親クラスで定義されたメソッドが、子クラスで定義された静的メソッドを呼び出せるようにすることでした。抽象静的メソッドを許可することは、静的バインディングが存在することを考えると、合理的で一貫しているように見えます。

それでも、警告を維持することを主張することができます。たとえば、PHPは抽象クラスの静的メソッドを呼び出すことができるため、上記の例では(selfstaticに置き換えて修正した後でも)パブリックメソッドParentClass::foo()を公開していると主張できますbrokenそして、あなたが本当に公開したくないこと。非静的クラスを使用すると、つまり、すべてのメソッドインスタンスメソッドを作成し、ParentClassの子をすべてシングルトンまたは何かにすることで、ParentClassは抽象化できず、そのインスタンスメソッドはインスタンス化できないため、この問題を解決できます。呼ばれません。この引数は弱いと思います(ParentClass::foo()を公開することは大したことではなく、静的クラスの代わりにシングルトンを使用することは不必要に冗長でいことが多いためです)が、あなたは合理的に同意しないかもしれません-それはやや主観的な呼び出しです。

したがって、この引数に基づいて、PHP開発者は言語で警告を維持しましたよね?

ええと、 正確ではありません

上にリンクされた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つです。

幸いなことに、推定可能なNikita Popovは、 PHP RFCの一部としてPHP 7の言語から警告を削除しました:E_STRICT通知の再分類 。最終的には正気が広まり、PHP 7がリリースされると、この愚かな警告を受け取らずにabstract staticを喜んで使用できます。

72
Mark Amery

この問題には非常に簡単な回避策がありますが、実際には設計の観点から理にかなっています。ジョナサンが書いたように:

静的メソッドでクラスを拡張する場合も同様です。そのクラスを拡張して同じシグネチャの静的メソッドを作成する場合、実際にはスーパークラスの静的メソッドをオーバーライドしているわけではありません

したがって、回避策としてこれを行うことができます:

<?php
abstract class MyFoo implements iMyFoo {

    public static final function factory($type, $someData) {
        // don't forget checking and do whatever else you would
        // like to do inside a factory method
        $class = get_called_class()."_".$type;
        $inst = $class::getInstance($someData);
        return $inst;
    }
}


interface iMyFoo {
    static function factory($type, $someData);
    static function getInstance();
    function getSomeData();
}
?>

そして、MyFooをサブクラス化するクラスがgetInstance静的メソッドとpublic getSomeDataメソッドを実装することを強制します。また、MyFooをサブクラス化しない場合でも、iMyFooを実装して、同様の機能を持つクラスを作成できます。

70

私はこれが古いことを知っていますが....

なぜその親クラスの静的メソッドに例外をスローしないのか、そうしないと例外が発生します。

12
Petah

抽象クラス/インターフェースはプログラマー間の契約とみなすことができると私は主張します。実際の機能を実装せずに、外観/動作をどのようにすべきかをさらに扱います。 php5.0と5.1.xに見られるように、PHP開発者がそれを行うことを妨げるのは自然法則ではありませんが、他の言語のOO設計パターン。他の言語に既に精通している場合、アイデアは予期しない動作を防止しようとします。

4
merkuro

静的な抽象関数を禁止する理由は見当たりません。それらを禁止する理由がないという最良の議論は、Javaで許可されているということです。質問は次のとおりです。-技術的に実行可能ですか? -はい、PHP 5.2に存在し、Javaに存在します。だから、それを行うことができます。それを行う必要があります。-意味がありますか?はい。非静的関数では意味がありますが、なぜ静的関数に意味がないのですか?静的関数の使用法の1つは、複数のインスタンスが存在してはならないクラスです(シングルトン)。たとえば、暗号化エンジン。複数のインスタンスに存在する必要はなく、これを防ぐ理由があります-たとえば、メモリの一部のみを侵入者から保護する必要があります。エンジンの一部であり、暗号化アルゴリズムはユーザーに任せますこれは一例に過ぎません。

2
user2964021

PHP 5.4以降では、特性を使用します。

trait StaticExample {
    public static function instance () {
    return new self;
    }
}

そしてあなたのクラスで最初に置く:

use StaticExample;
0
kamil