-
Notifications
You must be signed in to change notification settings - Fork 79
/
Copy pathinterfaces.xml
482 lines (438 loc) · 14.9 KB
/
interfaces.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
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<!-- EN-Revision: 0c2a0d736df56dd0f3a1b88bd6ce7c975c38285a Maintainer: hirokawa Status: ready -->
<!-- CREDITS: shimooka,takagi,mumumu -->
<sect1 xml:id="language.oop5.interfaces" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>オブジェクト インターフェイス</title>
<para>
オブジェクト インターフェースでは、クラスが実装すべきメソッドやプロパティの
宣言だけを行うコードを作成できます。ここでは具体的な実装は必要ありません。
インターフェイスはクラス、トレイト、列挙型と名前空間を共有するので、
それらと同じ名前を使ってはいけません。
</para>
<para>
インターフェイスは通常のクラスと同様に定義することができますが、
キーワード <literal>class</literal> のかわりに <literal>interface</literal>
を用います。またメソッドの実装は全く定義しません。
</para>
<para>
インターフェイス内で宣言される全てのメソッドは public である必要があります。
これは、インターフェイスの特性によります。
</para>
<para>
インターフェイスには、ふたつの互いを補完する役割があります。
</para>
<simplelist>
<member>
同じインターフェイスを実装していることで、
開発者が交換可能な異なるクラスを作成できるようにします。
同じインターフェイスを持つクラスによくある例として、
複数のデータベースにアクセスするサービスや
決済のゲートウェイ、
異なるキャッシュ戦略が挙げられます。
実装が異なっていても、
それを使うコードに変更を加えることなく、それらを交換することができます。
</member>
<member>
メソッドや関数が、インターフェイスを満たす引数を受け付け、
操作できるようにします。
オブジェクトが何をするのかや、
どう実装されているのかを気にする必要はありません。
振る舞いの重要性を説明するために、
<literal>Iterable</literal> や
<literal>Cacheable</literal>、
<literal>Renderable</literal> のような名前が付けられることがよくあります。
</member>
</simplelist>
<para>
インターフェイスは、
<link linkend="language.oop5.magic">マジックメソッド</link>
を宣言しても問題ありません。
</para>
<note>
<para>
<link
linkend="language.oop5.decon.constructor">コンストラクタ</link>
をインターフェイスで宣言できますが、全くおすすめできません。
そうしてしまうと、
インターフェイスを実装するクラスの柔軟性が大きく損なわれる上、
コンストラクタには継承ルールが適用されないため、
一貫しない、予期せぬ結果を生む可能性があるからです。
</para>
</note>
<sect2 xml:id="language.oop5.interfaces.implements">
<title><literal>implements</literal></title>
<para>
インターフェイスを実装するには、<literal>implements</literal>
演算子を使用し、
このインターフェイスに含まれる全てのメソッドを実装する必要があります。
実装されていない場合、致命的エラーとなります。
各インターフェイスをカンマで区切って指定することで、
クラスは複数のインターフェイスを実装することができます。
</para>
<warning>
<para>
インターフェイスを実装するクラスでは、
インターフェイス内の名前とは異なる名前を
メソッドの引数に使うことができます。
しかし、PHP 8.0 以降では
<link linkend="functions.named-arguments">名前付き引数</link>
がサポートされています。
これは、メソッドをコールする側がインターフェイス内の名前に依存する可能性があるということです。
そうした理由から、開発者は実装されるインターフェイスと同じ引数名を使うことを強く推奨します。
</para>
</warning>
<note>
<para>
クラスと同様、インターフェイスも
<link linkend="language.oop5.inheritance">extends</link>
演算子で継承することができます。
</para>
</note>
<note>
<para>
インターフェイスを実装したクラスでは、
<link linkend="language.oop.lsp">シグネチャの互換性に関するルール</link>
を守った形で、インターフェイス内の全てのメソッドを宣言しなければいけません。
クラスは、同じ名前のメソッドを定義した複数のインターフェイスを実装することが出来ます。
この場合、実装されるメソッドは全て、
<link linkend="language.oop.lsp">シグネチャの互換性に関するルール</link>
に従わなければいけません。
そうすることで、<link linkend="language.oop5.variance">共変性と反変性</link>
のルールも適用できます。
</para>
</note>
</sect2>
<!-- Move this to OOP constants page? -->
<sect2 xml:id="language.oop5.interfaces.constants">
<title>定数</title>
<para>
インターフェイスに定数を持たせることもできます。
インターフェイス定数は <link linkend="language.oop5.constants">クラス定数</link>
とまったく同じように動作します。
PHP 8.1.0 より前のバージョンでは、
そのインターフェイスを継承したクラスやインターフェイスから定数をオーバーライドすることができませんでした。
</para>
</sect2>
<sect2 xml:id="language.oop5.interfaces.properties">
<title>プロパティ</title>
<simpara>
PHP 8.4.0 から、インターフェイスはプロパティを宣言できるようになりました。
宣言する場合、そのプロパティの読み取り可否、書き込み可否、あるいは双方を
指定する必要があります。
インターフェイスによる宣言は、public な読み書きに対してのみ適用できます。
</simpara>
<simpara>
クラス側でインターフェイスのプロパティ要件を満たすには、
public な通常のプロパティを定義する、
対応するフックを実装する public な
<link linkend="language.oop5.property-hooks.virtual">仮想プロパティ</link>
を定義するなど、複数の方法があります。
読み取り用プロパティは <literal>readonly</literal> プロパティによって満たすこともできます。
ただし、インターフェイスで「書き込み可能」と宣言されているプロパティを <literal>readonly</literal> にすることはできません。
</simpara>
<example>
<title>インターフェイスのでのプロパティ宣言の例</title>
<programlisting role="php">
<![CDATA[
<?php
interface I
{
// 実装クラスは、public に読み取り可能なプロパティを持つ必要があります。
// public に書き込み可能かどうかは制限されません。
public string $readable { get; }
// 実装クラスは、public に書き込み可能なプロパティを持つ必要があります。
// public に読み取り可能かどうかは制限されません。
public string $writeable { set; }
// 実装クラスは、public に読み書き可能なプロパティを持つ必要があります。
public string $both { get; set; }
}
// このクラスは、3つのプロパティをすべてフックのない通常のプロパティとして実装しています。
// これは問題ありません。
class C1 implements I
{
public string $readable;
public string $writeable;
public string $both;
}
// このクラスは、3つのプロパティすべてを、要求されたフックのみを使って実装しています。
// これも問題ありません。
class C2 implements I
{
private string $written = '';
private string $all = '';
// get フックのみ使い仮想プロパティを作成しています。
// 「public に読み取り可能」という要件を満たします。
// 書き込みはできませんが、インターフェイスからは要求されていません。
public string $readable { get => strtoupper($this->writeable); }
// インターフェイスはこのプロパティが「書き込み可能」であることのみ要求しています。
// ここに get の動作も含めることは全く問題ありません。
// この例では仮想プロパティを作成しています。
public string $writeable {
get => $this->written;
set {
$this->written = $value;
}
}
// このプロパティは「public に読み書き可能」でなければならないため、
// get と set の両方を実装する必要があります。
// デフォルトの動作のままでも問題ありません。
public string $both {
get => $this->all;
set {
$this->all = strtoupper($value);
}
}
}
?>
]]>
</programlisting>
</example>
</sect2>
<sect2 xml:id="language.oop5.interfaces.examples">
&reftitle.examples;
<example xml:id="language.oop5.interfaces.examples.ex1">
<title>インターフェイスの例</title>
<programlisting role="php">
<![CDATA[
<?php
// インターフェイス 'Template' を宣言する
interface Template
{
public function setVariable($name, $var);
public function getHtml($template);
}
// インターフェイスを実装する。
// これは動作します。
class WorkingTemplate implements Template
{
private $vars = [];
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
public function getHtml($template)
{
foreach($this->vars as $name => $value) {
$template = str_replace('{' . $name . '}', $value, $template);
}
return $template;
}
}
// これは動作しません。
// Fatal error: Class BadTemplate contains 1 abstract methods
// and must therefore be declared abstract (Template::getHtml)
class BadTemplate implements Template
{
private $vars = [];
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
}
?>
]]>
</programlisting>
</example>
<example xml:id="language.oop5.interfaces.examples.ex2">
<title>インターフェイスの継承</title>
<programlisting role="php">
<![CDATA[
<?php
interface A
{
public function foo();
}
interface B extends A
{
public function baz(Baz $baz);
}
// これは動作します。
class C implements B
{
public function foo()
{
}
public function baz(Baz $baz)
{
}
}
// これは動作せず、fatal error となります。
class D implements B
{
public function foo()
{
}
public function baz(Foo $foo)
{
}
}
?>
]]>
</programlisting>
</example>
<example xml:id="language.oop5.interfaces.examples.variance.multiple.interfaces">
<title>共変性を保った形で、複数のインターフェイスを実装する</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo {}
class Bar extends Foo {}
interface A {
public function myfunc(Foo $arg): Foo;
}
interface B {
public function myfunc(Bar $arg): Bar;
}
class MyClass implements A, B
{
public function myfunc(Foo $arg): Bar
{
return new Bar();
}
}
?>
]]>
</programlisting>
</example>
<example xml:id="language.oop5.interfaces.examples.ex3">
<title>複数のインターフェイスの継承</title>
<programlisting role="php">
<![CDATA[
<?php
interface A
{
public function foo();
}
interface B
{
public function bar();
}
interface C extends A, B
{
public function baz();
}
class D implements C
{
public function foo()
{
}
public function bar()
{
}
public function baz()
{
}
}
?>
]]>
</programlisting>
</example>
<example xml:id="language.oop5.interfaces.examples.ex4">
<title>インターフェイスでの定数</title>
<programlisting role="php">
<![CDATA[
<?php
interface A
{
const B = 'Interface constant';
}
// Interface constant と表示します。
echo A::B;
class B implements A
{
const B = 'Class constant';
}
// Class constant と表示します。
// PHP 8.1.0 より前のバージョンでは、定数をオーバライドできなかったため、これは動作しませんでした。
echo B::B;
?>
]]>
</programlisting>
</example>
<example xml:id="language.oop5.interfaces.examples.ex5">
<title>抽象クラスとインターフェイス</title>
<programlisting role="php">
<![CDATA[
<?php
interface A
{
public function foo(string $s): string;
public function bar(int $i): int;
}
// 抽象クラスは、インターフェイスの一部のみを実装しても構いません。
// 抽象クラスを継承したクラスは、未実装の残りのメソッドを実装しなければなりません。
abstract class B implements A
{
public function foo(string $s): string
{
return $s . PHP_EOL;
}
}
class C extends B
{
public function bar(int $i): int
{
return $i * 2;
}
}
?>
]]>
</programlisting>
</example>
<example xml:id="language.oop5.interfaces.examples.ex6">
<title>継承と実装を同時に行う</title>
<programlisting role="php">
<![CDATA[
<?php
class One
{
/* ... */
}
interface Usable
{
/* ... */
}
interface Updatable
{
/* ... */
}
// ここでは、キーワードの順番が重要です。
// 'extends' を始めに置かなければいけません。
class Two extends One implements Usable, Updatable
{
/* ... */
}
?>
]]>
</programlisting>
</example>
<para>
インターフェイスと型宣言を組み合わせると、
特定のオブジェクトに特定のメソッドをうまく持たせることができます。
<link linkend="language.operators.type">instanceof</link> 演算子および
<link linkend="language.types.declarations">型宣言</link>
を参照ください。
</para>
</sect2>
</sect1>
<!-- 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
-->