com doc/zh: Sync with en: language/attributes.xml
[email protected] (Dai Jie) Wed, 30 Dec 2020 13:15:37 +0000
| Newsgroups | php.doc.zh |
|---|---|
| Message-ID | <[email protected]> |
Commit: 6269f2b0c62874e003d9d7c6fe0f6af795d1cfbb Author: daijie <[email protected]> Wed, 30 Dec 2020 21:15:37 +0800 Parents: 51d5438ab8c2d5a5f62707fc07bd6acf79b958e1 Branches: master Link: http://git.php.net/?p=doc/zh.git;a=commitdiff;h=6269f2b0c62874e003d9d7c6fe0f6af795d1cfbb Log: Sync with en Changed paths: A language/attributes.xml
diff_6269f2b0c62874e003d9d7c6fe0f6af795d1cfbb.txt
(text/plain, 9.4 KB)
diff --git a/language/attributes.xml b/language/attributes.xml
new file mode 100755
index 00000000..5e4fea34
--- /dev/null
+++ b/language/attributes.xml
@@ -0,0 +1,368 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- $Revision: $ -->
+<!-- EN-Revision: 352158 Maintainer: daijie Status: ready -->
+<!-- Reviewed: no -->
+
+ <chapter xml:id="language.attributes" xmlns="http://docbook.org/ns/docbook">
+ <title>注解</title>
+ <sect1 xml:id="language.attributes.overview">
+ <title>注解概览</title>
+ <?phpdoc print-version-for="attributes"?>
+
+ <para>
+ 注解功能使得代码中的声明部分都可以添加结构化、机器可读的元数据,
+ 注解的目标可以是类、方法、函数、参数、属性、类常量。
+
+ 通过 <link linkend="book.reflection">反射 API</link> 可在运行时获取注解所定义的元数据。
+ 因此注解可以成为直接嵌入代码的配置式语言。
+ </para>
+
+ <para>
+ 通过注解的使用,在应用中实现功能、使用功能可以相互解耦。
+ 某种程度上讲,它可以和接口(interface)与其实现(implementation)相比较。
+ 但接口与实现是代码相关的,注解则与声明额外信息和配置相关。
+ 接口可以通过类来实现,而注解也可以声明到方法、函数、参数、属性、类常量中。
+ 因此它们比接口更灵活。
+ </para>
+
+ <para>
+ 注解使用的一个简单例子:将接口(interface)的可选方法改用注解实现。
+ 我们假设接口 <literal>ActionHandler</literal> 代表了应用的一个操作:
+ 部分 action handler 的实现需要 setup,部分不需要。
+ 我们可以使用注解,而不用要求所有类必须实现 <literal>ActionHandler</literal>
+ 接口并实现 <literal>setUp()</literal> 方法。
+ 因此带来一个好处——可以多次使用注解。
+ </para>
+
+ <example>
+ <title>用注解实现接口的可选方法</title>
+ <programlisting role="php">
+<![CDATA[
+<?php
+interface ActionHandler
+{
+ public function execute();
+}
+
+#[Attribute]
+class SetUp {}
+
+class CopyFile implements ActionHandler
+{
+ public string $fileName;
+ public string $targetDirectory;
+
+ #[SetUp]
+ public function fileExists()
+ {
+ if (!file_exists($this->fileName)) {
+ throw new RuntimeException("File does not exist");
+ }
+ }
+
+ #[SetUp]
+ public function targetDirectoryExists()
+ {
+ mkdir($this->targetDirectory);
+ }
+
+ public function execute()
+ {
+ copy($this->fileName, $this->targetDirectory . '/' . basename($this->fileName));
+ }
+}
+
+function executeAction(ActionHandler $actionHandler)
+{
+ $reflection = new ReflectionObject($actionHandler);
+
+ foreach ($reflection->getMethods() as $method) {
+ $attributes = $method->getAttributes(SetUp::class);
+
+ if (count($attributes) > 0) {
+ $methodName = $method->getName();
+
+ $actionHandler->$methodName();
+ }
+ }
+
+ $actionHandler->execute();
+}
+
+$copyAction = new CopyFile();
+$copyAction->fileName = "/tmp/foo.jpg";
+$copyAction->targetDirectory = "/home/user";
+
+executeAction($copyAction);
+]]>
+ </programlisting>
+ </example>
+ </sect1>
+
+ <sect1 xml:id="language.attributes.syntax">
+ <title>注解语法</title>
+
+ <para>
+ 注解语法包含以下几部分。
+ 首先,注解声明总是以 <literal>#[</literal> 开头,以
+ <literal>]</literal> 结尾来包围。
+ 内部则是一个或以逗号包含的多个注解。
+ 注解的名称按 <link linkend="language.namespaces.basics">使用命名空间:基础</link>
+ 章节中描述,可以是非限定、限定、完全限定的名称。
+ 注解的参数是可以选的,以常见的括号<literal>()</literal>包围。
+ 注解的参数可以是字面值或者常量表达式。
+ 它同时接受位置参数和命名参数两种语法。
+ </para>
+
+ <para>
+ 通过反射 API 请求注解实例时,注解的名称会被解析到一个类,注解的参数则传入该类的构造器中。
+ 因此每个注解都需要引入一个类。
+ </para>
+
+ <example>
+ <title>注解语法</title>
+
+ <programlisting role="php">
+<![CDATA[
+<?php
+// a.php
+namespace MyExample;
+
+use Attribute;
+
+#[Attribute]
+class MyAttribute
+{
+ const VALUE = 'value';
+
+ private $value;
+
+ public function __construct($value = null)
+ {
+ $this->value = $value;
+ }
+}
+
+// b.php
+
+namespace Another;
+
+use MyExample\MyAttribute;
+
+#[MyAttribute]
+#[\MyExample\MyAttribute]
+#[MyAttribute(1234)]
+#[MyAttribute(value: 1234)]
+#[MyAttribute(MyAttribute::VALUE)]
+#[MyAttribute(array("key" => "value"))]
+#[MyAttribute(100 + 200)]
+class Thing
+{
+}
+
+#[MyAttribute(1234), MyAttribute(5678)]
+class AnotherThing
+{
+}
+]]>
+ </programlisting>
+ </example>
+ </sect1>
+
+
+ <sect1 xml:id="language.attributes.reflection">
+ <title>使用反射 API 读取注解</title>
+
+ <para>
+ 反射 API 提供了 <function>getAttributes</function> 方法,
+ 类、方法、函数、参数、属性、类常量的反射对象可通过它获取相应的注解。
+ 该方法返回了 <classname>ReflectionAttribute</classname> 实例的数组,
+ 可用于查询注解名称、参数、也可以实例化一个注解。
+ </para>
+
+ <para>
+ 实例和反射注解的分离使得程序员增加了在丢失反射类、类型错误、丢失参数等情况下的处理能力,也能处理错误。
+ 只有调用 <function>newInstance</function> 后,注解类的对象才会以验证过匹配的参数来实例化。
+ </para>
+
+ <example>
+ <title>通过反射 API 读取注解</title>
+
+ <programlisting role="php">
+<![CDATA[
+<?php
+
+#[Attribute]
+class MyAttribute
+{
+ public $value;
+
+ public function __construct($value)
+ {
+ $this->value = $value;
+ }
+}
+
+#[MyAttribute(value: 1234)]
+class Thing
+{
+}
+
+function dumpAttributeData($reflection) {
+ $attributes = $reflection->getAttributes();
+
+ foreach ($attributes as $attribute) {
+ var_dump($attribute->getName());
+ var_dump($attribute->getArguments());
+ var_dump($attribute->newInstance());
+ }
+}
+
+dumpAttributeData(new ReflectionClass(Thing::class));
+/*
+string(11) "MyAttribute"
+array(1) {
+ ["value"]=>
+ int(1234)
+}
+object(MyAttribute)#3 (1) {
+ ["value"]=>
+ int(1234)
+}
+*/
+
+]]>
+ </programlisting>
+ </example>
+
+ <para>
+ Instead of iterating all attributes on the reflection instance, only those
+ of a particular attribute class can be
+ retrieved by passing the searched attribute class name as argument.
+ </para>
+
+ <example>
+ <title>使用反射 API 读取指定的注解</title>
+
+ <programlisting role="php">
+<![CDATA[
+<?php
+
+function dumpMyAttributeData($reflection) {
+ $attributes = $reflection->getAttributes(MyAttribute::class);
+
+ foreach ($attributes as $attribute) {
+ var_dump($attribute->getName());
+ var_dump($attribute->getArguments());
+ var_dump($attribute->newInstance());
+ }
+}
+
+dumpAttributeData(new ReflectionClass(Thing::class));
+]]>
+ </programlisting>
+ </example>
+ </sect1>
+
+ <sect1 xml:id="language.attributes.classes">
+ <title>声明注解类</title>
+
+ <para>
+ 虽然没有严格要求,推荐为每个注解创建一个实际的类。
+ 在这个最简单的例子中,通过 use 语法从全局命名空间引入 <literal>#[Attribute]</literal> 注解所需要全空的类。
+ </para>
+
+ <example>
+ <title>目标限定使用的注解</title>
+
+ <programlisting role="php">
+<![CDATA[
+<?php
+
+namespace Example;
+
+use Attribute;
+
+#[Attribute]
+class MyAttribute
+{
+}
+]]>
+ </programlisting>
+ </example>
+
+ <para>
+ 要限制指定注解的声明类型,可为 <literal>#[Attribute]</literal> 注解第一个参数传入字节位掩码设置。
+ </para>
+
+ <example>
+ <title>简单的 Attribute 类</title>
+
+ <programlisting role="php">
+<![CDATA[
+<?php
+
+namespace Example;
+
+use Attribute;
+
+#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION)]
+class MyAttribute
+{
+}
+]]>
+ </programlisting>
+
+ <para>
+ 在另一个类型中声明 <classname>MyAttribute</classname> 会在调用
+ <function>ReflectionAttribute::newInstance</function> 时抛出异常。
+ </para>
+ </example>
+
+ <para>
+ 注解在每个声明中默认情况下只能使用一次。
+ 如果需要重复,可以在 <literal>#[Attribute]</literal> 声明中设置字节位掩码。
+ </para>
+
+ <example>
+ <title>使用 IS_REPEATABLE 允许注解在声明中出现多次</title>
+
+ <programlisting role="php">
+<![CDATA[
+<?php
+
+namespace Example;
+
+use Attribute;
+
+#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION | Attribute::IS_REPEATABLE)]
+class MyAttribute
+{
+}
+]]>
+ </programlisting>
+
+ </example>
+ </sect1>
+ </chapter>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"~/.phpdoc/manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->