web-dev-qa-db-ja.com

PHP> = 5.4の特性内のクラスコンストラクターをオーバーロードする方法

PHP 5では、コンストラクター(および他のメソッド)をオーバーロードできます。しかし、次のようなコードを取得した場合:

class Base {

    public function __construct($a, $b) {
        echo $a+$b;
    }


    public function sayHello() {
        echo 'Hello ';
    }
}


trait SayWorld {

    public function __construct($a, $b, $c = 0) {
        echo (int)$c * ($a+$b);
    }

    public function sayHello($a = null) {
        parent::sayHello();
        echo 'World!'.$a;
    }
}

class MyHelloWorld extends Base {
    use SayWorld;
}

$o = new MyHelloWorld(2, 3);
$o->sayHello(1);

エラーがあります:

致命的なエラー:MyHelloWorldには、トレイトからのコンストラクター定義が衝突しています

どうすれば修正できますか?私のコードをテストできます here

56
Guy Fawkes

今のところ、あなたがやりたいことをする唯一の方法だと思います:

class MyHelloWorld extends Base {

    use SayWorld {
        SayWorld::__construct as private __swConstruct;
    }

    public function __construct($a, $b, $c = 0)
    {
        $this->__swConstruct($a, $b, $c);
    }
}

編集2:

私のアドバイスは、PHPで1年以上の特性を扱うことに基づいています:特性でコンストラクタを書くことをまったく避けてください、または必要な場合-少なくともパラメータなしにします。それらを特性に持つことは、一般的なコンストラクターの概念に反します:コンストラクターは、それらが属するクラスに固有でなければなりません。その他の進化した高レベル言語は、暗黙的なコンストラクター継承もサポートしていません。これは、コンストラクターが他のメソッドよりもクラスとの関係がはるかに強いためです。実際、それらは非常に強い関係を持っているため、 [〜#〜] lsp [〜#〜] でさえ適用されません。 Scala言語(非常に成熟した [〜#〜] solid [〜#〜] フレンドリーなJavaの後継者)の特徴、 は、パラメーターを持つコンストラクターを持つことはできません

編集1:

PHP 5.4.11に bug )があり、実際にスーパークラスメソッドのエイリアスを許可しましたが、これはno-noと見なされましたPHP開発者なので、上記で説明した面倒な解決策にまだ固執しています。しかし、このバグにより、これで何ができるかについて議論が生じました。将来のリリース。

その間、同じ問題に何度も出くわしました。私の刺激は、特性を使用するために何度も繰り返さなければならないdocblockのパラメーターと行の数で指数関数的に上昇しました。そこで、可能な限りDRYルールに固執するために、次のパターンを思いつきました。

このようにパラメーターのセット全体を繰り返す代わりに:

trait SayWorld {

    /**
     * This is a valid docblock.
     *
     * @param int $a Doc comment.
     * @param int $b Doc comment.
     */
    public function __construct($a, $b) {
        echo (int)$c * ($a+$b);
    }
}

class MyHelloWorld extends Base {

    use SayWorld {
        SayWorld::__construct as private __swConstruct;
    }

    /**
     * Repeated and unnecessary docblock.
     *
     * @param int $a Doc comment.
     * @param int $b Doc comment.
     * @param int $c Doc comment.
     */
    public function __construct($a, $b, $c = 0)
    {
        $this->__swConstruct($a, $b);
    }
}

TupleC# および Python ユーザーに馴染みのある概念)のようなクラスを作成し、無限のパラメーターリストの代わりに使用します。

class SayWorldConstructTuple
{
    public $a;

    public $b;

    public function __construct($a, $b)
    {
        $this->a = $a;
        $this->b = $b;
    }
}

class MyHelloWorld extends Base {

    use SayWorld {
        SayWorld::__construct as private __swConstruct;
    }

    /**
     * New and valid docblock.
     *
     * @param SayWorldConstructTuple $Tuple
     * @param int $c Additional parameter.
     */
    public function __construct(SayWorldConstructTuple $Tuple, $c = 0)
    {
        $this->__swConstruct($Tuple->a, $Tuple->b);
        $this->c = $c;
    }
}

注:もちろん、このパターンは、より多くのTupleのコンストラクターパラメーターと、Tupleを使用するより多くのクラスでより便利です。

PHPの動的な性質を使用して、さらに自動化できます。

100
Maciej Sz

試してください:

use SayWorld {
  Base::__construct insteadof SayWorld;
}

参照: PHP Docs

6
Martin

古い投稿ですが、これが誰にも役立つ場合:

私は同様の状況でしたが、わずかに異なるアプローチを使用することにしました。 WordPressプラグインを書いていて、プラグイン情報(バージョン、名前、テキストドメインなど)を渡したいと思っていましたが、リファクタリングまたは別のファイルを拡張するときに各ファイルを変更したくありませんでした。クラスなので、クラス固有の操作のためにinit関数を呼び出すだけのコンストラクタで特性を作成しました。

trait HasPluginInfoTrait{
    public function __construct() { 

        $this->plugin_name        = PLUGIN_NAME;
        $this->version            = PLUGIN_VERSION;

        if ( method_exists( $this, 'init' ){
            $this->init();
        }
    }
}

class SampleClass {
    use HasPluginInfoTrait;

    private function init(){
        // Code specific to SampleClass
    }
}
1
Chris Schloegel