1
0
mirror of https://github.com/php/doc-ja.git synced 2026-03-23 22:52:11 +01:00
Files
archived-doc-ja/language/oop5/basic.xml
Louis-Arnaud e94b0c04ce 引数(arguments)とパラメータ(parameters)の区別を修正 (#327)
* 引数(arguments)とパラメータ(parameters)の区別を修正

Closes #62

* 引数/パラメータの使い分けを修正(Copilot review反映)

* Use パラメータ consistently per README_Glossary.md
2026-02-27 12:40:26 +09:00

863 lines
24 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<!-- EN-Revision: fc068b4857342eb409efeafe6723058f39ab1045 Maintainer: takagi Status: ready -->
<!-- CREDITS: hirokawa,mumumu,jdkfx -->
<sect1 xml:id="language.oop5.basic" xmlns="http://docbook.org/ns/docbook">
<title>クラスの基礎</title>
<sect2 xml:id="language.oop5.basic.class">
<title>class</title>
<para>
各クラスの定義は、<literal>class</literal>キーワードで始まり、クラス名が続きます。
そしてその後に波括弧のペアが続き、
その中にはクラスのプロパティとメソッドの定義を記述します。
</para>
<para>
クラス名には、PHP の<link linkend="reserved">予約語</link>
以外でラベルとして有効なあらゆる名前を使用することができます。
PHP 8.4.0 以降では、アンダースコア (<literal>_</literal>)
1文字のみのクラス名は非推奨となりました。
有効なクラス名は、先頭が文字あるいはアンダースコアで始まり、
その後に任意の数の文字/数字/アンダースコアが続くものです。
正規表現で表すと、
<code>^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$</code>
のようになります。
</para>
<para>
クラスの中には、
<link linkend="language.oop5.constants">定数</link>
<link linkend="language.oop5.properties">変数</link>
("プロパティ" といいます) そして関数 ("メソッド" といいます)
を含めることができます。
</para>
<example>
<title>簡単なクラス定義</title>
<programlisting role="php">
<![CDATA[
<?php
class SimpleClass
{
// プロパティの宣言
public $var = 'a default value';
// メソッドの宣言
public function displayVar() {
echo $this->var;
}
}
?>
]]>
</programlisting>
</example>
<para>
メソッドがオブジェクトのコンテキストからコールされる場合は、
疑似変数 <varname>$this</varname> が利用可能です。
<varname>$this</varname> は、呼び出し元オブジェクトの値です。
</para>
<warning>
<para>
static でないメソッドを static メソッドとしてコールすると、
<classname>Error</classname> がスローされます。
PHP 8.0.0 より前のバージョンでは、推奨されない警告が発生し、
<varname>$this</varname> が未定義になっていました。
</para>
<example xml:id="language.oop5.basic.class.this">
<title><varname>$this</varname> 疑似変数の例</title>
<programlisting role="php">
<![CDATA[
<?php
class A
{
function foo()
{
if (isset($this)) {
echo '$this is defined (';
echo get_class($this);
echo ")\n";
} else {
echo "\$this is not defined.\n";
}
}
}
class B
{
function bar()
{
A::foo();
}
}
$a = new A();
$a->foo();
A::foo();
$b = new B();
$b->bar();
B::bar();
?>
]]>
</programlisting>
&example.outputs.7;
<screen>
<![CDATA[
$this is defined (A)
Deprecated: Non-static method A::foo() should not be called statically in %s on line 27
$this is not defined.
Deprecated: Non-static method A::foo() should not be called statically in %s on line 20
$this is not defined.
Deprecated: Non-static method B::bar() should not be called statically in %s on line 32
Deprecated: Non-static method A::foo() should not be called statically in %s on line 20
$this is not defined.
]]>
</screen>
&example.outputs.8;
<screen>
<![CDATA[
$this is defined (A)
Fatal error: Uncaught Error: Non-static method A::foo() cannot be called statically in %s :27
Stack trace:
#0 {main}
thrown in %s on line 27
]]>
</screen>
</example>
</warning>
<sect3 xml:id="language.oop5.basic.class.readonly">
<title>読み取り専用クラス</title>
<para>
PHP 8.2.0 以降では、
クラスに対して <modifier>readonly</modifier> を指定することができます。
クラスに対して <modifier>readonly</modifier> を指定すると、
宣言されている全ての <link linkend="language.oop5.properties.readonly-properties">プロパティに対して <modifier>readonly</modifier> を指定した</link> ことになり、
かつ <link linkend="language.oop5.properties.dynamic-properties">動的なプロパティ</link> の作成を禁止したことになります。
さらに、<classname>AllowDynamicProperties</classname>
アトリビュートを指定しても動的なプロパティを作成できなくなります。
動的なプロパティを作成しようとすると、コンパイル時にエラーが発生します。
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
#[\AllowDynamicProperties]
readonly class Foo {
}
// Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo
?>
]]>
</programlisting>
</informalexample>
<para>
型を指定していないプロパティや、
static プロパティ に対しては <literal>readonly</literal> を指定できません。
readonly クラスには、それらをいずれも指定できません:
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
readonly class Foo
{
public $bar;
}
// Fatal error: Readonly property Foo::$bar must have type
?>
]]>
</programlisting>
<programlisting role="php">
<![CDATA[
<?php
readonly class Foo
{
public static int $bar;
}
// Fatal error: Readonly class Foo cannot declare static properties
?>
]]>
</programlisting>
</informalexample>
<para>
<modifier>readonly</modifier> を指定したクラスは、
子クラスでも <modifier>readonly</modifier> を指定した場合にのみ
<link linkend="language.oop5.basic.extends">継承</link> できます。
</para>
</sect3>
</sect2>
<sect2 xml:id="language.oop5.basic.new">
<title>new</title>
<para>
あるクラスのインスタンスを生成するには、<literal>new</literal>
キーワードを使わなければなりません。エラー時に
<link linkend="language.exceptions">例外</link>をスローするような
<link linkend="language.oop5.decon">コンストラクタ</link>を定義していない限り、
オブジェクトが常に生成されます。
クラスは、そのインスタンスを作成する前に定義すべきです
(これが必須となる場合もあります)。
</para>
<para>
クラス名を含む文字列を <literal>new</literal> で指定すると、
そのクラスのインスタンスを作成します。クラスが名前空間に属している場合は、
完全修飾名を指定しなければなりません。
</para>
<note>
<para>
クラスのコンストラクタに引数を渡さなかった場合、
クラス名の後の括弧は省略しても構いません。
</para>
</note>
<example>
<title>インスタンスを作成する</title>
<programlisting role="php">
<![CDATA[
<?php
class SimpleClass {
}
$instance = new SimpleClass();
var_dump($instance);
// 変数を使うこともできます
$className = 'SimpleClass';
$instance = new $className(); // new SimpleClass()
var_dump($instance);
?>
]]>
</programlisting>
</example>
<para>
PHP 8.0.0 以降では、
<literal>new</literal> を任意の式と一緒に使う機能がサポートされました。
これによって、式が文字列を生成する場合に、
より複雑なインスタンス化を行えるようになります。
式は括弧で囲まなければいけません。
</para>
<example>
<title>任意の式を使ってインスタンスを生成する</title>
<para>
以下の例では、
クラス名を生成する有効な任意の式を複数示します。
関数呼び出し、文字列連結、そして
<constant>::class</constant> 定数です。
</para>
<programlisting role="php">
<![CDATA[
<?php
class ClassA extends \stdClass {}
class ClassB extends \stdClass {}
class ClassC extends ClassB {}
class ClassD extends ClassA {}
function getSomeClass(): string
{
return 'ClassA';
}
var_dump(new (getSomeClass()));
var_dump(new ('Class' . 'B'));
var_dump(new ('Class' . 'C'));
var_dump(new (ClassD::class));
?>
]]>
</programlisting>
&example.outputs.8;
<screen>
<![CDATA[
object(ClassA)#1 (0) {
}
object(ClassB)#1 (0) {
}
object(ClassC)#1 (0) {
}
object(ClassD)#1 (0) {
}
]]>
</screen>
</example>
<para>
クラスのコンテキストにおいては、
<literal>new self</literal><literal>new parent</literal>
のようにして新しいオブジェクトを作成することができます。
</para>
<para>
作成済みのクラスのインスタンスを新たな変数に代入する場合、新しい変数は、
代入されたオブジェクトと同じインスタンスにアクセスします。
この動作は、インスタンスを関数に渡す場合も同様です。
作成済みのオブジェクトのコピーは、その
<link linkend="language.oop5.cloning">クローンを作成</link>
することにより作成可能です。
</para>
<example>
<title>オブジェクトの代入</title>
<programlisting role="php">
<![CDATA[
<?php
class SimpleClass {
public string $var;
}
$instance = new SimpleClass();
$assigned = $instance;
$reference =& $instance;
$instance->var = '$assigned will have this value';
$instance = null; // $instance と $reference は null になります
var_dump($instance);
var_dump($reference);
var_dump($assigned);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
NULL
NULL
object(SimpleClass)#1 (1) {
["var"]=>
string(30) "$assigned will have this value"
}
]]>
</screen>
</example>
<para>
複数のやり方で、オブジェクトのインスタンスを作ることが出来ます:
</para>
<example>
<title>新しいオブジェクトの作成</title>
<programlisting role="php">
<![CDATA[
<?php
class Test
{
public static function getNew()
{
return new static();
}
}
class Child extends Test {}
$obj1 = new Test(); // By the class name
$obj2 = new $obj1(); // Through the variable containing an object
var_dump($obj1 !== $obj2);
$obj3 = Test::getNew(); // By the class method
var_dump($obj3 instanceof Test);
$obj4 = Child::getNew(); // Through a child class method
var_dump($obj4 instanceof Child);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
bool(true)
bool(true)
bool(true)
]]>
</screen>
</example>
<para>
新しく作成したオブジェクトのメンバーに、作成したその式の中でもアクセスすることができます。
</para>
<example>
<title>新しく作成したオブジェクトのメンバーへのアクセス</title>
<programlisting role="php">
<![CDATA[
<?php
echo (new DateTime())->format('Y'), PHP_EOL;
// PHP 8.4.0 以降、囲む括弧は省略可能です。
echo new DateTime()->format('Y'), PHP_EOL;
?>
]]>
</programlisting>
&example.outputs.similar;
<screen>
<![CDATA[
2025
2025
]]>
</screen>
</example>
<note>
<simpara>
PHP 7.1 より前のバージョンでは、コンストラクタが定義されない場合、それへの引数が評価されていませんでした。
</simpara>
</note>
</sect2>
<sect2 xml:id="language.oop5.basic.properties-methods">
<title>プロパティとメソッド</title>
<para>
クラスのプロパティとメソッドは、それぞれ別の "名前空間" に存在するので、
同じ名前のプロパティとメソッドを共存させることもできます。
プロパティを参照する場合もメソッドを参照する場合も書きかたは同じです。
それがプロパティへのアクセスなのかメソッドの呼び出しなのかは、そのコンテキストによって決まります。
つまり、変数にアクセスしようとしているのか関数を呼び出そうとしているのかの違いです。
</para>
<example>
<title>プロパティへのアクセスとメソッドの呼び出し</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo
{
public $bar = 'property';
public function bar() {
return 'method';
}
}
$obj = new Foo();
echo $obj->bar, PHP_EOL, $obj->bar(), PHP_EOL;
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
property
method
]]>
</screen>
</example>
<para>
これはつまり、プロパティに <link linkend="functions.anonymous">無名関数</link>
を代入した場合に、その関数は直接呼び出せないということです。
その場合は、たとえば事前にプロパティを変数に代入しておく必要があります。
括弧で囲むと、プロパティを直接呼び出すこともできます。
</para>
<example>
<title>プロパティに格納した無名関数の呼び出し</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo
{
public $bar;
public function __construct() {
$this->bar = function() {
return 42;
};
}
}
$obj = new Foo();
echo ($obj->bar)(), PHP_EOL;
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
42
]]>
</screen>
</example>
</sect2>
<sect2 xml:id="language.oop5.basic.extends">
<!-- TODO Example about class constant redefinition -->
<!-- TODO Split into it's own page? -->
<title>extends</title>
<para>
クラスは、宣言部に <literal>extends</literal> キーワードを含めることで、
他のクラスの定数や、メソッド、
およびプロパティを継承することができます。多重継承を行うことはできず、クラスが継承できるベース
クラスは一つだけです。
</para>
<para>
継承された定数やメソッド、プロパティをオーバーライド(上書き)するには、
親クラスで定義されているのと同じ名前でそれを再宣言します。
しかし、親クラスでそのメソッドや定数が
<link linkend="language.oop5.final">final</link>
として定義されている場合はオーバーライドできません。
オーバーライドされた元のメソッドやstaticプロパティにアクセスするには、
<link linkend="language.oop5.paamayim-nekudotayim">parent::</link>
で参照します。
</para>
<note>
<simpara>
PHP 8.1.0 以降では、定数も final として宣言できます。
</simpara>
</note>
<example>
<title>簡単なクラスの継承</title>
<programlisting role="php">
<![CDATA[
<?php
class SimpleClass
{
function displayVar()
{
echo "Parent class\n";
}
}
class ExtendClass extends SimpleClass
{
// 親クラスのメソッドを再定義
function displayVar()
{
echo "Extending class\n";
parent::displayVar();
}
}
$extended = new ExtendClass();
$extended->displayVar();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Extending class
Parent Class
]]>
</screen>
</example>
<sect3 xml:id="language.oop.lsp">
<title>シグネチャの互換性に関するルール</title>
<para>
メソッドをオーバーライドするときは、
子クラスのシグネチャが親クラスのそれと互換性がなければいけません。
互換性が壊れた場合、致命的なエラーが発生します。
PHP 8.0.0 より前のバージョンでは、
互換性が壊れた場合に、<constant>E_WARNING</constant> レベルの警告が発生していました。
<link linkend="language.oop5.variance">共変性と反変性</link>
の規則を守っている場合は、シグネチャに互換性があります。
必須のパラメータをオプションにした場合も、互換性があります。
新しいオプションのパラメータを追加しただけで、アクセス権を厳しくせず、
緩めただけの場合も互換性があります。
これは、リスコフの置換原則(Liskov Substitution Principle)、
略して LSP として知られています。
但し、<link linkend="language.oop5.decon.constructor">コンストラクタ</link>
<literal>private</literal> メソッドについては、
この規則の例外で、
オーバライドしたシグネチャにミスマッチがあっても致命的なエラーにはなりません。
</para>
<example>
<title>互換性がある子クラスのメソッド</title>
<programlisting role="php">
<![CDATA[
<?php
class Base
{
public function foo(int $a) {
echo "Valid\n";
}
}
class Extend1 extends Base
{
function foo(int $a = 5)
{
parent::foo($a);
}
}
class Extend2 extends Base
{
function foo(int $a, $b = 5)
{
parent::foo($a);
}
}
$extended1 = new Extend1();
$extended1->foo();
$extended2 = new Extend2();
$extended2->foo(1);
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Valid
Valid
]]>
</screen>
</example>
<para>
次の例は、パラメータを削除した子クラスのメソッドや、
オプションのパラメータを必須にしたりすることが、親クラスのメソッドと互換性がなくなることを示しています。
</para>
<example>
<title>子クラスのメソッドでパラメータを削除すると致命的なエラーになる</title>
<programlisting role="php">
<![CDATA[
<?php
class Base
{
public function foo(int $a = 5) {
echo "Valid\n";
}
}
class Extend extends Base
{
function foo()
{
parent::foo(1);
}
}
]]>
</programlisting>
&example.outputs.8.similar;
<screen>
<![CDATA[
Fatal error: Declaration of Extend::foo() must be compatible with Base::foo(int $a = 5) in /in/evtlq on line 13
]]>
</screen>
</example>
<example>
<title>子クラスのメソッドで、オプションのパラメータを必須にすると致命的なエラーになる</title>
<programlisting role="php">
<![CDATA[
<?php
class Base
{
public function foo(int $a = 5) {
echo "Valid\n";
}
}
class Extend extends Base
{
function foo(int $a)
{
parent::foo($a);
}
}
]]>
</programlisting>
&example.outputs.8.similar;
<screen>
<![CDATA[
Fatal error: Declaration of Extend::foo(int $a) must be compatible with Base::foo(int $a = 5) in /in/qJXVC on line 13
]]>
</screen>
</example>
<warning>
<para>
メソッドのパラメータの名前を子クラスで変更しても、
シグネチャ上は非互換になりません。
しかし、こうしてしまうと
<link linkend="functions.named-arguments">名前付き引数</link>
を使った時に実行時エラーになるので、おすすめできません。
</para>
<example>
<title>子クラスでパラメータの名前を変更し、かつ名前付き引数を使うとエラーになる</title>
<programlisting role="php">
<![CDATA[
<?php
class A {
public function test($foo, $bar) {}
}
class B extends A {
public function test($a, $b) {}
}
$obj = new B;
// A::test() の規約に従って引数を渡す
$obj->test(foo: "foo", bar: "bar"); // エラー発生!
]]>
</programlisting>
&example.outputs.similar;
<screen>
<![CDATA[
Fatal error: Uncaught Error: Unknown named parameter $foo in /in/XaaeN:14
Stack trace:
#0 {main}
thrown in /in/XaaeN on line 14
]]>
</screen>
</example>
</warning>
</sect3>
</sect2>
<sect2 xml:id="language.oop5.basic.class.class">
<title>::class</title>
<para>
<literal>class</literal> キーワードでもクラス名の解決を行うことが出来ます。
クラスの名前が <literal>ClassName</literal> になっているクラスの完全修飾名を取得するには、
<literal>ClassName::class</literal> を使います。
これは、<link linkend="language.namespaces">名前空間</link>付きのクラスに使うと特に便利です。
</para>
<para>
<example xml:id="language.oop5.basic.class.class.name">
<title>クラス名の解決</title>
<programlisting role="php">
<![CDATA[
<?php
namespace NS {
class ClassName {
}
echo ClassName::class;
}
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
NS\ClassName
]]>
</screen>
</example>
</para>
<note>
<para>
<literal>::class</literal> によるクラス名の解決は、コンパイル時の変換です。
つまり、クラス名を作るタイミングでは、まだオートロードが行われていないということです。
結果的に、クラスがまだ存在しない時点でクラス名が展開されることになります。
この場合にはエラーは発生しません。
</para>
<example xml:id="language.oop5.basic.class.class.fail">
<title>クラス名が存在しない場合の名前解決</title>
<programlisting role="php">
<![CDATA[
<?php
print Does\Not\Exist::class;
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Does\Not\Exist
]]>
</screen>
</example>
</note>
<para>
PHP 8.0.0 以降では、
<literal>::class</literal> はオブジェクトに対しても使えるようになりました。
この名前解決はコンパイル時ではなく、実行時に行われます。
この場合、<function>get_class</function> をオブジェクトに対して使った時と同じ動きをします。
</para>
<example xml:id="language.oop5.basic.class.class.object">
<title>オブジェクトの名前解決</title>
<programlisting role="php">
<![CDATA[
<?php
namespace NS {
class ClassName {
}
$c = new ClassName();
print $c::class;
}
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
NS\ClassName
]]>
</screen>
</example>
</sect2>
<sect2 xml:id="language.oop5.basic.nullsafe">
<title>nullsafe メソッドとプロパティ</title>
<para>
PHP 8.0.0 以降では、プロパティやメソッドは
"nullsafe" 演算子 <literal>?-></literal> を使ってアクセスすることもできます。
nullsafe 演算子は既に述べたメソッドやプロパティと同じように振る舞いますが、
オブジェクトが &null; と評価された場合に、例外はスローされず、
&null; が返される点だけが異なります。
オブジェクトの評価がチェインの一部だった場合は、
残りのチェインはスキップされます。
</para>
<para>
この演算子の働きは、
オブジェクトにアクセスするたびに <function>is_null</function> でラップするコードに似ています。
しかし、よりコンパクトです。
</para>
<para>
<example>
<title>nullsafe 演算子</title>
<programlisting role="php" annotations="non-interactive">
<![CDATA[
<?php
// PHP 8.0.0 以降は、このように書けます:
$result = $repository?->getUser(5)?->name;
// これは、以下のコードチェックと同等です:
if (is_null($repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (is_null($user)) {
$result = null;
} else {
$result = $user->name;
}
}
?>
]]>
</programlisting>
</example>
</para>
<note>
<para>
nullsafe 演算子は、プロパティやメソッドが値を返す際、
null が正しく、期待されうる値とみなせる場合に一番よく使います。
エラーを示すためなら、例外をスローするほうが好ましいです。
</para>
</note>
</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
-->