PHP 8.3.4 Released!

Herencia de Objetos

La herencia es un principio de programación bien establecido y PHP hace uso de él en su modelado de objetos. Este principio afectará la manera en que muchas clases y objetos se relacionan unas con otras.

Por ejemplo, cuando se extiende una clase, la subclase hereda todos los métodos públicos y protegidos de la clase padre. A menos que una clase sobrescriba esos métodos, mantendrán su funcionalidad original.

Esto es útil para la definición y abstracción de la funcionalidad y permite la implementación de funcionalidad adicional en objetos similares sin la necesidad de reimplementar toda la funcionalidad compartida.

Los métodos privados de una clase padre no son accesibles para una clase hija. Como resultado, las clases hijas pueden reimplementar un método privado sin tener en cuenta las reglas de herencia normales. Antes de PHP 8.0.0, sin embargo, se aplicaban las restricciones final y static a los métodos privados. A partir de PHP 8.0.0, la única restricción de métodos privados es private final que se aplica en el constructor de la clase, ya que es una forma común de "deshabilitar" el constructor cuando es usado en métodos estáticos de fábrica (Patrón Factory).

La visibilidad de los métodos, propiedades y constantes pueden ser relajarse, por ejemplo, un méetodo protected puede marcase como public, pero no pueden restringirse, por ejemplo. marcando una propiedad public como private.

Nota:

A menos que la carga automática sea utilizada, entonces las clases deben ser definidas antes de ser usadas. Si una clase se extiende a otra, entonces la clase padre debe ser declarada antes de la estructura de clase hija. Esta regla se aplica a las clases que heredan de otras clases e interfaces.

Nota:

No está permitido sobrescribir una propiedad de lectura-escritura con una propiedad de sólo lectura o viceversa.

<?php

class A {
public
int $prop;
}
class
B extends A {
// Ilegal: lectura-escritura -> sólo lectura
public readonly int $prop;
}
?>

Ejemplo #1 Ejemplo de herencia

<?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'); // Salida: 'Foo: baz'
$foo->printPHP(); // Salida: 'PHP is great'
$bar->printItem('baz'); // Salida: 'Bar: baz'
$bar->printPHP(); // Salida: 'PHP is great'

?>

Return Type Compatibility with Internal Classes

Prior to PHP 8.1, most internal classes or methods didn't declare their return types, and any return type was allowed when extending them.

As of PHP 8.1.0, most internal methods started to "tentatively" declare their return type, in that case the return type of methods should be compatible with the parent being extended; otherwise, a deprecation notice is emitted. Note that lack of an explicit return declaration is also considered a signature mismatch, and thus results in the deprecation notice.

If the return type cannot be declared for an overriding method due to PHP cross-version compatibility concerns, a #[ReturnTypeWillChange] attribute can be added to silence the deprecation notice.

Ejemplo #2 The overriding method does not declare any return type

<?php
class MyDateTime extends DateTime
{
public function
modify(string $modifier) { return false; }
}

// "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" as of PHP 8.1.0
?>

Ejemplo #3 The overriding method declares a wrong return type

<?php
class MyDateTime extends DateTime
{
public function
modify(string $modifier): ?DateTime { return null; }
}

// "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" as of PHP 8.1.0
?>

Ejemplo #4 The overriding method declares a wrong return type without a deprecation notice

<?php
class MyDateTime extends DateTime
{
/**
* @return DateTime|false
*/
#[ReturnTypeWillChange]
public function
modify(string $modifier) { return false; }
}

// No notice is triggered
?>
add a note

User Contributed Notes 8 notes

up
212
jackdracona at msn dot com
13 years ago
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

?>
up
100
Mohammad Istanbouly
6 years ago
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
}
up
16
akashwebdev at gmail dot com
8 years ago
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();
?>
up
21
strata_ranger at hotmail dot com
13 years ago
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]

?>
up
14
jarrod at squarecrow dot com
14 years ago
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!

?>
up
-4
Anonymous
5 years ago
PHP7 gives you a warning if you redeclare a function in a child class with different parameters. For example:

class foo {
function print($text='') {
print text;
}
}

class bar extends foo {
function print($text1='',$text2='') {
print text1.text2
}
}

will give a PHP Warning: Declaration of bar::print($text1 = '', $text2 = '') should be compatible with foo::print($text= '').
up
-6
niemans at pbsolo dot nl
4 years ago
Inheritance works at create time, i.e. using the keyword 'new'. Static properties confused my understanding, so in order tho show the effect of visibility to inherintence I've created a simple demo script along with some set and get magic:

<?php
class A {
private
$a = 'private';
protected
$b = 'protected';
public
$c = 'public';
static
$d = 'static';
public function
__construct()
{
$this->e = 'constructed';
}
public function
__set($property, $value)
{
echo
' set ' . $property . '=' . $value;
$this->$property=$value;
}
public function
__get($property)
{
echo
' get ' . $property;
$this->$property = 'dynamic'; // invokes __set() !!
return $this->$property;
}
}

class
B extends A
{
public function
constructMe()
{
$this->e = 'constructed2';
}
}

class
C extends B
{
public function
__construct()
{
parent::constructMe();
}
}

echo
" \n";
$a = new A();
$b = new B();
echo
" \n";
echo
' B:c='.$b->c;
echo
" \n";
echo
' B:d=' .$b->d;
echo
" \n";

$c = new C();
echo
" \n";

print_r($a);
print_r($b);
print_r($c);

print_r(A::$d);
print_r(B::$d);
print_r(C::$d);

echo
'A class: ';
$R = new reflectionclass('A');
print_r($R->getdefaultproperties());
print_r($R->getstaticproperties());
echo
'B class: ';
$R = new reflectionclass('B');
print_r($R->getdefaultproperties());
print_r($R->getstaticproperties());

?>

This outputs:

set e=constructed
B:c=public
get d set d=dynamic B:d=dynamic
set e=constructed2
A Object
(
[a:A:private] => private
[b:protected] => protected
[c] => public
[e] => constructed
)
B Object
(
[a:A:private] => private
[b:protected] => protected
[c] => public
[d] => dynamic
)
C Object
(
[a:A:private] => private
[b:protected] => protected
[c] => public
[e] => constructed2
)
staticstaticstaticA class: Array
(
[d] => static
[a] => private
[b] => protected
[c] => public
)
Array
(
[d] => static
)
B class: Array
(
[d] => static
[b] => protected
[c] => public
)
Array
(
[d] => static
)

This shows how private variables ($a) are inherited, how static variables ($d) are inherited (by the class, not by the object) and that changing or adding variables in the parent ($e, $d) are not inherited by the child.
up
-8
sibian0218 at gmail dot com
5 years ago
I've noticed one thing concerning inheritance...
When declaring an abstract class with a private method,
which is overridden by a sub-class, private takes precedence over public for child class...
(in the case you're redeclaring a method with a different signature in fact).

Hope this helps
To Top