3

PHPで静的クラスを拡張するのに問題があるようです。

PHPコード:

<?php
    class InstanceModule {
        public static $className = 'None';
        public static function PrintClassName() {
            echo self::$className . ' (' . __CLASS__ . ')<br />';
        }
    }

    class A extends InstanceModule {
        public static function Construct() {
            self::$className = "A";
        }
    }

    class B extends InstanceModule {
        public static function Construct() {
            self::$className = "B";
        }
    }
?>

私の発信コード、および私が期待するもの:

<?php
    //PHP Version 5.3.14

    A::PrintClassName(); //Expected 'None' - actual result: 'None'
    B::PrintClassName(); //Expected 'None' - actual result: 'None'

    A::Construct();

    A::PrintClassName(); //Expected 'A' - actual result: 'A'
    B::PrintClassName(); //Expected 'None' - actual result: 'A'

    B::Construct();

    A::PrintClassName(); //Expected 'A' - actual result: 'B'
    B::PrintClassName(); //Expected 'B' - actual result: 'B'

    A::Construct();

    A::PrintClassName(); //Expected 'A' - actual result: 'A'
    B::PrintClassName(); //Expected 'B' - actual result: 'A'
?>

実際の完全な出力:

None (InstanceModule)
None (InstanceModule)
A (InstanceModule)
A (InstanceModule)
B (InstanceModule)
B (InstanceModule)
A (InstanceModule)
A (InstanceModule)

したがって、ここで起こっていることは(見た目から)self::$className、拡張クラスのいずれかを設定するとすぐに、他のクラスの変数をオーバーライドするということです。これは、静的クラスを使用しているためだと思います。以前の理解のように、クラスをとInstanceModuleの両方に単純にコピーするのではなく、クラスは1つしか存在できません。代わりにキーワードを使ってみましたが、違いはないようです。ABextendsstatic::$className

誰かが私がここで間違っていること、そしてこの問題を解決するために何をすべきかについて正しい方向に私を導くことができれば素晴らしいでしょう。

編集:明確にするために、このコードは私が望むことを実行しますが、関数を拡張して再利用するという考え全体を台無しにするので、明らかに恐ろしい回避策です:

<?php
    class A {
        public static $className = 'None';
        public static function PrintClassName() {
            echo self::$className . ' (' . __CLASS__ . ')<br />';
        }
        public static function Construct() {
            self::$className = "A";
        }
    }

    class B {
        public static $className = 'None';
        public static function PrintClassName() {
            echo self::$className . ' (' . __CLASS__ . ')<br />';
        }
        public static function Construct() {
            self::$className = "B";
        }
    }
?>
4

4 に答える 4

4

$ classNameは静的で親クラス内にあるため、classNameをAまたはB内に設定すると、親内の変数が変更され、変数が読み取られるときにも同じことが行われます。拡張クラスでclassNameをオーバーライドしない限り、元々InstanceModuleで定義された同じメモリ位置から情報を格納および取得することになります。

A / BでclassNameを再定義すると、InstanceModuleまたはA/Bからそれぞれparent::またはself::を使用してclassNameにアクセスできます。何をしようとしているのかによっては、抽象クラスも重要な役割を果たす可能性があります。

PHP5マニュアルの静的キーワードまたはクラス抽象化を参照してください。

于 2012-06-24T02:42:32.003 に答える
3

これは、php 7.3でもまだ問題のようです(ここに最初の質問が投稿されてから7年後)。

を使用した答え__callStaticはおそらくうまくいくように見えますが、単純なはずのものにとってはかなり複雑です。そして、他の2つの回答は、実際には問題の実用的な解決策を提供していないようです。そのため、ここに回答として自分自身の回避策を含めます。

静的変数を静的配列に置き換えることができます。

<?php
    class InstanceModule {
        public static $className = [];
        public static function PrintClassName() {
            $calledClass = get_called_class();
            if(empty(self::$className[$calledClass])){
              $thisClassName = "none";
            }else{
              $thisClassName = self::$className[$calledClass];
            }
            echo $thisClassName . ' (' . __CLASS__ . ')<br />';
        }
    }

    class A extends InstanceModule {
        public static function Construct() {
            $calledClass = get_called_class();
            self::$className[$calledClass] = "A";
        }
    }

    class B extends InstanceModule {
        public static function Construct() {
            $calledClass = get_called_class();
            self::$className[$calledClass] = "B";
        }
    }
?>

したがって、の各子クラスの「静的」値は、InstanceModule元のクラスの名前を持つキーの下に格納されます。

私には、兄弟クラスが静的プロパティを共有するというPHPのバグのように思えます。特に、親が抽象である場合は、決して発生しないはずです。この回避策はそれほどきれいではありませんが、それほど複雑ではないのは、私が作業できる唯一の方法です。

この回避策を使用すると、目的の結果が得られます。

<?php   
    A::PrintClassName(); //Expected 'None' - actual result: 'None'
    B::PrintClassName(); //Expected 'None' - actual result: 'None'

    A::Construct();

    A::PrintClassName(); //Expected 'A' - actual result: 'A'
    B::PrintClassName(); //Expected 'None' - actual result: 'None'

    B::Construct();

    A::PrintClassName(); //Expected 'A' - actual result: 'A'
    B::PrintClassName(); //Expected 'B' - actual result: 'B'

    A::Construct();

    A::PrintClassName(); //Expected 'A' - actual result: 'A'
    B::PrintClassName(); //Expected 'B' - actual result: 'B'
?>
于 2019-06-25T05:36:44.687 に答える
2

基本クラスにサブクラスインスタンスのリポジトリを保持させて、静的基本クラス変数からデータを取得するのではなく、各クラスに属するデータを適切に分離できるようにします。

__callStatic()以下に示すように、基本クラスのマジックメソッドを使用して、存在しないサブクラスのメソッドを呼び出すことができます。残念ながら、静的リポジトリ変数は、その魔法のメソッドの可視性のためにパブリックとして宣言する必要があります。

abstract class Base
{
    public static $repo = array();

    public static function __callStatic($name, $args)
    {
        $class = get_called_class();

        if (!isset(self::$repo[$class])) {
                echo "Creating instance of $class\n";
                self::$repo[$class] = new $class();
        }

        return call_user_func_array(array(self::$repo[$class], $name), $args);
    }

        protected function PrintClassName()
        {
                echo __CLASS__, " (", get_called_class(), ")\n";
        }

        protected abstract function Construct($a);
}

class A extends Base
{
        protected function Construct($a)
        {
                echo __CLASS__, ": setting x := $a\n";
        }
}

class B extends Base
{
        protected function Construct($a)
        {
                echo __CLASS__, ": setting y := $a\n";
        }
}

A::PrintClassName();
B::PrintClassName();

A::Construct('X');
B::Construct('Y');

出力:

Creating instance of A
Base (A)
Creating instance of B
Base (B)
A: setting x := X
B: setting y := Y
于 2012-06-25T00:28:25.737 に答える
1

あなたの状況に対する最善の答えはget_called_class()、現在の変数の代わりに関数を使用することだと思います。これは、現在のクラス名の代わりに、または現在のクラス名のみ$classNameを返す、後期静的バインディングクラス名を返します。__CLASS__get_class()

PrintClassName()返されたものだけを出力するように関数を変更した場合get_called_class()、出力は次のようになります。ここで、デフォルト値を組み込む必要があります。これはもちろんクラス間で共有されるため、静的メソッドを引き続き使用する場合は、両方のクラスにそのフラグを設定する必要があります。

A
B
A
B
A
B
A
B
于 2012-06-24T02:47:22.567 に答える