web-dev-qa-db-ja.com

PHP)の静的メンバーの継承

PHPでは、静的属性が親クラスで定義されている場合、子クラスでオーバーライドすることはできません。しかし、私はこれを回避する方法があるかどうか疑問に思っています。

他の誰かの(やや不格好な)関数のラッパーを書き込もうとしています。問題の関数は多くの異なるデータ型に適用できますが、それぞれに異なるフラグとオプションが必要です。ただし、99%の場合、各タイプのデフォルトで十分です。

毎回新しい関数を作成することなく、継承を使用してこれを実行できれば便利です。例えば:

class Foo {
    public static $default = 'DEFAULT';

    public static function doSomething ($param = FALSE ) {
        $param = ($param === FALSE) ? self::$default : $param;
        return $param;
    }
}

class Bar extends Foo {
    public static $default = 'NEW DEFAULT FOR CHILD CLASS';
}

echo Foo::doSomething() . "\n"; 
// echoes 'DEFAULT'

echo Bar::doSomething() . "\n"; 
// echoes 'DEFAULT' not 'NEW DEFAULT FOR CHILD CLASS' 
// because it references $default in the parent class :(
34
PartialOrder

静的変数をグローバル(この場合は関数)として使用することが、言語に関係なく悪い考えである理由の典型的な例。

最も堅牢な方法は、抽象ベースの「アクション」クラスの複数の実装サブクラスを作成することです。

次に、クラスのインスタンスをインスタンス化してそのメソッドを呼び出すだけの煩わしさを取り除くために、ある種のファクトリでラップすることができます。

例えば:

abstract class AbstractAction {
  public abstract function do();
}

class FooAction extends AbstractAction {
  public function do() {
    echo "Do Foo Action";
  }
}

class BarAction extends AbstractAction {
  public function do() {
    echo "Do Bar Action";
  }
}

次に、関数のインスタンス化を「支援」するファクトリを作成します

class ActionFactory {
  public static function get($action_name) {
    //... return AbstractAction instance here
  }  
}

次に、次のように使用します。

ActionFactory::get('foo')->do();
16
Allain Lalonde

実際、私はそれが真実ではないと思います:静的プロパティをovverrideすることができます(そのためには> = 5.3PHPが必要です)。しかし、その静的プロパティを参照するときは注意する必要があります(これは元のコードの間違いです)

self :: $ myStaticPropertyを使用する代わりに、static :: $ myStaticPropertyを使用する必要があります

self ::現在のクラスを参照するため、継承された静的メソッド内にいる場合、これはそのメソッドを定義したそのクラスの静的プロパティを参照します!参照キーワードstatic ::を使用している間は、$ thisのように動作します-インスタンスメソッド/プロパティを使用している場合。

doSomething()は、例のクラスBarの継承された静的メソッドです。 self :: thereを使用したので、クラスFooの静的プロパティを参照します。これが違いが見られなかった理由です...self ::をstatic ::に変更してみてください!

これがコード例です-私はそれを自分で使ってそれらをテストしました。静的なプロパティ/メソッドの継承、オーバーライド、値の変更があります。実行すると、結果が表示されます。

class A {

    // a static property - we will test override with it
    protected static $var = 'class A var - override';
    // a static property - we will test value overwrite with it
    protected static $var2 = 'class A var2 - value overwrite';


    public static function myStaticOverridePropertyTest() {
        return static::$var;
    }
    public static function myStaticValueOverwritePropertyTest() {
        return static::$var2;
    }

    /**
     * This method is defined only here - class B will inherit this one!
     * We use it to test the difference btw self:: and static::
     * 
     * @return string
     */
    public static function myStaticMethodTest() {
        //return self::getValue();
        return static::getValue();
    }

    /**
     * This method will be overwritten in class B
     * @return string
     */
    protected static function getValue() {
        return 'value from class A';
    }
}


class B extends A {

    // we override this inherited static property
    protected static $var = 'class B var - override';

    /**
     * This method is overwritten from class A
     * @return string
     */
    protected static function getValue() {
        return 'value from class B';
    }

    /**
     * We modify the value of the inherited $var2 static property
     */
    public static function modStaticProperty() {
        self::$var2 = 'class B - altered value! - value overwrite';
    }
}

echo ("-- testing class A:\n");
echo (A::myStaticOverridePropertyTest(). "\n");
echo (A::myStaticValueOverwritePropertyTest(). "\n");
echo (A::myStaticMethodTest(). "\n");

echo ("-- now testing class B:\n");
echo (B::myStaticOverridePropertyTest(). "\n");
echo (B::myStaticValueOverwritePropertyTest(). "\n");
echo ("  now invoking B::modStaticProperty()     .\n");
B::modStaticProperty();
echo (B::myStaticValueOverwritePropertyTest(). "\n");

echo ("-- now re-testing class A:\n");
echo (A::myStaticOverridePropertyTest(). "\n");
echo (A::myStaticValueOverwritePropertyTest(). "\n");
echo (A::myStaticMethodTest(). "\n");

これは出力します:

-テストクラスA:
クラスA変数-オーバーライド
クラスAvar2-値の上書き
クラスAの値
-クラスBをテストしています:
クラスB変数-オーバーライド
クラスAvar2-値の上書き
B :: modStaticProperty()を呼び出しています.。
クラスB-値が変更されました! -値の上書き
-クラスAを再テストしています:
クラスA変数-オーバーライド
クラスB-変更された値!-値の上書き
クラスAの値

そしてここに、オーバーライドされた静的プロパティと値のみが上書きされた静的プロパティの違いがわかります...太字でマークした出力行を見てください!クラスBのmodStaticProperty()を呼び出すと、クラスAの静的変数の値も変更されました。その静的プロパティは継承され、オーバーライドされなかったためです。考えてみてください...

35
Attila Wind

今後のPHP 5.3.0リリースには 後期静的バインディング が含まれています。これは役立つ可能性があります。この機能を使用すると、静的メソッド内で静的変数を使用し、後期に静的バインディングは、「正しい」メソッドを見つけるのに役立ちます。

class Foo {
    public static function getDefault() {
        static $default = 'DEFAULT';
        return $default;
    }
    public static function doSomething ($param) {
        $default=static::getDefault(); // here is the late static binding
        $param = ($param === FALSE) ? $default : $param;
        return $param;

    }
}

class Bar extends Foo {
     public static function getDefault() {
        static $default = 'NEW DEFAULT FOR CHILD CLASS';
        return $default;
    }
}
27
Paul Dixon