La principal forma de definir objetos en PHP es mediante unas estructuras especiales llamadas clases.
Las clases son los planos donde se detallan las características de los objetos, y con solo definir una unica clase se pueden generar muchos objetos, por lo que son muy útiles para reutilizar código.
Generar objetos a partir de clases se conoce como instanciar y para llevarlo a cabo se usa la palabra clave de PHP new
class NombreDeLaClase{
}
$object = new NombreDeLaClase;
Para entender como es que esto funciona tenemos que ilustrarlo con código que resuelva problemas reales.
Pensemos por ejemplo en un producto de una tienda en línea, este producto debería contar con los siguientes datos:
- Titulo
- Precio
- Existencias
- Descripción
Creemos una clase que represente esto:
class Product
{
private string $title = 'Pasta dental';
private string $description = 'Producto de higiene bucal';
private float $price = 39.9;
private int $stock = 4;
}
$Product = new Product;
Ahora tenemos una clase producto que agrupa y oculta sus datos, simplemente definimos una variable para cada dato requerido y declaramos el tipo de dato.
Antes de cada variable usamos la palabra private que es el indicativo de que el dato debe estar oculto y asi evitar que pueda ser accedido desde el exterior del objeto.
Podríamos haber definido un arreglo asociativo con estos datos, pero ¿como evitas que los datos sean modificados por tus algoritmos o por otros módulos?, ó ¿como evitas que los valores que sí van a ser modificados no se corrompan?
En el caso de un producto de una plataforma de comercio, el titulo y la descripción son valores que no suelen cambiar durante la ejecución del programa.
Por otro lado el precio es un valor que podría variar si se aplican impuestos o descuentos pero tanto el precio como las existencias son valores que de ser modificados tendrían que seguir algún criterio (como el de nunca poder ser valores negativos por ejemplo).
Para resolver estas cuestiones crearemos las funciones que definirán el comportamiento interno del producto, pero antes necesitamos aclarar ciertos detalles técnicos.
Propiedades, Métodos y su Visibilidad/Ocultamiento
Las propiedades de un objeto es como se conoce a las variables que definimos dentro de una clase para contener los valores principales de un objeto.
Los métodos de una objeto son las funciones que definiremos dentro de una clase para programar el comportamiento de dichos objetos y se declaran usando el formato que ya vimos para declarar funciones en PHP.
En conjunto las propiedades y métodos de un objeto es lo que se conoce como miembros de un objeto.
Y para controlar el nivel de ocultamiento de los miembros de un objeto se usan los modificadores de visibilidad Public y Private y con ellos se controla que puede o no ser accedido desde fuera del objeto.
Lo mas recomendable es por defecto definir todos los miembros como ocultos y solo definir como visibles desde el exterior a ciertos miembros en particular.
Estos miembros accesibles desde el exterior representan la interfaz del objeto (la superficie de contacto), y son el medio que usarán otros objetos y el resto del software para interactuar con ellos.
Encapsulamiento
Los miembros de un objeto están aislados del resto de cosas del programa, pero no están aislados entre si, ya que forman parte de la misma unidad estos pueden interactuar entre sí.
A esta característica de los objetos de permitir que sus elementos internos puedan referenciarse entre ellos se le llama encapsulamiento.
Para poder hacer referencia a las propiedades o métodos de un objeto desde dentro del mismo se usa una pseudovariable llamada $this, la cual es un comodín que hace referencia al propio objeto.
Este comodín se usa en conjunto con el operador de objeto -> (Operador flecha simple, el cual se construye con un "guión medio" y el símbolo "mayor que") para realizar esta interacción entre los miembros de un objeto $this->.
Acceso a las Propiedades del objeto vía Getters
Los Getters son funciones pensados para permitir la lectura de los valores internos de un objeto de forma controlada, se suelen llamar con el nombre de la propiedad que se desea leer pero precedida por el prefijo get, por ejemplo getTitle(), getPrice(), getStock(), etc.
Poniendo en practica lo aprendido
Vamos a modificar el código de la clase producto y añadiremos los mecanismos para poder leer los datos internos del producto y al final del código vamos a llamarlos para comprobar que realmente estamos accediendo a la información correcta.
class Product
{
private string $title = 'Pasta dental';
private string $description = 'Producto de higiene bucal';
private float $price = 39.9;
private int $stock = 4;
public function getTitle()
{
return $this->title;
}
public function getDescription()
{
return $this->description;
}
public function getPrice()
{
return $this->price;
}
public function getStock()
{
return $this->stock;
}
}
$Product = new Product;
echo '<div>
<h4>'.$Product->getTitle().'</h4>
<p>'.$Product->getDescription().'</p>
<p>Precio: $'.$Product->getPrice().'</p>
<p>Existencias: '.$Product->getStock().'</p>
</div>';
Este código PHP generará el siguiente código HTML:
<div>
<h4>Pasta dental</h4>
<p>Producto de higiene bucal</p>
<p>Precio: $39.9</p>
<p>Existencias: 4</p>
</div>
El cual se verá de la siguiente manera en el navegador:
Pasta dental
Producto de higiene bucal
Precio: $39.9
Existencias: 4
Hemos definido una función getter para cada propiedad de nuestro objeto y hemos dejado estos métodos como públicos para permitir su acceso fuera del objeto.
Dentro de cada getter usamos la referencia al objeto contenedor y el operador flecha $this-> para poder acceder a cada una de las propiedades.
Con todo esto logramos encapsular los datos de un producto de forma que no haya forma de modificar sus valores, solo tenemos mecanismos para poder leer dichos datos.
Usando solo arreglos, constantes y funciones podemos emular todos estos comportamientos y conceptos, pero definitivamente no es sencillo, y el código resultante no sería tan compacto como el que nos permite generar la Programación Orientada a Objetos.
Aunque un problema del código que llevamos escrito hasta aquí es que solo esta definido para un único producto, pero otra de las razones por las que usamos POO es por la facilidad que nos da para reutilizar código, por lo que ahora veremos como generar multiples objetos con el mismo código.
Constructores de objetos
Dentro de cada clase podemos definir un método especial conocido como constructor, y este nos permite proporcionarle a la clase los valores base con los que se creara cada objeto.
Este método especial no puede tener cualquier nombre, en concreto en PHP debe ser una función con el nombre __construct y solo puede llevar el modificador de visibilidad public.
A nuestro constructor podemos definirle argumentos de función como a cualquier otra función convencional
Al estar dicho método especial encapsulado en una clase, podemos interactuar con el resto de miembros del objeto, por lo que tomaremos los argumentos de la función y directamente los asignaremos a propiedades del objeto.
De esta forma podemos usar la misma clase para crear multiples objetos con valores diferentes pero reutilizando el mismo plano, permitiendo de forma elegante reutilizar grandes cantidades de código para crear estructuras similares.
Modifiquemos nuestra clase Producto para añadir un constructor con un argumento por cada propiedad del objeto.
class Product
{
private string $title;
private string $description;
private float $price;
private int $stock;
public function __construct(
string $title,
string $description,
float $price,
int $stock,
)
{
$this->title = $title;
$this->description = $description;
$this->price = $price;
$this->stock = $stock;
}
public function getTitle()
{
return $this->title;
}
public function getDescription()
{
return $this->description;
}
public function getPrice()
{
return $this->price;
}
public function getStock()
{
return $this->stock;
}
}
Para usar la clase emplearemos ahora una lista de productos, los cuales podrían ser leídos de alguna base de datos o proporcionados por una interfaz de usuario.
Luego esa lista de productos la recorreremos con un bucle foreach y crearemos un objeto por cada producto.
$items = [
[
'title' => 'Pasta dental',
'description' => 'Producto de higiene bucal',
'price' => 39.9,
'stock' => 4,
],
[
'title' => 'Crema corporal humectante',
'description' => 'Producto dermatológico',
'price' => 50.2
'stock' => 6,
],
[
'title' => 'Protector Solar 50+fps',
'description' => 'Protector solar facial',
'price' => 50.2
'stock' => 10,
],
[
'title' => 'Aspirina',
'description' => 'Tableta ácido acetilsalicílico',
'price' => 57
'stock' => 12,
],
[
'title' => 'Enjuague bucal',
'description' => 'Producto de higiene bucal',
'price' => 57
'stock' => 12,
],
];
$ProductList = [];
foreach($items as $item){
$ProductList[] = new Product(
$item['title'],
$item['description'],
$item['price'],
$item['stock'],
);
}
Con esto ya tendríamos un listado de objetos, que de momento solo es un conjunto de datos donde todos comparten el mismo comportamiento, lo cual de momento puede parecer mucho código para lo que realmente sirve.
Para cambiar un poco esto y empezar a tener noción del potencial de la POO modifiquemos nuestro ejemplo añadiendo más lógica para una situación común.
Para actualizar el comportamiento de todos nuestros objetos tipo producto solo hace falta modificar una sola pieza del software, que en este caso es la clase Product.
Si tuviésemos que aplicar un impuesto al precio de los productos en la misma clase podríamos definir en una constante de clase cual es el impuesto fijo a aplicar por ejemplo, o si necesitáramos un método para imprimir la lista de productos en cierto formato
class Product
{
const
private string $title;
private string $description;
private float $price;
private int $stock;
public function __construct(
string $title,
string $description,
float $price,
int $stock,
)
{
$this->title = $title;
$this->description = $description;
$this->price = $price;
$this->stock = $stock;
}
public function getTitle()
{
return $this->title;
}
public function getDescription()
{
return $this->description;
}
public function getPrice()
{
return $this->price;
}
public function getStock()
{
return $this->stock;
}
}
En resumen
Con esto ya tenemos la sintaxis básica para trabajar con objetos en PHP, ya que ahora conocemos las clases, que son los planos que definen y permiten crear objetos y como se definen las propiedades y los métodos dentro de ellas.