32

SPL(標準PHPライブラリ)クラスの1つを拡張していますが、親のコンストラクターを呼び出すことができません。これが私が得ているエラーです:

致命的なエラー:コンストラクターを呼び出せません

SplQueueのドキュメントへのリンクは次のとおりです: http ://www.php.net/manual/en/class.splqueue.php

これが私のコードです:

$queue = new Queue();

class Queue extends SplQueue {

    public function __construct() {
        echo 'before';
        parent::__construct();
        echo 'I have made it after the parent constructor call';
    }

}

exit;

親のコンストラクターを呼び出せない理由は何ですか?

4

5 に答える 5

52

SplQueueから継承しSplDoublyLinkedListます。これらのクラスはいずれも、独自のコンストラクターを定義しません。したがって、呼び出す明示的な親コンストラクターはなく、このようなエラーが発生します。ドキュメントは、これについて少し誤解を招く可能性があります (多くの SPL クラスの場合と同様)。

エラーを解決するには、親コンストラクターを呼び出さないでください。


現在、ほとんどのオブジェクト指向言語では、クラスで明示的なコンストラクターが宣言されていない場合、既定のコンストラクターが呼び出されることが期待されます。しかし、ここに落とし穴があります: PHP クラスにはデフォルトのコンストラクターがありません! コンストラクタが定義されている場合にのみ、クラスはコンストラクタを持ちます。

実際、リフレクションを使用してクラスを分析するとstdClass、コンストラクターがないこともわかります。

$c = new ReflectionClass('stdClass');
var_dump($c->getConstructor()); // NULL

SplQueueおよびSplDoublyLinkedList両方のコンストラクターを反映しようとすると、同様に生成NULLされます。

私の推測では、PHP にクラスをインスタンス化するように指示すると、PHP は新しいオブジェクトに必要なすべての内部メモリ割り当てを実行し、コンストラクターの定義を探して、orの定義が見つかった場合にのみそれを呼び出します。ソースコードを調べてみたところ、サブクラスで明示的に呼び出したために、呼び出すコンストラクターが見つからない場合、PHP が異常終了したようです (「参考文献」を参照)。__construct()<class name>()zend_vm_def.h

于 2011-01-10T19:26:20.310 に答える
24

parentこのエラーは、通常、で参照されているクラスにparent::__construct()実際には関数がない場合にスローされ__construct()ます。

于 2012-07-26T20:42:33.107 に答える
2

最も近い祖先のコンストラクターを呼び出したい場合は、class_parents で祖先をループし、コンストラクターがあるかどうかをmethod_existsで確認できます。その場合は、コンストラクターを呼び出します。そうでない場合は、次に近い祖先で検索を続けます。親のコンストラクターのオーバーライドを防ぐだけでなく、他の祖先のコンストラクターのオーバーライドも防止します (親にコンストラクターがない場合)。

class Queue extends SplQueue {

  public function __construct() {
    echo 'before';

    // loops through all ancestors
    foreach(class_parents($this) as $ancestor) {

      // check if constructor has been defined
      if(method_exists($ancestor, "__construct")) {

        // execute constructor of ancestor
        eval($ancestor."::__construct();");

        // exit loop if constructor is defined
        // this avoids calling the same constructor twice
        // e.g. when the parent's constructor already
        // calls the grandparent's constructor
        break;
      }
    }
    echo 'I have made it after the parent constructor call';
  }

}

evalコードを再利用するために、このコードをed対象の PHP コードを返す関数として記述することもできます。

// define function to be used within various classes
function get_parent_construct($obj) {

  // loop through all ancestors
  foreach(class_parents($obj) as $ancestor) {

    // check if constructor has been defined
    if(method_exists($ancestor, "__construct")) {

      // return PHP code (call of ancestor's constructor)
      // this will automatically break the loop
      return $ancestor."::__construct();";
    }
  }
}

class Queue extends SplQueue {

  public function __construct() {
    echo 'before';

    // execute the string returned by the function
    // eval doesn't throw errors if nothing is returned
    eval(get_parent_construct($this));
    echo 'I have made it after the parent constructor call';
  }
}

// another class to show code reuse
class AnotherChildClass extends AnotherParentClass {

  public function __construct() {
    eval(get_parent_construct($this));
  }
}
于 2015-10-30T00:16:10.893 に答える
2

次のようにハッキングできます。

if (in_array('__construct', get_class_methods(get_parent_class($this)))) {
    parent::__construct();
}

しかし、それは無力です。

すべてのクラスに対してコンストラクターを明示的に宣言するだけです。それは正しい行動です。

于 2015-09-11T02:10:23.237 に答える
2

同じエラーが発生しました。親クラスで空のコンストラクターを定義することで解決しました。そうすれば、他のクラスはそれを定義する必要がありません。よりクリーンなアプローチだと思います。

それでもコンストラクターを呼び出す必要がある場合は、これを行うことができます。

if (is_callable('parent::__construct')) {
    parent::__construct();
}
于 2019-07-31T06:49:11.040 に答える