ホームページ  >  記事  >  バックエンド開発  >  PHP でコード再利用の新しい特性を実装する方法

PHP でコード再利用の新しい特性を実装する方法

coldplay.xixi
coldplay.xixi転載
2020-07-27 17:01:331711ブラウズ

PHP でコード再利用の新しい特性を実装する方法

yii2 のソースコードを読んでいるときに trait に出会ったので、それを勉強して記録するためにブログを書きました。

PHP 5.4.0 以降、PHP は traits と呼ばれるコード再利用の方法を実装しました。

Traits は、PHP のような単一継承言語用に用意されたコード再利用メカニズムです。トレイトは、単一継承言語の制約を軽減し、開発者が異なる階層内の独立したクラスでメソッド セットを自由に再利用できるように設計されています。特性とクラス構成のセマンティクスは、複雑さを軽減し、従来の多重継承とミックスインに関連する典型的な問題を回避する方法を定義します。

Trait はクラスに似ていますが、きめ細かく一貫した方法で機能を組み合わせるようにのみ設計されています。特性を単独でインスタンス化することはできません。これは、従来の継承に水平機能の組み合わせを追加します。つまり、アプリケーション クラスのメンバーを継承する必要がありません。

トレイトの例

コードは次のとおりです:

<?php
trait ezcReflectionReturnInfo {
    function getReturnType() { /*1*/ }
    function getReturnDescription() { /*2*/ }
}
class ezcReflectionMethod extends ReflectionMethod {
    use ezcReflectionReturnInfo;
    /* ... */
}
class ezcReflectionFunction extends ReflectionFunction {
    use ezcReflectionReturnInfo;
    /* ... */
}
?>

Priority

基本クラスから継承されたメンバーは次のとおりです。メンバーによってカバーされるトレイトによって挿入されます。優先順位は、現在のクラスのメンバーがトレイトのメソッドをオーバーライドし、トレイトが継承されたメソッドをオーバーライドすることです。

優先順位の例

コードは次のとおりです:

<?php
class Base {
    public function sayHello() {
        echo &#39;Hello &#39;;
    }
}
trait SayWorld {
    public function sayHello() {
        parent::sayHello();
        echo &#39;World!&#39;;
    }
}
class MyHelloWorld extends Base {
    use SayWorld;
}
$o = new MyHelloWorld();
$o->sayHello();
?>

上記のルーチンは出力します: Hello World!

メンバーがベースから継承した SayWorldクラスが挿入されると、Trait のsayHello メソッドでカバーされます。その動作は、MyHelloWorld クラスで定義されたメソッドと一致しています。優先順位としては、現在のクラスのメソッドが特性メソッドをオーバーライドし、特性メソッドが基本クラスのメソッドをオーバーライドします。

優先順位の別の例

コードは次のとおりです:

<?php
trait HelloWorld {
    public function sayHello() {
        echo &#39;Hello World!&#39;;
    }
}
class TheWorldIsNotEnough {
    use HelloWorld;
    public function sayHello() {
        echo &#39;Hello Universe!&#39;;
    }
}
$o = new TheWorldIsNotEnough();
$o->sayHello();
?>

上記のルーチンは出力します: Hello Universe!

# # 複数の特性

# はコンマで区切られ、use ステートメントに複数の特性がリストされ、それらはすべてクラスに挿入できます。

複数の特性の使用例

コードは次のとおりです:

<?php
trait Hello {
    public function sayHello() {
        echo &#39;Hello &#39;;
    }
}
trait World {
    public function sayWorld() {
        echo &#39;World&#39;;
    }
}
class MyHelloWorld {
    use Hello, World;
    public function sayExclamationMark() {
        echo &#39;!&#39;;
    }
}
$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();
?>

上記のルーチンは出力します: Hello World!

Conflict解決策

2 つのトレイトが同じ名前のメソッドを挿入する場合、競合が明示的に解決されないと致命的なエラーが発生します。

同じクラス内の複数の特性の名前の競合を解決するには、 replaceof 演算子を使用して、競合するメソッドのどれを使用するかを明示的に指定する必要があります。

上記のメソッドでは、他のメソッドを除外することしかできません。as 演算子は、競合するメソッドの 1 つを別の名前で導入できます。

競合解決の例

コードは次のとおりです:

<?php
trait A {
    public function smallTalk() {
        echo &#39;a&#39;;
    }
    public function bigTalk() {
        echo &#39;A&#39;;
    }
}
trait B {
    public function smallTalk() {
        echo &#39;b&#39;;
    }
    public function bigTalk() {
        echo &#39;B&#39;;
    }
}
class Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
    }
}
class Aliased_Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
        B::bigTalk as talk;
    }
}
?>

この例では、Talker は特性 A と B を使用します。 A と B は競合するメソッドを持っているため、トレイト B の smallTalk とトレイト A の bigTalk を使用することを定義します。

Aliased_Talker as 演算子を使用して、トークを B の bigTalk のエイリアスとして定義します。

メソッドのアクセス制御を変更する

as 構文を使用して、メソッドのアクセス制御を調整することもできます。

メソッドのアクセス制御を変更する例

コードは次のとおりです:

<?php
trait HelloWorld {
    public function sayHello() {
        echo &#39;Hello World!&#39;;
    }
}
// 修改 sayHello 的访问控制
class MyClass1 {
    use HelloWorld { sayHello as protected; }
}
// 给方法一个改变了访问控制的别名
// 原版 sayHello 的访问控制则没有发生变化
class MyClass2 {
    use HelloWorld { sayHello as private myPrivateHello; }
}
?>

特性から特性への構成

クラスがトレイトを使用できるのと同じように、他のトレイトもトレイトを使用できます。特性を定義するときに 1 つ以上の特性を使用すると、他の特性の一部またはすべてのメンバーを組み合わせることができます。

特性から特性を合成する例

コードは次のとおりです:

<?php
trait Hello {
    public function sayHello() {
        echo &#39;Hello &#39;;
    }
}
trait World {
    public function sayWorld() {
        echo &#39;World!&#39;;
    }
}
trait HelloWorld {
    use Hello, World;
}
class MyHelloWorld {
    use HelloWorld;
}
$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
?>

上記のルーチンは出力します: Hello World!

Abstract特性メンバーの数

使用されるクラスに要件を強制するために、特性は抽象メソッドの使用をサポートします。

抽象メソッドによる必須要件の例を示します

コードは次のとおりです:

<?php
trait Hello {
    public function sayHelloWorld() {
        echo &#39;Hello&#39;.$this->getWorld();
    }
    abstract public function getWorld();
}
class MyHelloWorld {
    private $world;
    use Hello;
    public function getWorld() {
        return $this->world;
    }
    public function setWorld($val) {
        $this->world = $val;
    }
}
?>

Trait の静的メンバー

Traits は静的メンバーと静的メソッドによって定義できます。

静的変数の例

コードは次のとおりです:


<?php
trait Counter {
    public function inc() {
        static $c = 0;
        $c = $c + 1;
        echo "$c\n";
    }
}
class C1 {
    use Counter;
}
class C2 {
    use Counter;
}
$o = new C1(); $o->inc(); // echo 1
$p = new C2(); $p->inc(); // echo 1
?>

静的メソッドの例

コードは次のとおりです:


<?php
trait StaticExample {
    public static function doSomething() {
        return &#39;Doing something&#39;;
    }
}
class Example {
    use StaticExample;
}
Example::doSomething();
?>

静的変数と静的メソッドの例

コードは次のとおりです。


<?php
trait Counter {
    public static $c = 0;
    public static function inc() {
        self::$c = self::$c + 1;
        echo self::$c . "\n";
    }
}
class C1 {
    use Counter;
}
class C2 {
    use Counter;
}
C1::inc(); // echo 1
C2::inc(); // echo 1
?>

属性

Trait で属性を定義することもできます。

属性定義の例

コードは次のとおりです:


<?php
trait PropertiesTrait {
    public $x = 1;
}
class PropertiesExample {
    use PropertiesTrait;
}
$example = new PropertiesExample;
$example->x;
?>

トレイトが属性を定義する場合、そのクラスは次の属性を定義できません。同じ名前です。そうでない場合は間違いです。クラス内のプロパティの定義が特性内のその定義と互換性がある (可視性と初期値が同じ) 場合、エラー レベルは E_STRICT であり、そうでない場合は致命的なエラーです。

競合の例

コードは次のとおりです:


<?php
trait PropertiesTrait {
    public $same = true;
    public $different = false;
}
class PropertiesExample {
    use PropertiesTrait;
    public $same = true; // Strict Standards
    public $different = true; // 致命错误
}
?>

使用上の違い

例さまざまな用途

コードは次のとおりです:


<?php
namespace Foo\Bar;
use Foo\Test;  // means \Foo\Test - the initial \ is optional
?>
<?php
namespace Foo\Bar;
class SomeClass {
    use Foo\Test;   // means \Foo\Bar\Foo\Test
}
?>

最初の用途は名前空間に Foo\Test を使用し、見つかったものは \Foo\Test です。2 番目の用途は次のとおりです。特性を使用します。

\Foo\Bar\Foo\Test が見つかりました。

#__CLASS__

および __TRAIT____CLASS__
使用特性のクラス名を返し、__TRAIT__ は特性名を返します例 コードは次のとおりです。

コードは次のとおりです。

<?php
trait TestTrait {
    public function testMethod() {
        echo "Class: " . __CLASS__ . PHP_EOL;
        echo "Trait: " . __TRAIT__ . PHP_EOL;
    }
}
class BaseClass {
    use TestTrait;
}
class TestClass extends BaseClass {
}
$t = new TestClass();
$t->testMethod();
//Class: BaseClass
//Trait: TestTrait

Trait singleton


例は次のとおりです。以下

コードは次のとおりです:

##
<?php
trait singleton {    
    /**
     * private construct, generally defined by using class
     */
    //private function __construct() {}
    public static function getInstance() {
        static $_instance = NULL;
        $class = __CLASS__;
        return $_instance ?: $_instance = new $class;
    }
    public function __clone() {
        trigger_error(&#39;Cloning &#39;.__CLASS__.&#39; is not allowed.&#39;,E_USER_ERROR);
    }
    public function __wakeup() {
        trigger_error(&#39;Unserializing &#39;.__CLASS__.&#39; is not allowed.&#39;,E_USER_ERROR);
    }
}
/**
* Example Usage
*/
class foo {
    use singleton;
    private function __construct() {
        $this->name = &#39;foo&#39;;
    }
}
class bar {
    use singleton;
    private function __construct() {
        $this->name = &#39;bar&#39;;
    }
}
$foo = foo::getInstance();
echo $foo->name;
$bar = bar::getInstance();
echo $bar->name;


特性メソッドを呼び出す

明らかではありませんが、特性メソッドが通常のクラスで静的メソッドとして定義されている場合は、呼び出すことができます

实例如下

代码如下:

<?php 
trait Foo { 
    function bar() { 
        return &#39;baz&#39;; 
    } 
} 
echo Foo::bar(),"\\n"; 
?>

相关学习推荐:PHP编程从入门到精通

以上がPHP でコード再利用の新しい特性を実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjb51.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。