array ( 0 => 'index.php', 1 => 'PHP Manual', ), 'head' => array ( 0 => 'UTF-8', 1 => 'es', ), 'this' => array ( 0 => 'language.oop5.basic.php', 1 => 'Lo básico', ), 'up' => array ( 0 => 'language.oop5.php', 1 => 'Clases y objetos', ), 'prev' => array ( 0 => 'oop5.intro.php', 1 => 'Introducción', ), 'next' => array ( 0 => 'language.oop5.properties.php', 1 => 'Propiedades', ), 'alternatives' => array ( ), 'source' => array ( 'lang' => 'es', 'path' => 'language/oop5/basic.xml', ), ); $setup["toc"] = $TOC; $setup["toc_deprecated"] = $TOC_DEPRECATED; $setup["parents"] = $PARENTS; manual_setup($setup); ?>
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.
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
As of PHP 8.2.0, a class can be marked with the readonly modifier. Marking a class as readonly will add the readonly modifier to every declared property, and prevent the creation of dynamic properties. Moreover, it is impossible to add support for them by using the AllowDynamicProperties attribute. Attempting to do so will trigger a compile-time error.
<?php
#[AllowDynamicProperties]
readonly class Foo {
}
// Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo
?>
As neither untyped, nor static properties can be marked with the
readonly
modifier, readonly classes cannot declare
them either:
<?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
?>
A readonly class can be extended if, and only if, the child class is also a readonly class.
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.
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
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
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
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
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
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.