オブジェクトの継承
プログラミング言語の原則としてよくみられるものに継承があります。 PHP はオブジェクトモデルにおいてこの継承を利用しています。 多くのクラスとオブジェクトとの連携に継承は関係しています。
例えば、クラスを拡張するとき、サブクラスは親クラスから public と、protected のメソッドや、プロパティや定数をすべて引き継ぎます。 (子の)クラスが親のメソッドを上書きしない限り、 親のメソッドの機能が保持されます。
これは、機能を定義して抽象化するのに便利です。 また、同じようなオブジェクトに機能を追加する際に、 共通機能を再実装する必要がなくなります。
親クラスの private メソッドは、子クラスからアクセスすることができません。
その結果として、子クラスは通常の継承のルールを無視して
private メソッドそのものを再実装することができてしまいます。
しかし、PHP 8.0.0 より前のバージョンでは、
final
と static
に関連する制限が private メソッドに適用されていました。
PHP 8.0.0 以降では、private メソッドの規則が強制されるのは
private final
として宣言されたコンストラクタのみになりました。
なぜなら、static として宣言されたファクトリメソッドを使う場合に、
コンストラクタを無効にする方法として private final
が用いられるからです。
メソッドやプロパティ、そして定数の
アクセス権
に関するルールは、子クラスで緩めることが可能です。
たとえば、
親クラスで protected
なメソッドは
子クラスで public
としてマークできます。
しかし、厳しくすることはできません。
つまり、親クラスで public
なプロパティを
子クラスで private
にすることはできません。
これの例外はコンストラクタです。
コンストラクタのアクセス権は厳しくすることができます。
たとえば、親クラスの public
なコンストラクタは、
子クラスで private
としてマークできます。
注意:
オートローディングが有効になっていない限り、 クラスの定義は実際に使うより前になければなりません。 別のクラスを継承したクラスの場合は、 そのクラスより前に親クラスが宣言されていなければなりません。 この規則が適用されるのは、別のクラスやインターフェイスを継承したクラスです。
注意:
読み取りと書き込みが両方可能なプロパティを、 readonly として上書きしてはいけません。逆も同じです。
<?php
class A {
public int $prop;
}
class B extends A {
// 正しくありません: read-write -> readonly
public readonly int $prop;
}
?>
例1 継承の例
<?php
class Foo
{
public function printItem($string)
{
echo 'Foo: ' . $string . PHP_EOL;
}
public function printPHP()
{
echo 'PHP is great.' . PHP_EOL;
}
}
class Bar extends Foo
{
public function printItem($string)
{
echo 'Bar: ' . $string . PHP_EOL;
}
}
$foo = new Foo();
$bar = new Bar();
$foo->printItem('baz'); // 出力: 'Foo: baz'
$foo->printPHP(); // 出力: 'PHP is great'
$bar->printItem('baz'); // 出力: 'Bar: baz'
$bar->printPHP(); // 出力: 'PHP is great'
?>
内部クラスと戻り値の型の互換性
PHP 8.1 より前のバージョンでは、 ほとんどの内部クラスやメソッドは戻り値の型を宣言していませんでしたし、 それらを継承する際にもあらゆる戻り値の型を指定することができていました。
PHP 8.1.0 以降では、 ほとんどの内部メソッドが、戻り値の型を "とりあえず" 宣言するようになりました。 この場合、それらを継承したメソッドの戻り値の型は、 親と互換性があるものにすべきです。 そうしない場合、推奨されない警告が発生します。 注意すべきなのは、明示的に戻り値の型を宣言していない場合でも、 シグネチャが一致していないとみなされ、 推奨されない警告が発生することです。
PHP のバージョン間の互換性を保ちたいがために、 戻り値の型を宣言できない場合、 アトリビュート ReturnTypeWillChange を追加することで警告を抑止できます。
例2 戻り値の型を宣言せずにメソッドをオーバーライドする場合
<?php
class MyDateTime extends DateTime
{
public function modify(string $modifier) { return false; }
}
// PHP 8.1.0 以降では、"Deprecated: Return type of MyDateTime::modify(string $modifier) should either be compatible with DateTime::modify(string $modifier): DateTime|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice"
?>
例3 間違った型を宣言してメソッドをオーバーライドする場合
<?php
class MyDateTime extends DateTime
{
public function modify(string $modifier): ?DateTime { return null; }
}
// PHP 8.1.0 以降では、"Deprecated: Return type of MyDateTime::modify(string $modifier): ?DateTime should either be compatible with DateTime::modify(string $modifier): DateTime|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice"
?>
例4 間違った型を宣言してメソッドをオーバーライドする場合でも、警告を抑制する
<?php
class MyDateTime extends DateTime
{
/**
* @return DateTime|false
*/
#[\ReturnTypeWillChange]
public function modify(string $modifier) { return false; }
}
// 警告は発生しません。
?>
User Contributed Notes 5 notes
Here is some clarification about PHP inheritance – there is a lot of bad information on the net. PHP does support Multi-level inheritance. (I tested it using version 5.2.9). It does not support multiple inheritance.
This means that you cannot have one class extend 2 other classes (see the extends keyword). However, you can have one class extend another, which extends another, and so on.
Example:
<?php
class A {
// more code here
}
class B extends A {
// more code here
}
class C extends B {
// more code here
}
$someObj = new A(); // no problems
$someOtherObj = new B(); // no problems
$lastObj = new C(); // still no problems
?>
I think the best way for beginners to understand inheritance is through a real example so here is a simple example I can gave to you
<?php
class Person
{
public $name;
protected $age;
private $phone;
public function talk(){
//Do stuff here
}
protected function walk(){
//Do stuff here
}
private function swim(){
//Do stuff here
}
}
class Tom extends Person
{
/*Since Tom class extends Person class this means
that class Tom is a child class and class person is
the parent class and child class will inherit all public
and protected members(properties and methods) from
the parent class*/
/*So class Tom will have these properties and methods*/
//public $name;
//protected $age;
//public function talk(){}
//protected function walk(){}
//but it will not inherit the private members
//this is all what Object inheritance means
}
The Idea that multiple inheritence is not supported is correct but with tratits this can be reviewed.
for e.g.
<?php
trait custom
{
public function hello()
{
echo "hello";
}
}
trait custom2
{
public function hello()
{
echo "hello2";
}
}
class inheritsCustom
{
use custom, custom2
{
custom2::hello insteadof custom;
}
}
$obj = new inheritsCustom();
$obj->hello();
?>
You can force a class to be strictly an inheritable class by using the "abstract" keyword. When you define a class with abstract, any attempt to instantiate a separate instance of it will result in a fatal error. This is useful for situations like a base class where it would be inherited by multiple child classes yet you want to restrict the ability to instantiate it by itself.
Example........
<?php
abstract class Cheese
{
//can ONLY be inherited by another class
}
class Cheddar extends Cheese
{
}
$dinner = new Cheese; //fatal error
$lunch = new Cheddar; //works!
?>
I was recently extending a PEAR class when I encountered a situation where I wanted to call a constructor two levels up the class hierarchy, ignoring the immediate parent. In such a case, you need to explicitly reference the class name using the :: operator.
Fortunately, just like using the 'parent' keyword PHP correctly recognizes that you are calling the function from a protected context inside the object's class hierarchy.
E.g:
<?php
class foo
{
public function something()
{
echo __CLASS__; // foo
var_dump($this);
}
}
class foo_bar extends foo
{
public function something()
{
echo __CLASS__; // foo_bar
var_dump($this);
}
}
class foo_bar_baz extends foo_bar
{
public function something()
{
echo __CLASS__; // foo_bar_baz
var_dump($this);
}
public function call()
{
echo self::something(); // self
echo parent::something(); // parent
echo foo::something(); // grandparent
}
}
error_reporting(-1);
$obj = new foo_bar_baz();
$obj->call();
// Output similar to:
// foo_bar_baz
// object(foo_bar_baz)[1]
// foo_bar
// object(foo_bar_baz)[1]
// foo
// object(foo_bar_baz)[1]
?>