Desarrollo

Definición y utilización de objetos en PHP

Desarrollo PHP

Después de la introducción a la POO, vamos a ver la definición y utilización de objetos ya utilizando directamente PHP. Veremos cómo definir de forma sencilla un objeto, cómo definir sus atributos y funciones junto a la visibilidad de los mismos y cómo utilizarlos posteriormente.

La clase

Pese a que hablamos siempre de programación orientada a objetos, escucharéis más el concepto de clase que el de objeto. Esto es así porque, en la definición de objetos, la palabra reservada que se utilizará para definirlo es class. La definición inicial es parecida a la definición de las funciones en su cabecera inicial, sin embargo después de ella encontraremos las definiciones de propiedades y métodos que pertenecen a esa clase.

<?php
class Asignatura {
    
}
?>

También al igual que en las funciones, el nombre de la clase no puede ser una palabra reservada y debe comenzar con una letra o guión bajo, seguido de letras, números o guiones bajos.

Los atributos de la clase

Para la utilización de objetos necesitamos, obviamente, que la clase tenga unos atributos o propiedades, los cuales, como comentamos en la introducción, serán los valores que definen a cada objeto. Para definir un atributo en nuestra clase, se le debe anteponer una de las palabras reservadas public, protected o private. Posteriormente explicaremos qué significa cada una de estas declaraciones, pero ahora simplemente vamos a definir unos atributos de la siguiente forma:

<?php
class Asignatura {
	public $nombre;
	public $horario;
	public $curso;

	protected $profesor;

	private $alumnos = array();
}
?>

Hemos definido cinco atributos que, como veis, se escriben con la misma nomenclatura de variables (pues podemos decir que son variables ligadas a esa clase) pero anteponiendo delante de ellas alguna de las tres propiedades que hemos indicado y que nos ayudarán en la utilización de objetos.

También podemos observar cómo podemos definir también un valor por defecto para los atributos, como se demuestra en el caso de $alumnos, al que por defecto le hemos indicado que va a ser un array y que, inicialmente, estará vacío.

Las funciones de la clase

No hay mucha diferencia entre definir una función como ya las hemos visto a definirla dentro de una clase. El concepto es el mismo pero tiene algunos detalles nuevos que veremos a continuación.

En primer lugar, las funciones también deben estar definidas como public, private o protected como hemos indicado en los atributos. Otro detalle que tendremos que tener en cuenta en esta ocasión es cómo acceder desde una función a los atributos que hemos definido dentro de la clase. Para ello utilizamos la pseudovariable reservada $this.

La pseudovariable $this

Esta variable es única y está disponible cuando una función se define dentro del contexto de un objeto. Para la utilización de objetos el uso de $this es completamente necesario y habitual, pues esta variable hace referencia al propio objeto desde el que se le invoca, usualmente el mismo en el que se está definiendo. Pese a que hay algunos otros usos, están quedando obsoletos, por lo que podemos quedarnos con que $this sólo vamos a utilizarla dentro de la clase que estamos definiendo.

En otras palabras, $this es el propio objeto que estamos definiendo. Hace referencia a él mismo y se utiliza para acceder a sus propios atributos o funciones. Dentro de la clase que hemos definido anteriormente, no podemos acceder libremente a $nombre, pues nos generará un error de que dicha variable no está definida (o quizá no si $nombre también es una variable global definida fuera de la clase, pero no es el caso). Por ello, $this se utilizará aquí para “apuntar” a la propia clase y obtener así atributos y funciones.

Definimos las funciones de la clase

Explicado lo anterior, vamos ahora a definir unas pocas funciones de la clase que hemos definido inicialmente de la siguiente forma:

<?php

class Asignatura {
	public $nombre;
	public $horario;
	public $curso;

	protected $profesor;

	private $alumnos = array();

	public function consultarProfesor() {
		if (is_null($this->profesor)) {
			return 'No hay un/a profesor/a asignado/a a la asignatura ' . $this->nombre . '<br>';
		}

		return 'El/La profesor/a de la asignatura ' . $this->nombre . ' es ' . $this->profesor . '<br>';
	}

	protected function cambiarProfesor($profesor) {
		$this->profesor = $profesor;
	}

	public function insertarProfesor($profesor) {
		$profesoresPermitidos = array('Pedro', 'Sandra', 'Juan', 'Alicia');

		if (in_array($profesor, $profesoresPermitidos)) {
			$this->cambiarProfesor($profesor);

			return true;
		}

		return false;
	}

	public function listarAlumnos() {
		if (empty($this->alumnos)) {
			echo 'No hay alumnos matriculados en ' . $this->nombre . '<br>';
		} else {
			foreach ($this->alumnos as $alumno) {
				echo '- ' . $alumno . '<br>';
			}
		}
	}

	private function insertarAlumno($alumno) {
		array_push($this->alumnos, $alumno);
	}

	public function incorporarAlumnos($alumnos) {
		$alumnosPermitidos = array('Nerea', 'Manuel', 'Aitana', 'Jesús');

		foreach ($alumnos as $alumno) {
			if (in_array($alumno, $alumnosPermitidos)) {
				$this->insertarAlumno($alumno);
			}
		}
	}
}
?>

Tenemos, por tanto, nuestro objeto definido, nuestra clase creada. Las funciones que hemos creado tienen alguna incoherencia en el sentido de que podrían hacer lo que hacen algo mejor o son repetitivas, pero están hechas así a propósito para ver todos los posibles casos.

¿Qué hace cada función?

En primer lugar, vemos una función llamada consultarProfesor que devuelve un string con un texto informativo. Dentro de esta función se está utilizando por primera vez la pseudovariable $this que, como vemos, se utiliza junto al símbolo -> para acceder al atributo que queremos. En este caso, se comprueba si el profesor no es nulo y, si lo es, se devuelve un string indicando este hecho. Pero si no lo es, se devuelve un string indicando el nombre del profesor.

A continuación tenemos una función que hemos indicada como protected y que permite cambiar el profesor de la asignatura. Veremos después por qué la hemos indicado como protected. Seguidamente tenemos la función insertarProfesor. En ella hemos definido un array con los profesores que pueden dar la asignatura y, después, comprobamos si el profesor que se nos indica está entre los permitidos. Si lo está, procedemos a cambiarlo con la función cambiarProfesor definida anteriormente. Si no lo está, lo indicamos devolviendo false.

En cuanto a los alumnos, se ha creado una función listarAlumnos para que muestre un listado de alumnos o indique que no hay alumnos, en el caso de estar vacío el array. Finalmente, tenemos dos funciones. Una que hemos marcado como private para insertar el alumno utilizando la función predefinida array_push, y otra función que define los alumnos que pueden ser insertados y comprueba si se pueden insertar todos los alumnos indicados en el array $alumnos que recibe la función.

Cuidado, no hay que confundir este array $alumnos con la variable $alumnos definida en la clase y a la que se accedería con $this delante, siendo este un buen ejemplo para ver la diferencia. Es clave tenerlo en cuenta para aprender bien la utilización de objetos.

Visibilidad de atributos y funciones

En la utilización de objetos hemos comentado anteriormente que utilizamos las propiedades public, protected y private como ya se ha visto. Básicamente, podemos resumirlo de la siguiente forma:

  • public permite que se pueda acceder a los atributos y funciones indicados desde cualquier punto de nuestro programa, siempre que tengamos un objeto de la clase ya creado. Es decir, fuera de la clase podremos acceder a estos atributos y funciones sin ningún problema.
  • protected es más restrictivo, pues sólo permitirá acceder a sus atributos y funciones desde dentro de la misma clase o desde clases heredadas. El concepto de herencia lo veremos en unos pocos artículos, pero por ahora podemos quedarnos en que desde fuera de la clase no vamos a poder acceder a lo que hayamos definido como protected.
  • private es la más restrictiva de todas y podemos deducir ya que sólo nos va a permitir acceder a los atributos y funciones marcados como tal desde dentro de la propia clase única y exclusivamente. En este caso, las clases heredadas no podrán acceder, por tanto será algo que permitamos únicamente desde la propia clase.

Definiciones y costumbres habituales

No es una norma, pero sí es habitual que absolutamente todos los atributos se definan como private. ¿Por qué? Normalmente para un uso interno dentro de la clase y para impedir el uso externo. Vamos a poner un sencillo ejemplo simple de utilización de objetos.

Imaginad que tenemos una clase con los datos personales de una persona que tiene un campo que es la cuenta bancaria. Las cuentas bancarias tienen una serie de normas, como por ejemplo la que hablamos del IBAN en el artículo sobre utilidades para validar datos. Pensad qué pasaría si la definición del atributo $cuentaBancaria tuviera la propiedad public. Desde fuera de la clase se podría modificar esta cuenta bancaria y, por ejemplo, poner algo así como “Esta cuenta bancaria es una broma”. Como $cuentaBancaria es public, cogería ese string y lo colocaría como la cuenta sin ningún problema.

Por tanto, coloquemos ahora la propiedad private en esa variable. Ahora, si desde fuera de la clase se coloca ese string, va a haber un error y no se podrá cambiar la cuenta bancaria. Lo conveniente aquí sería crear una función cambiarCuentaBancaria y que esta función sí fuera public. Dentro de la función deberíamos comprobar que el número de cuenta es válido y, si lo es, como estamos dentro de la función, modificar el valor de $cuentaBancaria accediendo con la pseudovariable $this.

Hemos conseguido que, si alguien introduce un número de cuenta no válido, no modifique el atributo de la clase sin ninguna restricción. Debe pasar antes, sí o sí, por esa nueva función que hará las comprobaciones pertinentes. Es por ello que es una buena costumbre definir todos los atributos de una clase como private, aunque como siempre dependerá del programador y lo que esté haciendo.

Utilización de objetos

Tenemos ya nuestra clase definida, los atributos y funciones con sus propiedades y ahora queremos utilizarla. ¿Cómo?

Instanciar un objeto con new

Para la utilización de objetos necesitamos instanciar su clase. Esto es, “obtener un objeto” para que podamos manipularlo. Básicamente, dicho de forma sencilla y comprensible, podemos decir que será el guardarnos la clase en una variable que va a representar el objeto entero, con sus atributos y todas las funciones que puede utilizar. Para ello, utilizamos la palabra reservada new como se puede ver a continuación (se omite toda la definición de la clase para que no sea excesivamente extenso, pero obviamente debe estar):

<?php
class Asignatura { ... }

$matematicas = new Asignatura();
?>

Con esto ya tenemos la clase instanciada en la variable $matematicas. Como se puede ver, hemos utilizado la palabra new junto al nombre de la clase y los paréntesis (obligatorios) casi como si fuera una función. Si hiciéramos a continuación un var_dump($matematicas) para ver lo que hay en esa variable, veríamos por pantalla lo siguiente:

object(Asignatura)#1 (5) { ["nombre"]=> NULL ["horario"]=> NULL ["curso"]=> NULL ["profesor":protected]=> NULL ["alumnos":"Asignatura":private]=> array(0) { } }

Como veis, var_dump nos indica que esa variable es de tipo object, un tipo de datos que todavía no habíamos visto hasta ahora pero que se refiere, como podemos deducir, a objetos. Además, nos indica también a qué clase representa. Posteriormente nos muestra todos los atributos y su valor actual que, como se puede observar, es en todos los casos null excepto en el último, que habíamos definido con el array vacío.

Asignación de valores a atributos public

Ya hemos visto que todo lo indicado como public es accesible desde cualquier lado. Así, una vez tenemos instanciada la clase, podemos asignar valores a esos atributos.

<?php
class Asignatura { ... }

$matematicas = new Asignatura();

$matematicas->nombre = 'Matemáticas';
$matematicas->horario = 'lunes y miércoles a las 9:00';
$matematicas->curso = '2019/20';

echo 'El horario de ' . $matematicas->nombre . ' para el curso ' . $matematicas->curso . ' es ' . $matematicas->horario . '<br>';
?>

El código anterior asigna valores a $nombre, $horario y $curso. Como se puede observar, desde la instancia de la clase que hemos guardado en $matematicas se accede a los atributos (y funciones) también con el símbolo -> y el atributo (o función) a llamar. Hay que tener muy en cuenta que no se debe colocar el símbolo $ en la variable a la que accedemos. El código anterior, por tanto, nos mostraría por pantalla lo siguiente:

El horario de Matemáticas para el curso 2019/20 es lunes y miércoles a las 9:00

Si quisiéramos acceder al profesor, por ejemplo, escribiendo $matematicas->profesor nos encontraríamos con un error fatal que indica, claramente, que no se puede acceder a la propiedad protected llamada $profesor de la clase Asignatura:

Fatal error: Uncaught Error: Cannot access protected property Asignatura::$profesor

Utilizando las funciones definidas para el profesor

Vayamos con el profesor. Recordemos que el profesor está definido como protected, con lo que no podemos acceder directamente a él. Sin embargo, tenemos una función que se llama cambiarProfesor. Pero si intentamos hacer un $matematicas->cambiarProfesor(‘Ramón’) veremos por pantalla otro error fatal, pues esa función está definida también como protected:

Fatal error: Uncaught Error: Call to protected method Asignatura::cambiarProfesor()

Vamos a utilizar, pues, el siguiente código:

<?php
class Asignatura { ... }

$matematicas = new Asignatura();

$matematicas->nombre = 'Matemáticas';
$matematicas->horario = 'lunes y miércoles a las 9:00';
$matematicas->curso = '2019/20';

echo 'El horario de ' . $matematicas->nombre . ' para el curso ' . $matematicas->curso . ' es ' . $matematicas->horario . '<br>';

echo $matematicas->consultarProfesor();

if ($matematicas->insertarProfesor('Ramón')) {
	echo 'Se ha cambiado el/la profesor/a correctamente' . '<br>';
} else {
	echo 'El/La profesor/a indicado/a no tiene permitido dar esta asignatura' . '<br>';
}

if ($matematicas->insertarProfesor('Alicia')) {
	echo 'Se ha cambiado el/la profesor/a correctamente' . '<br>';
} else {
	echo 'El/La profesor/a indicado/a no tiene permitido dar esta asignatura' . '<br>';
}

echo $matematicas->consultarProfesor();
?>

Lo que vemos por pantalla, una vez ejecutemos este código, es lo siguiente:

El horario de Matemáticas para el curso 2019/20 es lunes y miércoles a las 9:00
No hay un profesor asignado a la asignatura Matemáticas
El/La profesor/a indicado/a no tiene permitido dar esta asignatura
Se ha cambiado el/la profesor/a correctamente
El/La profesor/a de la asignatura Matemáticas es Alicia

Recapitulemos. Aparte de lo impreso en cuanto a asignatura, curso y horario que hemos visto antes, lo primero que vemos es que se imprime el mensaje de “No hay un profesor asignado a la asignatura Matemáticas”. Es un resultado esperado, pues ya habíamos visto que no había profesor en nuestra clase y, al haber llamado a consultarProfesor, nos devuelve ese mensaje al no encontrar ninguno.

Posteriormente hemos intentado insertar, con la función insertarProfesor, a Ramón. Como podemos ver más arriba cuando hemos definido la función, este no era uno de los profesores disponibles para la asignatura, por tanto la función va a devolver false y, como lo hemos puesto en un IF-ELSE, entonces nos muestra el mensaje correspondiente a que Ramón no tiene permitido dar esta asignatura.

Por último, solicitamos la inserción de Alicia, que vemos que sí era una de las profesoras permitidas y, por tanto, la función insertarProfesor habrá llamado a $this->cambiarProfesor. Esta función es protected, pero como la estamos llamando desde dentro de la clase, no hay problema y su ejecución se realizado sin inconvenientes, cambiando así al profesor de la asignatura y colocando a Alicia. Se ve claramente cuando volvemos a solicitar que nos muestre el profesor, pues ahora sí hay uno y nos indica que Alicia es la profesora de la asignatura.

Utilizando las funciones predefinidas para alumnos

Por último, vamos a añadir código para los alumnos al final:

<?php
class Asignatura { ... }

$matematicas = new Asignatura();

$matematicas->nombre = 'Matemáticas';
$matematicas->horario = 'lunes y miércoles a las 9:00';
$matematicas->curso = '2019/20';

echo 'El horario de ' . $matematicas->nombre . ' para el curso ' . $matematicas->curso . ' es ' . $matematicas->horario . '<br>';

echo $matematicas->consultarProfesor();

if ($matematicas->insertarProfesor('Ramón')) {
	echo 'Se ha cambiado el/la profesor/a correctamente' . '<br>';
} else {
	echo 'El/La profesor/a indicado/a no tiene permitido dar esta asignatura' . '<br>';
}

if ($matematicas->insertarProfesor('Alicia')) {
	echo 'Se ha cambiado el/la profesor/a correctamente' . '<br>';
} else {
	echo 'El/La profesor/a indicado/a no tiene permitido dar esta asignatura' . '<br>';
}

echo $matematicas->consultarProfesor();

$matematicas->listarAlumnos();

$alumnos = array('Nerea', 'Ignacio', 'Susana', 'Jesús');
$matematicas->incorporarAlumnos($alumnos);

$matematicas->listarAlumnos();

$matematicas->insertarAlumno('Aitana');
?>

La salida por pantalla quedaría así finalmente:

El horario de Matemáticas para el curso 2019/20 es lunes y miércoles a las 9:00
No hay un/a profesor/a asignado/a a la asignatura Matemáticas
El/La profesor/a indicado/a no tiene permitido dar esta asignatura
Se ha cambiado el/la profesor/a correctamente
El/La profesor/a de la asignatura Matemáticas es Alicia
No hay alumnos matriculados en Matemáticas
- Nerea
- Jesús

Vamos a fijarnos en lo que hemos hecho. Primero hemos solicitado listar los alumnos con $matematicas->listarAlumnos() que nos ha indicado el mensaje esperado, es decir, que no hay alumnos matriculados. Recordemos que, inicialmente, hemos definido los alumnos como un array vacío.

Así pues, posteriormente hemos creado un array con cuatro alumnos y hemos solicitado incorporarlos. Si nos fijamos bien, de estos cuatro alumnos que hemos solicitado su inclusión, sólo dos de ellos tenían permitido entrar en esta asignatura. Si hubiéramos dejado la variable $alumnos como public y hubiéramos dejado meter cualquier alumno, hubieran entrado todos sin comprobación de ningún tipo. De esta forma nos hemos asegurado que, de los cuatro, sólo las dos matriculaciones permitidas han entrado. Ahora sí, si volvemos a solicitar el listado de alumnos, vemos que aparecen Nerea y Jesús, que ya se encuentran dentro de nuestra clase.

Si hubiéramos intentando hacer $matematicas->insertarAlumno(‘Aitana’) directamente, nos hubiera lanzado otro error fatal, pues estaríamos intentando utilizar una función que está marcada como private y eso no está permitido desde fuera de la función.

Resumen

En este denso artículo hemos visto las bases de la utilización de objetos en nuestros programas. Hemos visto cómo definirlos, con sus atributos y sus funciones, y aprendido a utilizar la visibilidad con las propiedades public, protected y private que, ya veréis, vamos a estar utilizándolas constantemente. Por último, hemos visto la utilización de objetos instanciando la clase en una variable y utilizándola a nuestra conveniencia, dentro de lo que la propia clase nos permite, o no, hacer.

Estos son los conceptos básicos para la utilización de objetos en nuestros programas. Nos permitirán estructurar mejor nuestros programas, ser más accesibles y controlar todo el proceso con instrucciones directas y claras, además de impedir que se realicen ciertas cosas.

Podemos construir, gracias a esta encapsulación, librerías de clases con muchas utilidades para que las maneje otro programador. Si utilizamos bien las propiedades de protección de atributos y funciones, el programador que utilice nuestra clase va a estar limitado a hacer lo que nosotros hemos querido que haga, pues sólo podrá acceder a los campos public definidos. Ese otro programador no tiene porqué saber cómo hace la clase para guardar, comprobar o modificar sus atributos, pues no lo necesita ya que su interés es únicamente su utilización.

En los próximos artículos ampliaremos aún más la utilización de objetos, aprenderemos sobre constructores y destructores, atributos y funciones estáticas y a la muy utilizada herencia.

Deja una respuesta