-
Notifications
You must be signed in to change notification settings - Fork 105
/
Copy pathattributes.xml
executable file
·382 lines (307 loc) · 9.68 KB
/
attributes.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision: $ -->
<!-- EN-Revision: 9155d793178b5fcd5131b734cd174fecc34c1ae6 Maintainer: daijie Status: ready -->
<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()
{
if (!file_exists($this->targetDirectory)) {
mkdir($this->targetDirectory);
} elseif (!is_dir($this->targetDirectory)) {
throw new RuntimeException("Target directory $this->targetDirectory is not a directory");
}
}
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>ReflectionAttribute::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>
通过传入参数:待搜索的注解类名,可返回指定的注解类,
而不需要再到反射类中迭代循环获取所有注解。
</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());
}
}
dumpMyAttributeData(new ReflectionClass(Thing::class));
]]>
</programlisting>
</example>
</sect1>
<sect1 xml:id="language.attributes.classes">
<title>声明注解类</title>
<para>
虽然没有严格要求,推荐为每个注解创建一个实际的类。
在这个最简单的例子中,通过 use 语法从全局命名空间引入 <literal>#[Attribute]</literal> 注解所需要全空的类。
</para>
<example>
<title>简单的 Attribute 类</title>
<programlisting role="php">
<![CDATA[
<?php
namespace Example;
use Attribute;
#[Attribute]
class MyAttribute
{
}
]]>
</programlisting>
</example>
<para>
要限制指定注解的声明类型,可为 <literal>#[Attribute]</literal> 注解第一个参数传入字节位掩码设置。
</para>
<example>
<title>目标限定使用的注解</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>可以指定以下目标:</para>
<simplelist>
<member><constant>Attribute::TARGET_CLASS</constant></member>
<member><constant>Attribute::TARGET_FUNCTION</constant></member>
<member><constant>Attribute::TARGET_METHOD</constant></member>
<member><constant>Attribute::TARGET_PROPERTY</constant></member>
<member><constant>Attribute::TARGET_CLASS_CONSTANT</constant></member>
<member><constant>Attribute::TARGET_PARAMETER</constant></member>
<member><constant>Attribute::TARGET_ALL</constant></member>
</simplelist>
<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
-->