使用特性

如果你曾经做过任何C语言编程,你也许会熟悉宏。宏是一个预定义的代码块,它在指定的行处展开。类似地,特性可以包含代码块,这些代码块在PHP解释器指定的行处被复制并粘贴到一个类中。

如何做...

1.特性用关键字 trait 标识,可以包含属性和方法。你可能已经注意到,在检查之前具有 CountryListCustomerList 类的案例时,有重复的代码。在这个例子中,我们将重构这两个类,并将 list() 方法的功能移到Trait 中。请注意,两个类中的 list() 方法是一样的。

2.特性用于类与类之间代码重复使用的情况。但请注意,传统的创建抽象类并扩展它的方法可能比 traits 更可能有某些优势。特性不能用来确定继承的线路,而抽象父类可以用于这个目的。

3.现在,我们将 list() 复制到一个名为 ListTrait 的特性中:

trait ListTrait
{
  public function list()
  {
    $list = [];
    $sql  = sprintf('SELECT %s, %s FROM %s', 
      $this->key, $this->value, $this->table);
    $stmt = $this->connection->pdo->query($sql);
    while ($item = $stmt->fetch(PDO::FETCH_ASSOC)) {
      $list[$item[$this->key]] = $item[$this->value];
    }
    return $list;
  }
}

4.然后,我们可以将 ListTrait 中的代码插入到新类 CountryListUsingTrait 中,如以下代码片段所示。 现在可以从此类中删除整个 list() 方法:

5.特性受命名空间影响。 在步骤1所示的示例中,如果将新的 CountryListUsingTrait 类放置在命名空间 Application\Generic 中,则我们还需要将 ListTrait 也一起移入该命名空间:

6. 特性中的方法将覆盖继承的方法。

7.在下面的例子中,您会注意到 setId() 方法的返回值在 Base 父类和 Test 特性之间有所不同。Customer 类继承自 Base,但也使用 Test。在这种情况下,特性中定义的方法将覆盖 Base 父类中定义的方法:

8. 在类中定义了与特性中一样的方法,则在引入特性的时候了类中的方法将覆盖特性中的方法。

9.在这个例子中,Test 特性定义了一个属性 $id 以及 getId()setId()方法。该特性还定义了 setName(),它与 Customer 类中定义的相同方法有冲突。在这种情况下,Customer类中直接定义的 setName()方法将覆盖特性中定义的 setName()

10.当使用多个特性时,使用 insteadof 关键字来解决方法名冲突。结合使用 as 关键字来对方法名进行别名设置。

11.在这个例子中,有两个特性,IdTraitNameTrait。这两个特性都定义了一个 setKey() 方法,但是使用了不同的方式来表达key。Test 类使用了这两个特性。注意 insteadof 关键字,它允许我们区分冲突的方法。因此,当从 Test 类调用 setKey() 时,源将从 NameTrait 中抽取。此外,来自 IdTraitsetKey() 仍将可用,但用的是别名setKeyDate()

如何运行...

从步骤1开始,您了解到特性用于出现重复代码的情况下。 您需要评估是否可以简单地定义一个基类并扩展它,或者使用特性是否更好地满足您的目的。 当在逻辑上不相关的类中看到代码重复时,特性将特别有用。

为了说明特性方法如何覆盖继承的方法,请将步骤7中提到的代码块复制到一个单独的文件 chap_04_oop_traits_override_inherited.php 中。 添加以下代码行:

从输出中可以看到(下图所示),属性 $id 中存储了 stdClass() 的实例,这就是在特性中定义的行为:

为了说明直接定义的类方法如何覆盖特性方法,请将步骤9中提到的代码块复制到单独的文件 chap_04_oop_trait_methods_do_not_override_class_methods.php 中。 添加以下代码行:

从以下输出中可以看到,$id 属性存储为整数,如在 Customer 类中定义的,而特性将 $id 定义为 stdClass 的实例:

在步骤10中,您学习了如何在使用多个特性时解决重复的方法名称冲突。 将步骤11中显示的代码块复制到单独的文件 chap_04_oop_trait_multiple.php 中。 添加以下代码:

请注意,在下面的输出中,setKey() 返回的数据是PHP 7新函数random_bytes() (在 NameTrait中定义)产生的,而 setKeyDate() 返回的数据是使用 date()rand() 函数(在 IdTrait 中定义)产生的:

最后更新于