miércoles, 20 de julio de 2016

Clases anónimas en php7

Ya ha sido liberado php7, lo que implica una renovación del lenguaje. Una de las novedades que trae son las clases anónimas.

¿Qué es una clase anónima? Simplificando, es una clase que no existe como tal pero que puede instanciar un objeto. Esto quiere decir que podemos crear un objeto a partir de una clase sin nombre, que no ha sido previamente creada.

¿Para qué nos puede servir? Veámoslo con un ejemplo.

Tenemos la siguiente clase Contract que tiene una variable de clase privada llamada $contractDate.
[code]class Contract { private $contractDate; private $contractMonths; public function __construct( string $date, int $contractMonths = 12) { $this->contractMonths = $contractMonths; $this->contractDate = new DateTime($date); } public function getEndDate() { (...) } (...) }[/code]
La variable $contactDate representa una fecha, es decir, tenemos como opciones: un String, un objeto DateTime (mejor), …

Vemos que el constructor de la clase Contract recibe dos parámetros: un String con la fecha y un integer con el número de meses de duración del contrato. Para manejar fechas lo más seguro es utilizar la propia clase de php DateTime, pero la fecha de finalización del contrato se calcularía en un método de la Clase contract getEndDate(), y esta podría guardarse en una variable de clase $endDate o devolverse calculada al vuelo en un método.

Una tercera opción sería pensar que, si la responsabilidad de las fechas está en la clase DateTime, ¿por qué no darle esa responsabilidad? Pues ya está, creemos la clase ContractDate que extendería DateTime, añadiendo sólo un método llamado getEndDate(). Tendríamos un nuevo fichero (un clase por fichero como buena práctica) con la clase ContractDate que sólo vamos a utilizar en la clase Contract pero estaría disponible en toda la aplicación. Aquí entran en juego las clases anónimas para evitar dicha situación y poder extender DateTime sin generar nuevos ficheros.

Modificamos el constructor.
[code hl="11"]class Contract { private $contractDate; const DEFAULT_CONTRACT_MONTHS = 12; public function __construct( string $date, int $contractMonths = self::DEFAULT_CONTRACT_MONTHS) { $this->contractMonths = $contractMonths; $this->contractDate = new class() extends DateTime { public function getEndDate( int $months, string $dateFormat = 'Y-m-d') : string { $date = new DateTime($this->format('Y-m-d')); $date->add(new DateInterval("P".$months."M")); $date->sub(new DateInterval("P1D")); return $date->format($dateFormat); } }; } }[/code]
Vemos que al inicializar $this->contractDate en el constructor escribimos new Class() extends DateTime { … }. Ahora tenemos un objeto de tipo clase anónima, pero a su vez es de tipo DateTime.

Ahora el método getEndDate() en la clase Contract ya no sería necesario si no queremos exponer ese dato fuera de la clase Contract.

Añadimos nuevos métodos públicos a la clase Contract. Uno de ellos se encargará de utilizar la fecha de finalización.
[code hl="13"]public function changeContractMonths(int $months) { $this->contractMonths = $months; } public function printStartDate() { echo("Start Date is ".$this->contractDate->format('Y-m-d').PHP_EOL); } public function printEndDate() { echo("End Date is ".$this->contractDate->getEndDate($this->contractMonths).PHP_EOL); }[/code]
De esta forma hemos visto como:
  • Crear un objeto a partir de una clase que no existe (anónima).
  • Extender una clase existente sin necesidad de crear una nueva clase.

Si añadimos la función notifyEach2Weeks5Times() vemos que el objeto generado a partir de la clase anónima se sigue comportando como un objeto de tipo DateTime.
[code]public function notifyEach2Weeks5Times() { $interval = new DateInterval('P2W'); $period = new DatePeriod($this->contractDate, $interval, 5, DatePeriod::EXCLUDE_START_DATE); foreach ($period as $nextDateTime) { echo $nextDateTime->format('Y-m-d').PHP_EOL; } }[/code]
Probamos la clase Contract con el código:
[code] echo("Contrato de 12 meses desde la fecha actual.".PHP_EOL); $contract = new Contract(date("Y-m-d"), 12); echo($contract->printStartDate()); echo($contract->printEndDate()); echo("Cambiamos los meses de contrato a 24.".PHP_EOL); $contract->changeContractMonths(24); echo($contract->printEndDate()); echo PHP_EOL; echo("Recordatorio cada 2 semanas 5 veces:".PHP_EOL); $contract->notifyEach2Weeks5Times(); [/code]
El resultado obtenido es:
[code]Contrato de 12 meses desde la fecha actual. Start Date is 2015-12-17 End Date is 2016-12-16 Cambiamos los meses de contrato a 24. End Date is 2017-12-16 Recordatorio cada 2 semanas 5 veces: 2015-12-31 2016-01-14 2016-01-28 2016-02-11 2016-02-25[/code]
Fichero en Git Hub

No hay comentarios:

Publicar un comentario