El lenguaje orientado a objetos
Introducción
En este capítulo veremos la noción más interesante de la programación en PHP.
No se preocupe: aunque la palabra objeto pueda sonar extraña, en realidad solo representa elementos de programación simples y lógicos diseñados para facilitarle la vida.
Sin más demora, sumerjámonos con alegría en el maravilloso mundo de la programación orientada a objetos.
Los objetos en programación
Antes de explicar por qué se habla de objetos, vamos a desvelar de manera sencilla qué significa esto en programación.
Anteriormente, exploramos la noción de variable, que sirve para almacenar información para su uso posterior.
También examinamos la noción de función, que permite utilizar el mismo código varias veces con diferentes parámetros.
Lógicamente, el siguiente paso consiste en tener un elemento que contenga tanto variables como funciones. Esto es lo que llamamos un objeto.
Un objeto es esencialmente una variable que, en lugar de contener un valor, contendrá variables y funciones. Nos referimos a esta variable como de tipo objeto.
Los objetos existen en la mayoría de los lenguajes de programación, y su creación puede variar de un lenguaje a otro. Sin embargo, en PHP, utilizamos un estándar de creación conocido como clase.
¿Para qué nos puede servir una clase? Los objetos tienden a compartir muchas características (variables) y usos (funciones). Para evitar redundancias, vamos a definir un modelo que nos permitirá crear los objetos.
Las clases
Una clase es un modelo que permite crear uno o varios objetos.
¿Cómo creamos una clase?
Utilizamos la palabra clave class.
<?php
// declaración de una clase
class NombreClase
{
public $primeraVariable=valor;
public $segundaVariable=valor;
...
primeraFuncion(parámetros...)
{
...
}
segundaFuncion(parámetros...)
{
...
}
}
Las { } de la clase encapsulan todas la variables y todas las funciones de la clase.
Es importante tener en cuenta el nombre de la clase: NombreClase. La primera palabra comienza con mayúscula, a diferencia de las variables y funciones. Usamos comúnmente la PascalCase en lugar de la camelCase. No es obligatorio, pero se recomienda.
También es crucial tener el modificador de acceso public antes de las variables. Este es el alcance de la variable y retomaremos este tema más adelante. Es obligatorio;...
Los objetos
Para crear un objeto a partir de una clase, utilizaremos la palabra clave new seguida del nombre de la clase. A esto se le llama crear una instancia de la clase o instanciar la clase.
La forma correcta en español no sería «instanciar», sino instar. No obstante, le insto a encontrar algún ejemplo de uso correcto en el mundo de la programación. En esta edición se usarán los dos términos indistintamente.
Es posible instar una clase tantas veces como queramos.
Sintaxis:
$miObjeto=new nombreClase;
Cuando el objeto ya ha sido instanciado, podemos acceder a sus variables y funciones con la instrucción ->:
$miObjeto->miVariable; // para acceder a la variable miVariable
$miObjeto->miFuncion(); // para acceder a la función miFuncion
Preste atención al hecho de que no se pone $ delante del nombre de la variable.
Vamos a instanciar la clase Producto para crear un objeto al que llamaremos impresora:
<?php
// Aquí, la declaración de la clase
...
// Creación de la impresora como instancia del objeto Producto
$impresora=new Producto;
// Asignamos valores las variables
$impresora->nombre="impresora";
$impresora->precio=700;
$impresora->cantidad=20;
$impresora->agotado=false;
// Visualización...
Las propiedades y los métodos de un objeto
Para distinguir las variables y las funciones de una clase de las variables y de las funciones habituales, utilizamos un vocabulario diferente:
-
Las variables de una clase se llaman propiedades de la clase (también se les llama atributos en ocasiones).
-
Las funciones de una clase se llaman métodos de la clase.
Este será el vocabulario utilizado a lo largo del libro.
La razón de la programación orientada a objetos
¿Por qué se denomina a este nuevo tipo de variable «objetos»?
Los seres humanos tienden a hacer analogías entre el lenguaje de programación y la vida real. Existe una analogía entre la definición de nuestros objetos, tal como los acabamos de describir en el código, y la de los objetos en la vida real.
Retomemos el ejemplo anterior:
La clase «Producto» describe un producto real disponible en una tienda.
Este producto tiene ciertas características que lo definen: su nombre, su precio, la cantidad disponible, etc. Estas son las propiedades del objeto. Las propiedades se suelen asociar a un sustantivo que define una cualidad del objeto.
Podríamos tomar el ejemplo de un automóvil cuyas propiedades serían la marca, el color y el número de puertas.
Los objetos también tienen ciertas capacidades: podemos arrancar el automóvil, acelerar, hacer que disminuya la velocidad y detenerlo. Estos son los «métodos» del objeto. Los métodos se suelen asociar a un verbo que describe una facultad del objeto.
En nuestro ejemplo, «Producto» tiene tres métodos. Es posible agregar un producto, eliminar un producto y mostrar las propiedades de un producto.
Las propiedades de un objeto se asemejan a las características físicas de un objeto en...
El objeto $this
A menudo es útil en un método de una clase poder usar las propiedades de esa clase.
Del mismo modo, podría ser útil llamar a un método de una clase desde otro método de la misma clase.
Por desgracia, no es posible llamar directamente a estos elementos por su nombre. No son variables ni funciones ordinarias.
Es necesario pasar por el objeto instanciado para acceder a sus propiedades o métodos, utilizando el operador - >.
Pero en la declaración de la clase, no conocemos el nombre del objeto que se instanciará.
Por lo tanto, dentro de la clase, necesitamos poder hacer referencia al objeto sobre el que estamos trabajando para poder utilizar sus propiedades y métodos.
Es por eso por lo que se creó el objeto $this.
$this representa por defecto el objeto que instancia la clase. Así, podemos, en la declaración de la clase, llamar a las propiedades y métodos del objeto que instanciará la clase.
La sintaxis es la siguiente:
<?php
// declaración de una clase
class NombreClase
{
public $primeraVariable=valor;
public $segundaVariable=valor;
primeraFuncion(parámetros...)
{
// cómo acceder a una propiedad de la clase
$this->primeraVariable ...
// cómo acceder...
Los métodos mágicos
¡De hecho, hay magia en la programación orientada a objetos!
Los métodos se llaman «mágicos» cuando se ejecutan sin que se les llame directamente.
Sí, pero ¿cuándo sucede esto?
Todo depende del método mágico en cuestión, ya que cada uno tiene un papel predefinido y se ejecuta cuando ocurre un evento específico, como la instanciación de una clase, la visualización de un objeto o el acceso a una propiedad en lectura o escritura...
Aquí encontraremos todos los métodos mágicos de PHP: https://www.php.net/manual/es/language.oop5.magic.php
Veamos algunos de los métodos mágicos más utilizados.
1. El método __toString()
Este método se activa tan pronto como el objeto es tratado como una cadena de caracteres. Es necesario definir este método en la clase.
Utilizamos __ (doble guion bajo) delante de cada método mágico.
Ejemplo: tenemos el objeto $impresora y queremos visualizar nuestro objeto: echo $impresora;
La instrucción echo no podrá mostrar el contenido de un objeto, si la clase no contiene el método mágico__toString().
Para poder hacer un echo $impresora, bastará con remplazar nuestro antiguo método visualizar() por __toString() :
<?php
class Producto
{
public $nombre="mi Producto";
public $cantidad=3;
public $precio=120;
public $agotado=false;
function __toString()
{
return "Nombre: ".$this->nombre.'<br>'.
"Precio: ".$this->precio.'<br>'.
"Cantidad: ".$this->cantidad.'<br>'.
(($this->agotado)?"Producto agotado<br>":
"Producto disponible<br>"); ...
Los espacios de nombres
Por ahora, la clase utilizada está definida dentro del script que la utiliza.
Sin embargo, es más conveniente extraer esta clase y guardarla en un archivo separado, un archivo que solo contendrá esa clase. Esto contribuye a la modularidad de la aplicación, haciéndola más fácil de mantener y probar. Cada clase es independiente de las demás.
El estándar recomendado es colocar cada clase en un archivo con el mismo nombre que la clase, con la extensión .php (respetando mayúsculas y minúsculas).
Ejemplo
La clase Producto se guardará en un archivo llamado Producto.php.
Luego puede incluir este archivo en el script principal con la instrucción include.
Sintaxis:
<?php
include NombreClasse.php
Ejemplo
Guarde la clase Producto en el archivo Producto.php:
Cree un nuevo archivo con VSCode.
En el archivo index.php, seleccione solo el código de la clase, cópielo ([Ctrl] C), vuelva al nuevo archivo creado y péguelo ([Ctrl] V). No olvide incluir <?php en la primera línea. Finalmente, guarde el nuevo archivo ([Ctrl] S) con el nombre Producto.php.
<?php
// Descripción de la clase Producto. ¿Para qué sirve esta clase?
// ¿Qué objetos representa? Describir utilidad funcional
class Producto
{
private $nombre="mi Producto";
private $cantidad=3;
private $precio=120;
private $agotado=false;
function __construct($nombre,$cantidad,$precio,$agotado=false)
{
$this->nombre=$nombre;
$this->cantidad=$cantidad;
$this->precio=$precio;
$this->agotado=$agotado;
}
function getNombre()
{
return $this->nombre;
} ...
Herencia en clases
Algunas clases pueden tener elementos en común. En la lógica de evitar duplicar código innecesariamente, surgió la noción de herencia entre clases.
La herencia permite que una clase posea todas las propiedades y métodos de otra clase, al tiempo que tiene sus propios elementos adicionales.
La herencia se define con la palabra clave extends.
Sintaxis:
Class ClaseHija extends ClaseMadre
{
...
}
Tomemos el ejemplo de la clase Producto. Imaginemos un Producto que se vende en lotes. Este Producto es un poco diferente del Producto estándar. Cuando se ordena un Producto por lote, se ordena de una sola vez varias unidades del mismo Producto.
Por lo tanto, tiene sentido crear una nueva clase ProductoPorLotes que herede de la clase Producto, pero que contenga una propiedad adicional: el número de artículos por lote ($numArticulosPorLote).
Podemos crear esta clase en el subdirectorio MisProductos, al igual que la clase Producto. Cree el archivo MisProductos/ProductoPorLotes e inserte el siguiente código:
<?php
namespace MisProductos;
class ProductoPorLotes extends Producto
{
private $numArticulosPorLote;
function getNumArticulosPorLote()
{
return...
La firma de un método
La firma de un método describe las entradas y salidas del método.
En otras palabras, la firma indica el número de parámetros de entrada y el tipo de los parámetros (string, integer, array, objeto, etc.).
Es posible, desde PHP7, especificar el tipo de los parámetros de entrada y también el tipo del método de salida (tipo del valor de retorno).
Sintaxis:
class NombreClase
{
...
miPrimeraFuncion(type parámetro, type parámetro…):type
{
...
}
}
El type antes de cada parámetro indica el tipo del parámetro y el :type detrás de la declaración del método es el tipo del retorno del método (si el método no devuelve un valor, podemos asignarle un tipo vacío :void o simplemente omitirlo).
Agregar tipos permite un mejor control del uso de los métodos. Por lo tanto, se recomienda hacerlo.
Por ejemplo, en la clase Producto, podemos definir el tipo de los parámetros del constructor.
function __construct(string $nombre, int $cantidad, float $precio,
...
Redefinición de un método
Volviendo a la redefinición de los métodos.
Para los métodos distintos al constructor, el método de la clase hija que redefine el método de la clase madre debe tener la misma firma.
En nuestro ejemplo, es posible redefinir los métodos anadirProducto() y suprimirProducto() en la clase ProductoPorLotes para tener en cuenta la propiedad $numArticulosPorLote en el cálculo:
class ProductoPorLotes extends Producto
{
private $numArticulosPorLote;
function anadirProducto()
{
$this->cantidad+=$this->numArticulosPorLote;
if($this->cantidad>0) $this->agotado=false;
}
function suprimirProducto()
{
$this->cantidad-=$this->numArticulosPorLote;
if($this->cantidad<=0){
$this->cantidad=0;
$this->agotado=true; ...
Visibilidad de los elementos en las clases hijas
Existe un modificador de visibilidad o alcance que permite acceder a los elementos de la clase madre desde la clases hijas, sin permitir el acceso a otras clases que no hereden de la clase madre. Este modificador es protected.
Sintaxis:
class NombreClase
{
protected $primeraVariable=valor;
protected $segundaVariable=valor;
protected primerMetodo()
{
...
}
En la clase Producto, por ejemplo, es preferible definir las variables como protected, para poder reutilizarlas en los métodos de la clase ProductoPorLotes.
Estas son las modificaciones para la clase Producto:
<?php
namespace MisProductos;
class Producto
{
protected $nombre="Mi Producto";
protected $cantidad=3;
protected $precio=120;
protected $agotado=false;
...
}
En esta ocasión, el script ya no provoca ningún error:
![images/04RIT04.png](https://www.eni-training.com/download/3344c6bd-83e8-47fb-bff9-51328b4bd484/images/04RIT04.png?id=AAEAAAD%2f%2f%2f%2f%2fAQAAAAAAAAAMAgAAAE1FbmkuRWRpdGlvbnMuTWVkaWFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VuaS5FZGl0aW9ucy5NZWRpYXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAAwdGVzdCB2Ml84IC0gZmZhNjQzOWQtZDZjYi00NzYxLWJhYTMtZWZkZDNiZDcwMWM25rSq0F7p2IgL)
Redefinición del constructor de la clase madre
El constructor funciona de manera diferente, ya que es posible redefinir un constructor que no tiene la misma firma en la clase hija.
Por ejemplo, en la clase ProductoPorLotes, agregaremos el parámetro $numArticulosPorLote para definir este valor al crear el objeto. Este parámetro no existe en la definición del constructor de la clase madre Producto.
El constructor de la clase hija tiene prioridad. Pero también queremos llamar al constructor de la clase madre.
Para evitar duplicar el código del constructor de la clase madre en el de la clase hija, llamamos a este constructor utilizando el operador :: y la palabra clave parent.
Sintaxis:
class nombreClaseHija
{
function __construct(parámetros de la clase hija)
{
...
// llamada al constructor de la clase madre
parent::construct(parámetros de la clase madre)
}
}
En el ejemplo, podemos definir el constructor dentro de la clase ProductoPorLotes:
<?php
namespace MisProductos;
class ProductoPorLotes extends Producto
{
private $numArticulosPorLote;
function __construct(string $nombre, int $cantidad, int $numArticulosPorLote, float $precio, bool $agotado=false)
{
$this->numArticulosPorLote=$numArticulosPorLote;
parent::__construct($nombre,$cantidad,$precio,$agotado);
}
function anadirProducto()
{ ...
Las constantes y las variables «static»
Hay dos tipos adicionales de variables que se pueden incluir en una clase: las constantes y las variables «static».
1. Las constantes
Las constantes son elementos cuyo valor se define solo una vez y no se puede cambiar durante la ejecución del script.
Las constantes se utilizan para definir elementos de configuración fijos en la aplicación.
La convención es escribir las constantes en mayúsculas, separadas por el carácter _ (norma conocida como snake_case).
Sintaxis:
class MiClase
{
const MI_CONSTANTE=valor;
}
Estas constantes se pueden utilizar en los métodos de la clase usando la palabra clave self y el operador de visibilidad ::.
Es importante distinguir entre self y $this :
-
self hace referencia a la clase en la que estamos trabajando.
-
$this hace referencia al objeto instanciado actualmente.
Esta distinción es muy importante.
Independientemente del objeto que instancia la clase, el elemento self::CONSTANTE hace referencia al mismo valor incluido en la propia clase.
$this hace referencia a variables intrínsecas al objeto. Estas variables pueden tener valores diferentes según los objetos, a diferencia de self, ya que nos encontramos en la clase común.
Sintaxis del operador self:
class MiClase
{
const...
Las clases abstractas y las interfaces
No profundizaremos más en el estudio del lenguaje orientado a objetos (aunque aún hay mucho que decir), pero hay una última noción que es importante, especialmente porque se utiliza en Symfony: la noción de clase abstracta e interfaz.
1. Las clases abstractas
Todo se deriva de la herencia de clase; la idea es tener clases madre que solo sirvan para la herencia.
Una clase abstracta es una clase que no se puede instanciar. No se pueden crear objetos de esa clase.
Se define con la palabra clave abstract.
Una clase abstracta puede contener métodos abstractos. Los métodos abstractos son métodos que es obligatorio redefinir en la clase o las clases hijas.
Estos métodos solo contienen firmas, no tienen contenido.
Sintaxis de una clase abstracta:
abstract class MiClase
{
// puede contener tanto propiedades como métodos
abstract public function miFuncion(parámetros);
}
Un método solo puede ser abstracto si la clase es abstracta.
En nuestro ejemplo, la clase Producto puede derivar de una clase abstracta que llamaremos ProductoAbstract, que agrupará la estructura general de la clase Producto.
Creemos una subcarpeta de MisProductos que llamaremos MisProductosWith Abstract.
Luego, creemos un archivo ProductoAbstract.php en esta subcarpeta y coloquemos la clase abstracta en MisProductos/MisProductosWithAbstract:
<?php
namespace MisProductos\MisProductosWithAbstract;
abstract class ProductoAbstract
{
const MONEDA="euros";
static $numProductos;
protected $nombre;
protected $cantidad;
protected $precio;
protected $agotado;
abstract function __toString();
abstract function anadirProducto();
abstract function suprimirProducto();
}
Una clase abstracta permite asegurar la estructura de las clases que heredan de ella. Es una buena manera de diseñar una estructura básica desde el principio...
Conclusión
Ahora tiene todos los conocimientos esenciales para desarrollar en PHP y, sobre todo, utilizar el framework Symfony. Por supuesto, hay otras nociones en PHP que no se han abordado para no sobrecargar este libro. Si desea profundizar en la programación en PHP, le recomendamos que visite el sitio: PHP the right way, que detalla de manera clara y precisa todas las nociones de PHP: https://www.phptherightway.com