La programación orientada a objetos (OOP) en PHP es una forma de estructurar el código utilizando "objetos" que contienen tanto datos como métodos.
Definamos algunos términos clave antes de profundizar en el tema:
Clase
Es un plano para crear objetos. Aquí definimos las propiedades (variables) y métodos (funciones) que los objetos creados a partir de la clase tendrán. También se le conoce como "esquema" o "blueprint".
Respecto al término blueprint, significa proyecto original o modelo a seguir. De hecho, si has estudiado alguno de mis cursos de Laravel, habrás notado que cuando creo migraciones se usa un código de este estilo:
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
En el ejemplo se trabaja con Blueprint para definir la estructura de la tabla users. Esto no es casual, en este contexto, la palabra blueprint es un plano de construcción que detalla cómo se debe crearse una estructura. Es una excelente metáfora, podemos decir que las migraciones entonces son el blueprint de las tablas en la base de datos.
Todo esto nos acerca al mundo real, debido a que toda construcción comienza con un plano. Pero dicho plano no es el resultado final, sino la guía para llegar a él.
A mí me gusta llamar a las clases "moldes". Imagina el molde de un pastel, ese molde define la forma del pastel, pero no es el pastel en sí. Del mismo modo, una clase define la forma y el comportamiento, pero no es el objeto en sí.
Objeto
Si una clase es un molde de un pastel, un objeto es el pastel hecho con ese molde.
La definición técnica es que un objeto es una instancia de una clase. Incluso cuando creamos un objeto, decimos que hemos instanciado una clase.
La definición de instancia es: Caso o ejemplar de algo. En programación sería, una instancia es un ejemplar concreto de una clase, con sus propias propiedades y comportamientos.
Detalles de la sintaxis en PHP
Para crear una clase en PHP, usamos la palabra class. Aquí tienes un ejemplo simple de una clase llamada Cake con una propiedad y un método:
class Cake
{
public $flavor;
public function bake(): string
{
return "Baking a {$this->flavor} cake!";
}
}
Como puedes ver, la clase Cake tiene una propiedad $flavor y un método bake() que devuelve una cadena indicando el sabor del pastel que se está horneando. Su sintaxis es sencilla y clara. Empezamos con la palabra reservada class, seguida del nombre de la clase y un par de llaves que encierran el contenido de la clase.
Como reglas básicas para nombrar una clase en PHP:
- El nombre de la clase debe comenzar con una letra o un guion bajo (_).
- Puede contener letras, números y guiones bajos.
Aunque se recomienda usar la convención CamelCase, donde cada palabra comienza con una letra mayúscula y no se usan espacios ni guiones bajos (por ejemplo, MyClassName, AnotherClassName o como en nuestro ejemplo, Cake).
Dentro del método bake(), usamos la palabra $this para referirnos a la instancia actual del objeto. Gracias a ello alcanzamos la propiedad $flavor del objeto.
Veamos cómo podemos hacer uso de esta clase creando instancias (objetos):
$cake = new Cake();
$cake->flavor = 'chocolate';
echo $cake->bake(); // Imprime "Baking a chocolate cake!"
En este ejemplo, creamos una instancia de la clase Cake usando la palabra clave new. Luego, asignamos un valor a la propiedad $flavor del objeto y llamamos al método bake() para obtener el resultado.
Recuerda que la clase es el molde, y el objeto es el pastel hecho con ese molde. Así que como puedes imaginar, podemos crear múltiples objetos (pasteles) a partir de la misma clase (molde), cada uno con sus propias propiedades y comportamientos.
$cake2 = new Cake();
$cake2->flavor = 'vanilla';
echo $cake2->bake(); // Imprime "Baking a vanilla cake!"
Y como dato curioso, una propiedad y un método pueden tener el mismo nombre.
class Cake
{
public $flavor = 'vanilla';
// ...
public function flavor()
{
return 'Example method';
}
}
$cake = new Cake();
echo $cake->flavor; // Imprime vanilla
echo $cake->flavor(); // Imprime "Example method"
Esto, aunque se puede, no es recomendable porque puede generar confusión. Nota que la diferencia está en el uso de paréntesis (). Sin paréntesis accedemos a la propiedad, con paréntesis llamamos al método.
new
Como pudiste ver, la palabra new se utiliza para crear una nueva instancia de una clase de manera tradicional. Directamente lo hicimos con:
$cake = new Cake();
new self
Dentro de una clase, puedes crear una nueva instancia de la misma clase utilizando new self. Esto es útil cuando deseas crear un objeto de la misma clase desde un método de instancia.
class ParentClass
{
public static function create(): self
{
return new self();
}
}
class ChildClass extends ParentClass
{
// ...
}
$childInstance = ChildClass::create();
var_dump($childInstance instanceof ChildClass); // Imprime bool(false)
var_dump($childInstance instanceof ParentClass); // Imprime bool(true)
Gracias a la herencia, desde ChildClass podemos llamar al método estático create() de ParentClass, pero el objeto creado será de tipo ParentClass, no de ChildClass.
Es muy útil cuando queremos asegurarnos de que la instancia creada sea siempre de la clase en la que se define el método, sin importar desde dónde se llame.
Para ilustrarlo usé diferentes palabras clave, que acontinuación voy a definir:
static: En este caso, indica que el método pertenece a la clase en sí, no a una instancia específica de la clase. Por lo tanto, puedes llamar al método sin crear una instancia.instanceof: Es un operador que verifica si un objeto es una instancia de una clase específica o de una clase que hereda de ella. Como puedes ver, devuelvetrueofalse.var_dump(): Aunque ya lo hemos revisado en otros artículos, repasamos que es una función que muestra información estructurada sobre una variable. Incluye el tipo y valor.
new static
Esto es interesante, porque new static permite crear una instancia de la clase desde la cual se llama al método, incluso si el método está definido en una clase padre. Esto es especialmente útil en el contexto de la herencia.
Si uso el mismo ejemplo anterior, pero cambiando new self por new static, el resultado sería diferente:
class ParentClass
{
public static function create(): self
{
return new static();
}
}
class ChildClass extends ParentClass
{
// ...
}
$childInstance = ChildClass::create();
var_dump($childInstance instanceof ChildClass); // Imprime bool(true)
var_dump($childInstance instanceof ParentClass); // Imprime bool(true)
En este caso, cuando llamamos a ChildClass::create(), el objeto creado es de tipo ChildClass, no de ParentClass. Veamos un ejemplo práctico:
class Model
{
public static function create(): self
{
return new static();
// ...
}
}
class Post extends Model {}
class User extends Model {}
$post = Post::create();
$user = User::create();
echo get_class($post); // Imprime Post
echo get_class($user); // Imprime User
En este caso, tanto Post::create() como User::create() crean instancias de sus respectivas clases, gracias a new static. Para el ejemplo usé la función get_class(), que devuelve el nombre de la clase a la que pertenece un objeto. Con var_dump() también podríamos ver el mismo resultado pero el punto es variar y aprender más funciones útiles de PHP.
new parent
Esta palabra habla por sí sola, con new parent creamos una instancia de la clase padre desde una clase hija. Esto es útil cuando queremos acceder a la funcionalidad de la clase padre desde la clase hija.
class ParentClass
{
public function example()
{
return 'Example from ParentClass';
}
}
class ChildClass extends ParentClass
{
public function greetFromParent()
{
$parentInstance = new ParentClass();
return $parentInstance->example();
}
}
$child = new ChildClass();
echo $child->greetFromParent(); // Imprime "Example from ParentClass"
Resumen de new self, new static y new parent
El tema es entender que dentro del contexto de una clase, se pueden crear nuevas instancias utilizando diferentes palabras clave como new self, new static y new parent. Cada una tiene su propio propósito y comportamiento, especialmente en el contexto de la herencia.
- new self: Crea una instancia de la propia clase en la que se define el método.
- new static: Crea una instancia de la clase desde la cual se llama al método.
- new parent: Crea una instancia de la clase padre desde una clase hija.
Instanciar desde una variable
Esto es especialmente útil cuando el nombre de la clase se determina en tiempo de ejecución.
class Example
{
public function message()
{
return 'Example message';
}
}
$className = 'Example';
$instance = new $className();
echo $instance->message(); // Imprime "Example message"
Instancia en línea
Como su nombre lo dice, consiste en crear una instancia y llamar a un método en una sola línea de código:
class Example
{
public function message()
{
return 'Example message';
}
}
echo (new Example())->message(); // Imprime "Example message"
Conclusión
La instanciación de clases es un concepto fundamental.
Está muy relacionado con el concepto herencia, que veremos en próximos artículos. De momento, es importante entender cómo crear instancias utilizando diferentes formas como new, new self, new static y new parent. Esto te permitirá aprovechar al máximo las capacidades de OOP en tus proyectos. Además, técnicas como instanciar desde una variable o usar instancias en línea pueden hacer tu código más dinámico y conciso.