Lo básico

class

La definición básica de una clase comienza con la palabra reservada class, seguida de un nombre de clase, y continuando con un par de llaves que encierran las definiciones de las propiedades y métodos pertenecientes a dicha clase.

El nombre de clase puede ser cualquier etiqueta válida, siempre que no sea una palabra reservada de PHP. Un nombre válido de clase comienza con una letra o un guión bajo, seguido de una cantidad arbitraria de letras, números o guiones bajos. Como expresión regular, se expresaría de la siguiente forma: ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$.

Una clase puede tener sus propias constantes, variables (llamadas "propiedades"), y funciones (llamados "métodos").

Ejemplo #1 Definición de una clase sencilla

<?php
class ClaseSencilla
{
// Declaración de una propiedad
public $var = 'un valor predeterminado';

// Declaración de un método
public function mostrarVar() {
echo
$this->var;
}
}
?>

La pseudovariable $this está disponible cuando un método es invocado dentro del contexto de un objeto. $this es una referencia al objeto invocador.

Advertencia

Llamar a un método no estático arroja estáticamente un Error. Antes de PHP 8.0.0, esto generaría un aviso de desaprobación obsoleta, y $this esto no estaría definido.

Ejemplo #2 Algunos ejemplos de la pseudovariable $this

<?php
class A
{
function
foo()
{
if (isset(
$this)) {
echo
'$this está definida (';
echo
get_class($this);
echo
")\n";
} else {
echo
"\$this no está definida.\n";
}
}
}

class
B
{
function
bar()
{
A::foo();
}
}

$a = new A();
$a->foo();

A::foo();

$b = new B();
$b->bar();

B::bar();
?>

Salida del ejemplo anterior en PHP 7:

$this está definida (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.

Output of the above example in PHP 8:

$this está definida (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

Readonly classes

A partir de PHP 8.2.0, una clase puede ser marcada con el modificador readonly. Marcar una clase como readonly añadirá el readonly modificador a cada propiedad declarada, y evitará la creación de propiedades dinámicas. Además, es imposible añadir soporte para ellas utilizando el atributo AllowDynamicProperties. Intentar hacerlo provocará un error en tiempo de compilación.

<?php
#[\AllowDynamicProperties]
readonly class
Foo {
}

// Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo
?>

Daado que ni las propiedades no tipadas ni las estáticas pueden ser marcadas con el modificador readonly, las clases readonly tampoco pueden ser declararlas:

<?php
readonly class Foo
{
public
$bar;
}

// Fatal error: Readonly property Foo::$bar must have type
?>
<?php
readonly class Foo
{
public static
int $bar;
}

// Fatal error: Readonly class Foo cannot declare static properties
?>

Una clase readonly puede ser extendida si, y solo si, la clase hija también es una clase readonly.

new

Para crear una instancia de una clase, se debe emplear la palabra reservada new. Un objeto se creará siempre a menos que el objeto tenga un constructor que arroje una excepción en caso de error. Las clases deberían ser definidas antes de la instanciación (y en algunos casos esto es un requerimiento).

Si se emplea un string que contenga el nombre de una clase con new, se creará una nueva instancia de esa clase. Si la clase estuviera en un espacio de nombres, se debe utilizar su nombre completo al realizar esto.

Nota:

Si no hay argumentos para pasar al constructor de la clase, se pueden omitir los paréntesis después del nombre de la clase.

Ejemplo #3 Creación de una instancia

<?php
$instancia
= new ClaseSencilla();

// Esto también se puede hacer con una variable:
$nombreClase = 'ClaseSencilla';
$instancia = new $nombreClase(); // new ClaseSencilla()
?>

En el contexto de una clase, es posible crear un nuevo objeto con new self y new parent.

Cuando se asigna una instancia ya creada de una clase a una nueva variable, ésta última accederá a la misma instancia que el objeto que le fue asignado. Esta conducta es la misma que cuando se pasan instancias a una función. Se puede realizar una copia de un objeto ya creado a través de la clonación del mismo.

Ejemplo #4 Asignación de objetos

<?php

$instancia
= new ClaseSencilla();

$asignada = $instancia;
$referencia =& $instancia;

$instancia->var = '$asignada tendrá este valor';

$instancia = null; // $instancia y $referencia son null

var_dump($instancia);
var_dump($referencia);
var_dump($asignada);
?>

El resultado del ejemplo sería:

NULL
NULL
object(ClaseSencilla)#1 (1) {
   ["var"]=>
     string(27) "$asignada tendrá este valor"
}

Existe un par de formas de crear instancias de un objecto:

Ejemplo #5 Creación de nuevos objetos

<?php
class Prueba
{
static public function
getNew()
{
return new static;
}
}

class
Hija extends Prueba
{}

$obj1 = new Prueba();
$obj2 = new $obj1;
var_dump($obj1 !== $obj2);

$obj3 = Prueba::getNew();
var_dump($obj3 instanceof Prueba);

$obj4 = Hija::getNew();
var_dump($obj4 instanceof Hija);
?>

El resultado del ejemplo sería:

bool(true)
bool(true)
bool(true)

Es posible acceder a un miembro de un objeto recién creado en una única expresión:

Ejemplo #6 Acceder a un miembro de un objeto recién creado

<?php
echo (new DateTime())->format('Y');
?>

El resultado del ejemplo sería algo similar a:

2016

Nota: Antes de PHP 7.1, los argumentos no se evalúan si no hay una función constructora definida.

Propiedades y métodos

Las propiedades y métodos de una clase viven en «espacios de nombres» diferentes, por tanto, es posible tener una propiedad y un método con el mismo nombre. Al hacer referencia tanto a una propiedad como a un método se utiliza la misma notación, y si se accederá a la propiedad o se llamará al método, solamente depende del contexto, es decir, si el empleo es el acceso a una variable o la llamada a una función.

Ejemplo #7 Acceso a propiedad contra llamada a método

<?php
class Foo
{
public
$bar = 'property';

public function
bar() {
return
'method';
}
}

$obj = new Foo();
echo
$obj->bar, PHP_EOL, $obj->bar(), PHP_EOL;

El resultado del ejemplo sería:

propiedad
método

Esto significa que llamar a una función anónima que ha sido asignada a una propiedad no es posible directamente. En su lugar, la propiedad ha de ser asignada primero a una variable, por ejemplo. A partir de PHP 7.0.0, es posible llamar a dicha propiedad directamente encerrándola entre paréntesis.

Ejemplo #8 Llamar a una función anónima almacenada en una propiedad

<?php
class Foo
{
public
$bar;

public function
__construct() {
$this->bar = function() {
return
42;
};
}
}

$obj = new Foo();

echo (
$obj->bar)(), PHP_EOL;

El resultado del ejemplo sería:

42

extends

Una clase puede heredar los métodos y propiedades de otra clase empleando la palabra reservada extends en la declaración de la clase. No es posible la extensión de múltiples clases; una clase sólo puede heredar de una clase base.

Los métodos y propiedades heredados pueden ser sobrescritos con la redeclaración de éstos utilizando el mismo nombre que en la clase madre. Sin embargo, si la clase madre definió un método como final, éste no podrá ser sobrescrito. Es posible acceder a los métodos sobrescritos o a las propiedades estáticas haciendo referencia a ellos con parent::.

Ejemplo #9 Herencia de clases sencilla

<?php
class ClaseExtendida extends ClaseSencilla
{
// Redefinición del método padre
function mostrarVar()
{
echo
"Clase extendida\n";
parent::mostrarVar();
}
}

$extendida = new ClaseExtendida();
$extendida->mostrarVar();
?>

El resultado del ejemplo sería:

Clase extendida
un valor predeterminado

Signature compatibility rules

When overriding a method, its signature must be compatible with the parent method. Otherwise, a fatal error is emitted, or, prior to PHP 8.0.0, an E_WARNING level error is generated. A signature is compatible if it respects the variance rules, makes a mandatory parameter optional, and if any new parameters are optional. This is known as the Liskov Substitution Principle, or LSP for short. The constructor, and private methods are exempt from these signature compatibility rules, and thus won't emit a fatal error in case of a signature mismatch.

Ejemplo #10 Compatible child methods

<?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);

El resultado del ejemplo sería:

Valid
Valid

The following examples demonstrate that a child method which removes a parameter, or makes an optional parameter mandatory, is not compatible with the parent method.

Ejemplo #11 Fatal error when a child method removes a parameter

<?php

class Base
{
public function
foo(int $a = 5) {
echo
"Valid\n";
}
}

class
Extend extends Base
{
function
foo()
{
parent::foo(1);
}
}

Output of the above example in PHP 8 is similar to:

Fatal error: Declaration of Extend::foo() must be compatible with Base::foo(int $a = 5) in /in/evtlq on line 13

Ejemplo #12 Fatal error when a child method makes an optional parameter mandatory

<?php

class Base
{
public function
foo(int $a = 5) {
echo
"Valid\n";
}
}

class
Extend extends Base
{
function
foo(int $a)
{
parent::foo($a);
}
}

Output of the above example in PHP 8 is similar to:

Fatal error: Declaration of Extend::foo(int $a) must be compatible with Base::foo(int $a = 5) in /in/qJXVC on line 13
Advertencia

Renaming a method's parameter in a child class is not a signature incompatibility. However, this is discouraged as it will result in a runtime Error if named arguments are used.

Ejemplo #13 Error when using named arguments and parameters were renamed in a child class

<?php

class A {
public function
test($foo, $bar) {}
}

class
B extends A {
public function
test($a, $b) {}
}

$obj = new B;

// Pass parameters according to A::test() contract
$obj->test(foo: "foo", bar: "bar"); // ERROR!

El resultado del ejemplo sería algo similar a:

Fatal error: Uncaught Error: Unknown named parameter $foo in /in/XaaeN:14
Stack trace:
#0 {main}
  thrown in /in/XaaeN on line 14

::class

La palabra reservada class es usada para la resolución de nombres de clases. Se puede obtener un string con el nombre completamente cualificado de la clase ClassName utilizando ClassName::class. Esto es particularmete útil con clases en espacios de nombres.

Ejemplo #14 Resolución de nombres de clases

<?php
namespace NS {
class
ClassName {
}

echo
ClassName::class;
}
?>

El resultado del ejemplo sería:

NS\ClassName

Nota:

La resolución de nombres de clases con ::class es una transformación durante la compilación. Esto significa que, en el instante de crear el string del nombre de la clase, aún no se ha realizado ninguna autocarga. Como consecuencia, los nombres de clases se expanden incluso si la clase no existe. No se emite ningún error en tal caso.

Ejemplo #15 Missing class name resolution

<?php
print Does\Not\Exist::class;
?>

El resultado del ejemplo sería:

Does\Not\Exist

As of PHP 8.0.0, the ::class constant may also be used on objects. This resolution happens at runtime, not compile time. Its effect is the same as calling get_class() on the object.

Ejemplo #16 Object name resolution

<?php
namespace NS {
class
ClassName {
}
}
$c = new ClassName();
print
$c::class;
?>

El resultado del ejemplo sería:

NS\ClassName

Nullsafe methods and properties

As of PHP 8.0.0, properties and methods may also be accessed with the "nullsafe" operator instead: ?->. The nullsafe operator works the same as property or method access as above, except that if the object being dereferenced is null then null will be returned rather than an exception thrown. If the dereference is part of a chain, the rest of the chain is skipped.

The effect is similar to wrapping each access in an is_null() check first, but more compact.

Ejemplo #17 Nullsafe Operator

<?php

// As of PHP 8.0.0, this line:
$result = $repository?->getUser(5)?->name;

// Is equivalent to the following code block:
if (is_null($repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (
is_null($user)) {
$result = null;
} else {
$result = $user->name;
}
}
?>

Nota:

The nullsafe operator is best used when null is considered a valid and expected possible value for a property or method return. For indicating an error, a thrown exception is preferable.