Trait

PHP 实现了一种代码复用的方法,称为 Trait 。 Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。 Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用方法。 Trait 和类组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。 Trait 和类相似,但仅仅旨在用细粒度和一致的方式来组合功能。无法通过 Trait 自身来实例化。它为传统继承增加了水平特性的组合,也就是说,应用的几个类之间不需要继承。

<?php

trait Foo
{
    /**
     * Return a string.
     *
     * @param  void
     * @return string
     */
    public function method(): string
    {
        return 'foo';
    }
}

class Bar
{
    use Foo;
}

class Baz
{
    use Foo;
}

$bar = new Bar();
var_dump($bar->method()); // string(3) "foo"

$baz = new Baz();
var_dump($baz->method()); // string(3) "foo"

从基类继承的成员会被 Trait 插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 Trait 的方法,而 Trait 则覆盖了被继承的方法。

<?php

class Foo
{
    /**
     * Return a string.
     *
     * @param  void
     * @return string
     */
    public function method(): string
    {
        return 'foo';
    }
}

trait Bar
{
    /**
     * Return a string.
     *
     * @param  void
     * @return string
     */
    public function method(): string
    {
        return 'bar';
    }
}

class Baz extends Foo
{
    use Bar;
}

class Qux extends Foo
{
    use Bar;

    /**
     * Override the trait method.
     *
     * @param  void
     * @return string
     */
    public function method(): string
    {
        return 'baz';
    }
}

$baz = new Baz();
var_dump($baz->method()); // string(3) "bar"

$qux = new Qux();
var_dump($qux->method()); // string(3) "baz"

在一个类中使用多个 Trait。

<?php

trait Foo
{
    /**
     * Return a string.
     *
     * @param  void
     * @return string
     */
    public function foo(): string
    {
        return 'foo';
    }
}

trait Bar
{
    /**
     * Return a string.
     *
     * @param  void
     * @return string
     */
    public function bar(): string
    {
        return 'bar';
    }
}

class Baz
{
    use Foo;
    use Bar;
}

$baz = new Baz();
var_dump($baz->foo()); // string(3) "foo"
var_dump($baz->bar()); // string(3) "bar"

如果两个 Trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。为了解决多个 Trait 在同一个类中的命名冲突,需要使用 insteadof 操作符来明确指定使用冲突方法中的哪一个。以上方式仅允许排除掉其它方法, as 操作符可以 为某个方法引入别名。 注意, as 操作符不会对方法进行重命名,也不会影响其方法。

<?php

trait Foo
{
    /**
     * Return a string.
     *
     * @param  void
     * @return string
     */
    public function method(): string
    {
        return 'foo';
    }
}

trait Bar
{
    /**
     * Return a string.
     *
     * @param  void
     * @return string
     */
    public function method(): string
    {
        return 'bar';
    }
}

class Baz
{
    use Foo, Bar {
        Foo::method insteadof Bar;
    }
}

class Qux
{
    use Foo, Bar {
        Bar::method insteadof Foo;
        Bar::method as test;
    }
}

$baz = new Baz();
var_dump($baz->method()); // string(3) "foo"

$qux = new Qux();
var_dump($qux->method()); // string(3) "bar"
var_dump($qux->test());   // string(3) "bar"

使用 as 关键字还可以用来调整方法的访问控制。

<?php

trait Foo
{
    /**
     * Return a string.
     *
     * @param  void
     * @return string
     */
    protected function method(): string
    {
        return 'foo';
    }
}

class Bar
{
    use Foo {
        Foo::method as public;
    }
}

class Baz
{
    use Foo {
        Foo::method as public test;
    }
}

$bar = new Bar();
var_dump($bar->method()); // string(3) "foo"

$baz = new Baz();
var_dump($baz->method()); // PHP Fatal error:  Uncaught Error: Call to protected method Baz::method() from context ''
var_dump($baz->test());   // string(3) "foo"

正如类能够使用 Trait 一样,其它 Trait 也能够使用 Trait 。在 Trait 定义时通过使用一个或多个 Trait ,能够组合其它 Trait 中的部分或全部成员。

<?php

trait Foo
{
    /**
     * Return a string.
     *
     * @param  void
     * @return string
     */
    public function foo(): string
    {
        return 'foo';
    }
}

trait Bar
{
    /**
     * Return a string.
     *
     * @param  void
     * @return string
     */
    public function bar(): string
    {
        return 'bar';
    }
}

trait Baz
{
    use Foo;
    use Bar;
}

class Qux
{
    use Baz;
}

$qux = new Qux();
var_dump($qux->foo()); // string(3) "foo"
var_dump($qux->bar()); // string(3) "bar"

为了对使用的类施加强制要求, Trait 支持抽象方法的使用。

<?php

trait Foo
{
    /**
     * Just an abstract method.
     *
     * @param  void
     * @return string
     */
    abstract protected function method(): string;
}

class Bar
{
    use Foo;

    /**
     * Override the abstract method.
     *
     * @param  void
     * @return string
     */
    public function method(): string
    {
        return 'bar';
    }
}

$bar = new Bar();
var_dump($bar->method()); // string(3) "bar"

Trait 方法中的静态变量。

<?php

trait Foo
{
    /**
     * Use static variables in method.
     *
     * @param  void
     * @return int
     */
    public static function method(): int
    {
        static $foo = 0;
        return $foo++;
    }
}

class Bar
{
    use Foo;
}

var_dump(Bar::method()); // int(0)
var_dump(Bar::method()); // int(1)
var_dump(Bar::method()); // int(2)

Trait 中的静态方法。

<?php

trait Foo
{
    /**
     * Return a string.
     *
     * @param  void
     * @return string
     */
    public static function method(): string
    {
        return 'foo';
    }
}

class Bar
{
    use Foo;
}

var_dump(Bar::method()); // string(3) "foo"

Trait 中同样可以定义属性。 Trait 定义了一个属性后,类就不能定义同样名称的属性,否则会产生致命错误。 有种情况例外:属性是兼容的(同样的访问可见度、初始默认值)。

<?php

trait Foo
{
    /**
     * Just a test property.
     *
     * @var string
     */
    public string $foo = 'foo';

    /**
     * Just a test property.
     *
     * @var bool
     */
    public bool $bar = true;
}

class Bar
{
    use Foo;

    /**
     * Override the parent property.
     *
     * @var string
     */
    public string $foo = 'foo';

    /**
     * Try to override the parent property.
     *
     * @var bool
     */
    public bool $bar = false;
}

// PHP Fatal error:  Bar and Foo define the same property ($bar) in the composition of Bar. However, the definition differs and is considered incompatible.

results matching ""

    No results matching ""