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


121

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

厳密な標準静的関数 Program :: getSelectSQL() Program.class.inc で抽象であってはなりません

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

私はこの変更への言及をここで見つけました

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

私の質問は次のとおりです。なぜPHPに抽象静的関数を使用すべきではないのか、誰かが明確に説明できますか?


12
新しい読者は、この非合理的な制限がPHP 7で削除されたことに注意する必要があります
Mark Amery

回答:


76

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

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

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

CORRECTION(philfreoによっては)
abstract staticまだPHP 5.3で許可されていない、LSB関連するが異なるされます。


3
では、抽象クラスを拡張するすべての子で関数getSelectSQL()の必要性を強制したい場合はどうでしょうか。親クラスのgetSelectSQL()が存在する正当な理由がありません。最善の行動計画は何ですか?私が抽象静的を選択した理由は、すべての子にgetSelectSQL()を実装するまでコードがコンパイルされないためです。
Artem Russakovskii

1
ほとんどの場合、getSelectSQL()が抽象的な/ instance /メソッドになるように再設計する必要があります。そうすることで、各子の/ instances /がそのようなメソッドを持つことになります。
Matthew Flaschen、2009年

7
PHP 5.3では、抽象staticはまだ許可されていません。遅い静的バインディングはそれとは何の関係もありません。stackoverflow.com/questions/2859633
Artefacto

40
私の意見では、PHPには「レイトスタティックバインディング」があり、クラス自体がオブジェクトであるかのように静的メソッドを使用するというアイデアを自然に提供するため、この厳密な警告は愚かです(たとえば、Rubyのように)。これは静的メソッドのオーバーロードにつながりabstract static、この場合に役立つ可能性があります。
ドミトリー2013年

4
この答えは漠然と間違っています。「まだ許可されていない」とは、E_STRICTレベルの警告が表示されることを意味します。少なくとも5.3以降では、抽象静的関数を作成し、拡張クラスに実装して、static ::キーワードを介してそれらを参照することを完全に歓迎します。明らかに、親クラスの静的バージョンはまだ存在し、抽象的であり、通常の非静的抽象関数を呼び出したかのように致命的にエラーになるため、直接(そのクラス内でself ::またはstatic ::を介して)呼び出すことはできません。機能的にはこれは便利です。その効果に対する@dmitryの感情に同意します。
ahoffner 2014年

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()明示的にのbar()メソッドを参照します。ストリクトモードをオフにしてこのコードを実行しようとすると、「PHPの致命的なエラー:抽象メソッドParentClass :: bar()を呼び出せません」が表示されます。ParentClassfoo()ChildClass

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

しかし、その後、PHP 5.3が追加され、staticキーワードを介してメソッドが呼び出されたクラスを参照する機能が追加されました(self常に、メソッドが定義されたクラスを参照するキーワードとは異なります)。上記の例でに変更self::bar()するstatic::bar()と、PHP 5.3以降で問題なく機能します。あなたは、詳細について読むことができるselfstatic新しい静的対新しい自己

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

あなたはまだ、警告を維持するための主張をすることができると思います。たとえば、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()で十分です。

彼の例のコードが「正常に機能する」というラスムスの主張は誤りです。ご存知のように、それは厳格なモード警告をスローします。strictモードをオンにせずにテストしていたと思います。とにかく、混乱したラスムスはリクエストを誤って「偽物」として閉じたままにしました。

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

幸運なことに、推定可能なNikita Popovは、PHP RFCの一部としてPHP 7の言語から警告を削除しました:E_STRICT通知の再分類。結局のところ、正気が広まり、PHP 7がリリースされるとabstract static、このばかげた警告を受け取らなくても、誰でも楽しく使用できます。


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静的メソッドとパブリックgetSomeDataメソッドを実装するように強制します。また、MyFooをサブクラス化しない場合でも、iMyFooを実装して同様の機能を持つクラスを作成できます。


2
機能を作るために、このパターンを持つことが可能である保護されました。私がそれをするとき、それはMyFooを拡張するクラスがgetInstanceがパブリックでなければならないという警告を投げることを意味します。また、インターフェイス定義に保護を設定することはできません。
artfulrobot 2013

3
場合static::によっては役立つことがあります。
2014年

3
トレイトでは機能しません。トレイトだけがabstract staticメソッドを持つことができ、PHPのビッチなしで......
Rudie

1
インターフェイスの使用は、おそらくここでの最良のソリューションです。+1。
ファンカルロスコト2015年

非常にシンプルなので、これは実際には非常にエレガントです。+1
G.スチュワート

12

これが古いのは知っているけど……

親クラスの静的メソッドに例外をスローするだけではなく、オーバーライドしないと例外が発生します。


1
それは役に立たない、例外は静的メソッドの呼び出しで発生します-オーバーライドしないと「メソッドが存在しない」エラーが同時に発生します。
BT

3
@BTつまり、メソッドの抽象を宣言せずに実装しますが、呼び出されたときに例外をスローするだけです。つまり、オーバーライドされてもスローされません。
Petah

これは最もエレガントなソリューションのようです。
Alex S 14

実行時よりもコンパイル時にこのようなものを表示する方が良いです。あなただけのファイルをロードする必要があるため、彼らが生産している前に、その悪いか不適合かどうかを把握するためのコードを実行し、問題がない見つけることが容易
Rahly

4

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


ここに関連するPHPではないが、別の良い説明は次のとおりです。stackoverflow.com/questions/3284/...
merkuro

3
我が神よ!あなたに反対票を投じた2人は完全に頭を悩ませています!これは、このスレッドで最も洞察に満ちた答えです。
セオドアR.スミス

5
@ TheodoreR.Smith Insightful?エラーが含まれ、ほとんど一貫性がありません。抽象クラスが「実際の機能を実装していないという主張は必ずしも真実ではなく、それがインターフェースに加えて存在するそれらの全体のポイントです。「php開発者がそれを行うのを妨げるのは自然な法律ではない」という主張の中で、私は「それ」が何であるかを知りません。そして、「基本的にこれらのアイデアは予期しない動作を防止しようとする」という主張では、「これらのアイデア」または潜在的な「予期しない動作」が何であるかはわかりません。あなたがこれから抽出した洞察が何であれ、それは私には失われています。
マークアメリー

2

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


3
5.2に存在する抽象静的メソッドに関するあなたの主張は、非常に誤解を招くものです。最初に、それらを禁止する厳格モード警告は5.2で導入されました。5.2で許可されているように聞こえるようにします。次に、5.2では、「クラスの一部を実装し、クラスの別の部分をユーザーに任せる」ために、レイトスタティックバインディングがまだ存在していなかったためそれらを簡単に使用できませんでした。
マークアメリー2015

0

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

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

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

use StaticExample;

1
abstract public static function get_table_name();E_STRICT警告を出さずに、トレイトを入れて、抽象クラス内でそのトレイトを使用することができました。これは、私が望んでいたように、まだ子供たちに静的メソッドを定義することを強制しました。素晴らしい!
Programster

-1

PHPの「遅延静的バインディング」の問題を調べてください。静的メソッドを抽象クラスに配置する場合は、おそらく後で実行するよりも早く実行するでしょう。厳密な警告が、壊れた言語機能の使用を回避するように指示していることは理にかなっています。


4
彼は「厳格な基準」の警告を意味すると思います。
Jacob Hume

2
レイトスタティックバインディングにはどのような「問題」があると主張しますか?あなたはそれらが「壊れている」と断言します。これは大胆な主張であり、証拠も説明もなしにここで行われました。この機能は私にとって常にうまく機能しており、この投稿はナンセンスだと思います。
Mark Amery
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.