web-dev-qa-db-ja.com

親のコンストラクターを呼び出すときに致命的なエラーが発生するのはなぜですか?

SPL(Standard PHP Library))クラスの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;

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

28
Tonto McGee

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

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


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

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

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

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

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

45
BoltClock

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

21
Kristian

あなたはこのようにそれをハックするかもしれません:

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

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

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

2
simonkuang

最も近い祖先のコンストラクタを呼び出したい場合は、 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';
  }

}

コードを再利用する場合は、このコードをPHP code to evaled:

// 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));
  }
}
2
Alexander Jank

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

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

if (is_callable('parent::__construct')) {
    parent::__construct();
}
0