122

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

厳格な基準:静的関数Program::getSelectSQL()はProgram.class.incで抽象化してはなりません

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

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

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

私の質問は、PHP に抽象静的関数があってはならない理由を誰かが明確に説明できますか?

4

8 に答える 8

79

長くて悲しい話です。

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

<?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()明示的に参照します。厳密モードをオフにしてこのコードを実行しようとすると、「PHP Fatal error: Cannot call abstract method ParentClass::bar()」が表示されます。bar()ParentClassfoo()ChildClass

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

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

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

それでも、警告を維持することを主張することはできると思います。たとえば、PHP では抽象クラスの静的メソッドを呼び出すことができるため、上記の例では ( に置き換えselfて修正した後でも)、壊れたパブリック メソッドをstatic公開していて、実際にはそうしたくないと主張することができます。公開。非静的クラスを使用する - つまり、すべてのメソッドをインスタンス メソッドにし、すべての子をシングルトンか何かにする - はこの問題を解決します。呼ばれます。この議論は弱いと思います(ParentClass::foo()ParentClassParentClassParentClass::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 の主張は誤りです。ご存じのとおり、厳格モードの警告がスローされます。彼は厳密モードをオンにせずにテストしていたと思います。とにかく、混乱した Rasmus は、リクエストを「偽物」として誤って閉じたままにしました。

そして、それが警告がまだ言語にある理由です。これは完全に満足のいく説明ではないかもしれません - あなたはおそらく警告の合理的な正当化を期待してここに来ました. 残念ながら、現実の世界では、合理的な意思決定ではなく、ありふれた過ちや不適切な推論から選択が生まれることがあります。これは単にそれらの時間の 1 つです。

幸いなことに、評価の高い Nikita Popov が、 PHP RFC: Reclassify E_STRICT noticesの一部として、PHP 7 の言語から警告を削除しました。最終的には正気を保てるようになり、PHP 7 がリリースされれabstract staticば、このばかげた警告を受けることなく、誰もが問題なく使用できるようになります。

于 2015-07-05T23:14:38.133 に答える
76

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

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

編集(2009年9月16日)
これに関する更新。PHP 5.3を実行すると、良いか悪いかにかかわらず、abstractstaticが復活しました。(詳細については、 http://php.net/lsbを参照してください)

訂正( philfreoによる)
abstract staticはPHP 5.3ではまだ許可されていません。LSBは関連していますが、異なります。

于 2009-06-16T00:14:31.167 に答える
70

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

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

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

<?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 を実装して同様の機能を持つクラスを作成できます。

于 2011-06-17T13:17:44.903 に答える
12

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

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

于 2010-06-16T11:28:02.330 に答える
4

抽象クラス/インターフェースは、プログラマー間の契約と見なすことができると私は主張します。実際の機能を実装するのではなく、物事がどのように見える/どのように振る舞うべきかについてより多くを扱います。php5.0 と 5.1.x に見られるように、php 開発者がそれを行うのを妨げるのは自然法則ではなく、他の言語で他の OO 設計パターンに合わせたいという衝動です。基本的に、これらのアイデアは、すでに他の言語に精通している場合、予期しない動作を防止しようとします。

于 2009-06-16T00:31:40.780 に答える
2

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

于 2013-11-07T09:11:23.173 に答える
0

PHP 5.4+ では、trait を使用します:

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

そしてあなたのクラスでは、最初に:

use StaticExample;
于 2014-09-01T11:22:06.223 に答える
-1

PHP の「Late Static Binding」の問題を調べてください。抽象クラスに静的メソッドを配置している場合、おそらく遅かれ早かれそれに遭遇するでしょう。厳密な警告が、壊れた言語機能を使用しないように指示していることは理にかなっています。

于 2009-06-16T01:23:09.863 に答える