PHP Orientado a Objetos ¿Por qué necesitamos clases y objetos? Encapsulamiento, getters y setters en PHP Herencia y abstracción con PHP Interacción entre objetos Repositorio Interfaces y Polimorfismo Repositorio Notas Diferencia entre clases abstractas e interfaces Autocarga de clases y nombres de espacio con PHP Repositorio Continua aprendiendo Autocarga de clases con Composer y PSR-4 Repositorio Únete a la discusión Repaso y solución a los ejercicios Repositorio Herencia: Inyección de dependencias: Refactorización: Métodos VS propiedades: Clases abstractas VS Interfaces: Patrón Factory y Value Objects Patrón Factory Value Objects Refactorización Reducción de uso de condicionales IF y sentencias SWITCH Repositorio Null object Herencia Propiedades y métodos estáticos Repositorio Propiedades estáticas Operador de resolución de ámbito Métodos estáticos y código acoplado Named constructors Constructores semánticos e interfaces fluidas Repositorio Named constructors Named constructors Fluent interfaces Qué son los facades y cómo implementarlos en tu proyecto Repositorio Notas Actividad Cómo declarar y usar constantes de clases Repositorio Notas Uso de los métodos mágicos __get, __set, __isset y __unset con PHP Repositorio 4 8 9 12 12 12 12 12 14 15 15 15 15 15 15 15 15 16 16 16 16 16 17 17 17 17 18 18 18 18 18 18 18 19 19 19 20 20 20 20 21 22 22 22 22 23 23 23 23 23 Notas Llamadas dinámicas a métodos con PHP Repositorio Notas Material relacionado Llamadas a métodos mágicos con __call y __callStatic en PHP Repositorio Notas Material relacionado Uso de los métodos mágicos __toString y __invoke en PHP Repositorio Notas Métodos mágicos __sleep y __wakeup en PHP Repositorio Notas Continua aprendiendo: Cómo clonar objetos y usar el método mágico __clone Repositorio Notas Notas Material relacionado Iteración de objetos en PHP Repositorio Notas Notas ¿Cuál es la diferencia? Material relacionado Objetos Inmutables en PHP Repositorio Notas Material relacionado Comparación entre objetos en PHP Notas Material relacionado Traits en PHP Notas Conflictos de nombres de métodos Material relacionado Creación de macros en PHP usando traits, métodos estáticos y __call Notas Actividad Material relacionado Instalación y uso de componentes de terceros con Composer Notas Material relacionado Desarrollo de clases y métodos con pruebas automatizadas Notas Material relacionado Array Access en PHP – Parte 1 Repositorio Notas Material relacionado 23 24 24 24 24 25 25 25 26 26 26 26 27 27 27 28 29 29 29 29 31 31 31 31 32 33 33 34 34 34 34 34 35 35 36 36 36 37 38 38 39 39 39 39 41 41 41 42 42 42 43 44 Array Access en PHP – Parte 2 Repositorio Compatibilidad con array_key_exists Uso de ArrayAccess en el framework Laravel Clases anónimas en PHP Repositorio Notas 46 44 44 44 45 45 45 ¿Por qué necesitamos clases y objetos? Aprender un framework como Laravel suena muy llamativo y emocionante, y realmente no requieres más que de conocimientos básicos de PHP para comenzar. Sin embargo, si quieres crear aplicaciones realmente profesionales y escribir un código de alta calidad, necesitarás aprender más a detalle el lenguaje PHP y el paradigma de programación orientada a objetos del cual Laravel hace uso extenso. En Styde ya tenemos un curso de Primeros Pasos con Laravel (entre muchos otros cursos de este framework) y tenemos un curso avanzado de creación de componentes para PHP que te explica conceptos avanzados como la inyección de dependencias, uso de PHPUnit, etc. Pero hoy quiero rebobinar un poco, y crear este curso dedicado a todos los desarrolladores que quizás como tú, se confunden con los temas de OOP como clases abstractas o términos como encapsulamiento, o quizás buscan la respuesta a una pregunta aún más sencilla: ¿Por qué necesitamos clases y objetos? Repositorio Ver el código de esta lección en GitHub Notas Notas por Sergio Ojeda. ¿Que es un objeto? Pensar en objetos es muy parecido a como vemos el mundo, si observamos a nuestro alrededor todo esta compuesto por objetos, sea una casa, un carro, una hoja, etc. Cada objeto está compuesto por dos cosas fundamentalmente, sus propiedades y sus comportamientos. Por ejemplo, podemos decir que un Teléfono tiene propiedades como color, modelo o compañía telefónica, y tiene comportamientos como realizar llamadas, envío de mensajes de texto, entre otros. Podemos utilizar esto como base para la Programación Orientada a Objetos, transformando un elemento de la vida real en elementos utilizables dentro de la programación. Como ya es sabido, un objeto aunque sea el mismo, puede tener distintos comportamientos o propiedades, continuando con el ejemplo anterior, el objeto puede ser un Teléfono, pero existen una gran cantidad de teléfonos de muchas marcas, colores, tamaños y características que lo hacen distinto a los demás, esto define lo que es la identidad del objeto, porque aunque comparten características similares, como las llamadas, encendido, apagado, entre otros, cada uno tiene algo que lo hace diferente. Los objetos dentro de la programación son muy parecidos a la realidad. Pues que un objeto en programación tiene propiedades (o también llamados atributos) y comportamientos (que serían las funciones también llamadas métodos). Las Clases Podemos ver las clases como una abstracción de un elemento de la vida real para transformarlos en elementos utilizables dentro de la programación. Las clases están conformadas tanto por propiedades (que son como variables dentro de la clase) como por métodos (que son como funciones dentro de la clase). Como se menciono anteriormente, las propiedades las podemos comparar con las variables dentro de la clase, en el ejemplo del vídeo tenemos como propiedades de la Clase Persona el nombre y el apellido var $firstName, $lastName; 1 var $firstName, $lastName; Un método es una acción realizada por un objeto, estos están definidos como las funciones de la clase. Por ejemplo el método fullName me retorna el nombre completo de una Persona. function fullName() { return $this->firstName . ' ' . $this->lastName; } 1 2 3 4 function fullName() { return $this>firstName . ' ' . $this->lastName; } Se puede decir que las clases son solo plantillas cuyos métodos o acciones no serán ejecutados en tanto no exista un objeto que requiera su aplicación. Para crear una nueva instancia de un objeto tenemos la palabra reservada new: $person1 = new Person('Duilio','Palacios'); 1 $person1 = new Person('Duilio','Palacios'); Ahora veamos que funcion cumple el método constructor: function __construct($firstName, $lastName) { $this->firstName = $firstName; $this->lastName = $lastName; } function __construct($firstName, $lastName) 1 { 2 $this->firstName = $firstName; 3 $this->lastName = $lastName; 4 } 5 Dentro del código de un constructor se suelen asignar los valores de algunas o todas las propiedades de dicho objeto, para conseguir que el objeto sea creado con valores iniciales. Por ende, un método constructor se ejecuta cada vez que se instancia la clase. No es de carácter obligatorio contar con uno, pero suelen ser bastante útiles. En nuestro ejemplo, cuando se inicia un objeto del tipo Persona va asignar a las propiedades $firstName y $lastName con los datos que coloquemos cuando instanciamos nuestro objeto. $person1 = new Person('Duilio', 'Palacios'); 1 $person1 = new Person('Duilio', 'Palacios'); Para asignar valores o referirnos a la propiedad dentro de una clase se utiliza $this, por ejemplo si utilizamos $this->firstName nos referimos a la variable dentro de nuestra clase Persona. Cada vez que se instancia una clase se crea un objeto nuevo: $person2 = new Person('Ramon','Lapenze'); 1 $person2 = new Person('Ramon','Lapenze'); Con esto creamos dos objetos del tipo persona, y cada uno tiene su propio nombre y apellido. De esta manera podemos evitar la repetición de código. Se puede acceder a los métodos de manera muy fácil, se utiliza la fecha (->) la cual nos permite acceder a las propiedades o métodos de nuestra Clase. echo "{$person1->fullName()} es amigo de {$person2->fullName()}"; echo "{$person1->fullName()} es amigo de {$person2→fullName()}"; Ejemplo Hagamos un ejercicio con un Teléfono, que en este caso sería el objeto de la clase, algunas de las características pueden ser modelo, color y compañía, por otro lado dentro de sus funciones tenemos realizar llamadas y enviar mensajes. Partiendo de eso, podemos crear nuestro objeto: /** * Clase Telefono */ class Phone { var $model; var $color; var $company; function __construct($model, $color, $company) { $this->model = $model; $this->color = $color; $this->company = $company; } function call() { return 'Estoy llamando a otro móvil'; } function sms() { return "Estoy enviando un mensaje de texto"; } } $nokia = new Phone('Nokia', 'Blanco', 'Movistar'); Como se pudo observar, las características son parte de las propiedades del objeto, definidas en variables, y las funciones son las acciones o métodos que dicho objeto puede realizar. Tenemos que utilizar el símbolo -> para interactuar con dichas propiedades y métodos: echo $nokia->call(); // Estoy llamando a otro móvil echo $nokia->color; // Imprimirá Blanco echo $nokia->call(); // Estoy llamando a otro móvil echo $nokia->color; // Imprimirá Blanco Ejercicios Propuestos 1. Realiza una definición de algún objeto de tu preferencia, por ejemplo: Un carro. Tengo un Toyota de color rojo, con cuatro puertas el cual puede acelerar y tocar la bocina. 2. A partir del ejercicio anterior, procede a crear una Clase con las propiedades y los métodos que consideres que estén dentro de tu definición. Encapsulamiento, getters y setters en PHP Esta es la segunda lección de nuestro Curso de programación orientada a objetos en PHP, asegúrate de haber visto la primera lección ¿Por qué necesitamos clases y objetos? antes de continuar. Hoy vamos a hablar sobre uno de los objetivos más importantes de la Programación Orientada a Objetos, el cuál es poder proteger y ocultar información, a dicho proceso se le denomina encapsulamiento. El encapsulamiento de datos previene que el desarrollador haga cambios inesperados al sistema, como también ocultar la información para que no pueda ser modificada o vista por otras clases y esto es muy útil pero además fácil de hacer, como aprenderemos en la lección de hoy. ¿Cómo accedemos a dicha información de forma segura? En el desarrollo de esta clase vamos a explicarte cómo podemos modificar de forma segura las propiedades (o variables) de una clase y cómo podemos presentar dicha información al usuario de una forma correcta con el uso de métodos conocidos como Setters y Getters. Repositorio Ver el código de esta lección en GitHub Recuerda tener siempre en cuenta: Getter: Su función es permitir el obtener el valor de una propiedad de la clase y así poder utilizar dicho valor en diferentes métodos. Setter: Su función permite brindar acceso a propiedades especificas para poder asignar un valor fuera de la clase. exit($person1->firstName) //Imprime el valor y termina el script actual. exit($person1->firstName) //Imprime el valor y termina el script actual. Para tener una definición más clara de los niveles de acceso puedes dirigirte a la lección de Encapsulamiento del curso teórico de OOP. Ejercicios Agrega validación adicional para que el usuario sólo pueda agregar nicknames que tengan al menos 2 caracteres y no sean igual a su nombre o apellido. Agrega la propiedad “fecha de nacimiento” a la clase persona, y que esta propiedad pueda pasarse a través del constructor. Luego crea un método para obtener la edad del usuario (getAge), por supuesto la edad la vas a calcular a partir de la fecha de nacimiento. Herencia y abstracción con PHP La herencia en programación orientada a objetos, nos permite tener clases que extiendan de otras, heredando así sus propiedades y métodos no privados. En teoría esto puede sonar complicado, pero en la práctica es muy sencillo como vamos a aprender en la clase de hoy. La herencia en programación orientada a objetos, nos permite tener clases que extiendan de otras, heredando así sus propiedades y métodos no privados. En teoría esto puede sonar complicado, pero en la práctica es muy sencillo como vamos a aprender en la clase de hoy. Repositorio Ver el código de esta lección en GitHub Notas Recuerda que una clase puede heredar de otra usando la palabra reservada extends para heredar de una clase: class Child extends Parents { // } Una clase Padre solo puede heredar sus variables y métodos con visibilidad publica (public) o protegida (protected) y no privada (private) esto lo veremos con más calma más adelante. class Parents { protected $foo; protected function bar() { // } public function diplay() { // } } Para crear una clase Abstracta debes de utilizar la palabra reservada abstract antes de la palabra class abstract class Parents { // } abstract class Parents { // } Una clase abstracta solo puede heredar sus variables y métodos con visibilidad publica (public) o protegida (protected) ademas de sus métodos abstractos(abstract). abstract class Parents { abstract protected function foo() { // } abstract public function bar() { // } } abstract class Parents { abstract protected function foo() { // } abstract public function bar() { // } } Las clases que heredan de otra clase pueden cambiar el comportamiento de la clase padre sobreescribiendo sus métodos class Parents { public function say() { echo 'Hola!'; } } class Child extends Parents { public function say() { echo 'Hola Mundo!'; } } $child = new Child(); $child->say(); // Hola mundo; Finalmente hay que acotar que PHP no permite la herencia multiple, es decir que no puedes utilizar extends de la siguiente forma: class Chind extends Parent, Other { //Esto no es válido!!!! } Pero de este tema hablaremos en lecciones siguientes. Continua aprendiendo Jerarquía en la programación orientada a objetos Curso teórico de programación orientada a objetos Interacción entre objetos Una característica muy importante de la programación orientada a objetos es la capacidad que los objetos tienen para interactuar con otros. En programación estructurada nuestro código se lee de arriba hacia abajo y escribimos procedimientos de hasta cientos de líneas. En OOP dividimos las responsabilidades de un procedimiento en pequeñas clases y métodos y logramos que un método interactue con otros. De esta manera aunque ya no será posible leer nuestro código en línea recta, podremos hacer cambios más fácilmente en el sistema, escribir pruebas, reusar código, etc. Ahora bien aunque hayas aprendido a declarar clases y métodos, es muy posible que sigas teniendo el paradigma de la programación estructura por mucho tiempo y que cometas ciertos errores que afecten la calidad de tu proyecto y no te permitan aprovechar los beneficios de la OOP, es por ello que en esta clase te enseñaré cómo puedes diseñar tus métodos para lograr una interacción correcta entre objetos, siguiendo el principio “tell, don’t ask”. Además aprenderemos sobre la declaración de tipos en PHP y veremos un repaso de herencia, getters y setters. Repositorio Ver el código de esta lección en GitHub Pronto agregaremos más notas a esta lección. Interfaces y Polimorfismo La palabra polimorfismo significa “múltiples formas” y en programación orientada a objetos puede tener varios significados, por ejemplo la habilidad que tiene un método dentro de un objeto con interactuar con diferentes objetos de diferentes clases de la misma forma pero con resultados diferentes. Esto se logra cuando creamos clases que tienen la misma interfaz (es decir los mismos métodos públicos) pero se comportan de manera diferente. En teoría suena complicado pero con los ejemplos del siguiente video lo aprenderás de manera muy sencilla. Repositorio Ver el código de esta lección en GitHub Notas El polimorfismo es la habilidad de tomar multiples formas: imagina que tienes una Factura y la factura requiere ser impresa como PDF, HTML o como texto plano. Quienes no comprenden estos conceptos suelen hacer esto: <?php class Invoice {} class HtmlInvoice extends Invoice {} class PdfInvoice extends Invoice {} class TextInvoice extends Invoice {} <?php class Invoice {} class HtmlInvoice extends Invoice {} class PdfInvoice extends Invoice {} class TextInvoice extends Invoice {} Pero ¿Qué sucede si tenemos por ejemplo facturas para pago recurrente, para compra de un solo producto, multiples productos, pagos a contados, pagos a crédito? No podemos seguir abusando de la herencia: <?php class RecurrentPaymentInvoice extends PdfInvoice {} //Esto ya es abuso de herencia y no tiene sentido <?php class RecurrentPaymentInvoice extends PdfInvoice {} //Esto ya es abuso de herencia y no tiene sentido Aquí lo mejor es que una factura sea una sola clase: class Invoice y la funcionalidad que pueda cambiar (tomar multiples formas, es decir, polimórfica) se exprese como interfaces que puedan ser: implementadas en clases concretas, instanciadas e inyectadas a la clase, como piezas de lego o de un automóvil que puedas reemplazar: <?php interface ReportFormat {} interface InvoiceType {} interface PaymentType {} <?php 1 2 interface ReportFormat {} 3 4 interface InvoiceType {} 5 interface PaymentType {} Con esto ya es posible crear clases puntuales (para reportes, tipos de pago, tipos de factura, incluso tipo de cliente, etc.) que ya no extenderán de Invoice sino que implementarán una interfaz específica: <?php class HtmlReportFormat implements ReportFormat <?php 1 2 3 class HtmlReportFormat implements ReportFormat Ahora si quieres una factura para pagos recurrentes, a crédito y en HTML ya no tienes que enredarte con una herencia interminable ni repetir código, sino simplemente componerla, por ejemplo de la siguiente forma (o usando el patrón factory que verás más adelante): <?php $invoice = new Invoice(new RecurrentInvoice(Type::Monthly), new CreditPayment(Carbon::format('3 days')); $invoice->reportWith(new HtmlReport()); //o: new HtmlReport($invoice); <?php 1 2 $invoice = new Invoice(new RecurrentInvoice(Type::Monthly), new 3 CreditPayment(Carbon::format('3 days')); 4 5 $invoice->reportWith(new HtmlReport()); //o: new HtmlReport($invoice); Advertencia: todo esto es un ejemplo inventado que podría adaptarse o no a un caso real, queda de parte de cada desarrollador hacer el análisis correspondiente a cada problema específico. Diferencia entre clases abstractas e interfaces La interfaz es sólo un “contrato” que no contiene código. Como has podido ver en el video en la interfaz sólo se define el encabezado de los métodos que deben ser implementados por la clase que implemente dicha interfaz. Una clase abstracta por el contrario es algo intermedio entre una clase y una interfaz: dado que también contiene un “contracto” en este caso métodos abstractos que deben ser implementados por la clase hija, pero también contiene métodos concretos con código. Más adelante veremos más ejemplos de uso de clases abstractas. Estas notas fueron agregadas para responder a dudas comunes de nuestros usuarios, si tienes otra pregunta específica de este curso por favor hazla en el canal #php de nuestro Slack: Autocarga de clases y nombres de espacio con PHP Tan importante como aplicar buenas prácticas y patrones de diseño en nuestro código, es organizarlo en archivos de forma coherente. El estándar en PHP es crear un archivo por clases y luego utilizar una función de autocarga (autoload) para cargar dichas clases. Además de ver esto en la lección de hoy, aprenderás qué son los nombres de espacio (namespaces), cómo usarlos y porqué son importantes Repositorio Ver el código de esta lección en GitHub Pronto agregaremos más notas a esta lección. Continua aprendiendo Autocarga (autoload) de clases con PHP Nombres de espacio en PHP Autocarga de clases con Composer y PSR-4 Hace unos años, cada framework de PHP utilizaba las capacidades de auto-carga de PHP de forma diferente. Esto llevaba a inconsistencias y a que resultara realmente difícil combinar unos componentes o paquetes de PHP con otros. Afortunadamente hoy tenemos la recomendación de estándar PSR-4 y además el manejador de dependencia Composer que también se encarga de autocargar nuestras clases, y esto es lo que veremos en la clase de hoy. Repositorio Ver el código de esta lección en GitHub Pronto agregaremos más notas a esta lección. Únete a la discusión Repaso y solución a los ejercicios En la lección de hoy veremos un repaso de los conceptos de programación orientada a objetos que hemos aprendido hasta ahora como PSR-4, herencia, clases abstractas, inyección de dependencias y refactorización. Además veremos la solución a los principales ejercicios planteados durante la lección 5. Repositorio Ver el código de esta lección en GitHub Herencia: Cuando trabajamos con herencia algunas veces notaremos que un método pertenece a una clase padre o por el contrario no pertenece a la clase padre sino a la clase hija. Una manera sencilla de deducir esto es preguntarte ¿Esta funcionalidad la necesitan todas las clases que heredan de ___ o sólo una clase puntual? Si la respuesta es todas o la mayoría, puedes colocar la funcionalidad en la clase padre, de lo contrario sitúala en la clase hija. Aunque a veces no necesitamos herencia en lo absoluto sino composición. Inyección de dependencias: Con la inyección de dependencias podemos “componer” un objeto (en nuestro ejemplo una unidad) para que aún siendo de la misma clase Unit pueda tener características totalmente diferentes (en nuestro ejemplo armas o armaduras distintas). Este concepto lo seguiremos viendo más adelante. Constructores y setters: podemos pasar dependencias (objetos) u otros valores a un objeto a través de su constructor o a través de métodos setters. Refactorización: Es el término que usamos cuando cambiamos código para que mantenga la misma funcionalidad pero esté más ordenado, sea más fácil de entender, mantener y extender. Métodos VS propiedades: Recuerda que las propiedades nos dan sólo un valor, mientras que con el método podemos encapsular comportamiento. Clases abstractas VS Interfaces: Con ambas podemos forzar que las clases que extiendan de la clase abstracta o implementen de la interfaz cumplan con un “contrato”. La diferencia es que las clases abstractas nos permiten agregar funcionalidad extra (porque es una clase padre) y las interfaces sólo representan un contrato. Algunas veces nos daremos cuenta que necesitamos una en vez de la otra, como veremos en una lección más adelante. *** En las siguientes lecciones seguiremos evolucionando nuestro juego para seguir otras buenas prácticas y explicarte más patrones de diseño y técnicas de OOP y refactorización. Patrón Factory y Value Objects En la lección de hoy vamos a aprender sobre un par de patrones de la programación orientada a objetos como lo son Factory y Value Object. También aprenderemos que muchas veces podemos aplicar refactorización de nuestro código para simplificarlo en vez de hacerlo más complejo En la lección de hoy vamos a aprender sobre un par de patrones de la programación orientada a objetos como lo son Factory y Value Object. También aprenderemos que muchas veces podemos aplicar refactorización de nuestro código para simplificarlo en vez de hacerlo más complejo. Repositorio Ver el código de esta lección en GitHub Patrón Factory En OOP, factory se refiere a un objeto o método que tiene por objetivo crear o instancias otros objetos. Esto es una buena práctica y no debe confundirse con el abuso del operador new dentro de clases. Más sobre el tema en nuestro curso avanzado de creación de componentes con PHP. Value Objects Los value objects nos permiten agrupar grupos de valores que tienen sentido juntos pero no tienen sentido separados. Por ejemplo: Dinero: new Money(50, 'USD'); // cantidad y tipo de moneda Coordenadas: new Coordinates('38.898648N', '77.037692W'); // latitud y longitud Fecha: new Date(2016, 07, 21); //año, mes, día Entre muchos otros. Refactorización Como habrás notado en esta lección, refactorizar no significa complicar el código, sino adaptarlo a la lógica y requerimientos de nuestra aplicación, y esto implica muchas veces simplificar el código, por ejemplo los cambios en la forma como manejamos las dependencias a través de polimorfismo para las armas y armaduras de nuestro juego de ejemplo hizo innecesaria la herencia en la clase Unit y este tipo de cambios sucede también muy a menudo en aplicaciones reales. Al refactorizar tu código debes ser muy cuidadoso con no romper la funcionalidad existente de tu aplicación, por ejemplo si estás eliminando una jerarquía asegúrate de reemplazar las subclases en otras partes de tu app y de que realmente no son necesarias, etc. Este tema lo veremos más adelante en uno nuevo curso de refactorización. *** En la lección siguiente aprenderemos cómo utilizando OOP podemos reducir la cantidad de código estructurado (como condicionales y sentencias switch) de nuestra aplicación. Reducción de uso de condicionales IF y sentencias SWITCH Cuando trabajamos con programación estructurada, tomamos decisiones en nuestro código utilizando condicionales IF o sentencias SWITCH, pero cuando trabajamos con programación orientada a objetos, podemos desarrollar de manera que sean objetos y no decenas de condicionales o switch / case los que tomen las decisiones sobre cómo se debe procesar nuestro programa. Por ejemplo: cuánto descuento se le debe aplicar a un cliente. Esto lo vamos a ver en la lección de hoy con un par de ejemplos sencillos pero muy útiles y fáciles de entender. Repositorio Ver el código de esta lección en GitHub Null object Un null object es una especie de placeholder con el cuál podremos evitar tener que preguntar si un objeto está presente o no, dado que el objeto siempre estará presente así la versión del mismo no cause ningún efecto sobre el programa. Herencia Podemos utilizar herencia para colocar los condicionales necesarios dentro de la clase padre y así en las subclases no tendremos más que sobre-escribir los métodos correspondientes. Veremos más sobre esto en una lección siguiente. *** Con esto finalizamos la parte 1 de este curso, en la segunda parte veremos más características de la OOP en PHP como son métodos y propiedades estáticas, métodos mágicos y más. Propiedades y métodos estáticos Para comenzar la segunda parte de nuestro curso de programación orientada a objetos, te enseñaré no sólo a crear y usar métodos y propiedades estáticos, sino también a cuando es conveniente su uso y cuando no y porqué. También aprenderemos sobre el uso del operador de ámbito :: Repositorio Ver el código de esta lección en GitHub Propiedades estáticas Mientras que los valores de las propiedades que hemos visto hasta ahora pertenecen a la instancia de un objeto, una propiedad estática forma parte de la clase: class Person { protected static $name; public function __construct($name) { static::$name = $name; } public function getName() { return static::$name; } } $person1 = new Person('Duilio'); $person2 = new Person('Ramon'); $person1->getName(); // retornará Ramon Quiere decir que todos los objetos que accedan a dicha propiedad estática podrán leer y modificar el mismo valor, y esto la mayoría de las veces no es conveniente y puede traer resultados inesperados y errores más difíciles de depurar. Incluso en casos donde realmente queremos compartir una información a través de todo el sistema, es mejor utilizar un contenedor de inyección de dependencias, como aprenderás en el curso avanzado de PHP: creación de componentes. Operador de resolución de ámbito Para llamar a métodos estáticos, acceder a propiedades estáticas o constantes, usamos el operador de resolución de ámbito ::. Ejemplos: Str::camelCase('words_with_underscores'); debería retornar: wordsWithUnderscores Unit::MAX_DAMAGE Diccionario::$words Métodos estáticos y código acoplado Cuando llamamos a un método estático dentro de un método de otra clase, estamos atando una clase a otra. En el ejemplo de la lección, ahora nuestra clase Attack requiere obligatoriamente de la clase Styde\Translator debido al llamado a Translator::get() mientras que Armor y Weapon son reemplazables dentro de la clase Unit puesto que se pasan por inyección de dependencias. Verás más sobre este tema en nuestro curso avanzado de PHP: creación de componentes. Named constructors Los métodos estáticos son bastante útiles para crear los llamados named constructors, como por ejemplo: Unit::createSoldier('Silence') es el equivalente a new Soldier('Silence', new BasicSword()); Carbon::now() es el equivalente a new Carbon(); *** En las siguientes 2 lecciones aprenderás sobre buenas prácticas y trucos en el uso de métodos estáticos. Constructores semánticos e interfaces fluidas A pesar de todo, los métodos estáticos tienen una serie de utilidades interesantes y hoy te voy a explicar sobre una de ellas, que en inglés se denomina “named constructors” y nosotros podríamos traducir por constructores semánticos, además veremos sobre los “fluent interfaces” o interfaces fluidas que son bastante usadas dentro del framework Laravel. Repositorio Ver el código de esta lección en GitHub Named constructors Para crear un constructor semántico sólo tienes que crear un método estático que cree y retorne una nueva instancia de una clase: public static function createSoldier($name) { return new Unit($name, new BasicSword) } Aunque en el video no pasé el parámetro $name a través del método, fíjate cómo es perfectamente posible. Así que te invito a hacer este cambio y a crear otro método para otro tipo de unidad. public static function newAdmin(array $attributes) { $admin = new User($attributes); $admin->role = 'admin' return $admin; } A pesar de todo, los métodos estáticos tienen una serie de utilidades interesantes y hoy te voy a explicar sobre una de ellas, que en inglés se denomina “named constructors” y nosotros podríamos traducir por constructores semánticos, además veremos sobre los “fluent interfaces” o interfaces fluidas que son bastante usadas dentro del framework Laravel. Repositorio Ver el código de esta lección en GitHub Named constructors Para crear un constructor semántico sólo tienes que crear un método estático que cree y retorne una nueva instancia de una clase: public static function createSoldier($name) { return new Unit($name, new BasicSword) } 1 public static function createSoldier($name) 2 { 3 return new Unit($name, new 4 BasicSword) } Aunque en el video no pasé el parámetro $name a través del método, fíjate cómo es perfectamente posible. Así que te invito a hacer este cambio y a crear otro método para otro tipo de unidad. public static function newAdmin(array $attributes) { $admin = new User($attributes); $admin->role = 'admin' return $admin; } public static function newAdmin(array $attributes) 1 { 2 $admin = new User($attributes); 3 $admin->role = 'admin' 4 5 return $admin; 6 7 } Como aprendiste en una lección anterior, un named constructor es a su vez un método factory. Fluent interfaces Para crear interfaces fluidas sólo tienes que recordar retornar $this luego de cada función. Las interfaces fluidas suelen usarse para métodos que modifican el estado de un objeto (como un setter). function setArmor(Armor $armor) { $this->armor = $armor; return $this; } function setArmor(Armor $armor) { $this->armor = $armor; return $this; } *** En la siguiente lección aprenderemos sobre el uso de métodos estáticos en la creación de “facades”. Qué son los facades y cómo implementarlos en tu proyecto El uso de métodos estáticos es muy sencillo puesto que nos permite invocar a un método en cualquier lugar de nuestro sistema, sin tener que preocuparnos por inyectar dependencias y ni siquiera por crear una nueva instancia de una clase. Pero esta facilidad de uso viene con un costo: terminamos con un sistema menos flexible y más acoplado. Aquí es donde entra el concepto de facades en PHP, ideado por Taylor Otwell para Laravel 4, las cuáles son el punto intermedio entre una buena arquitectura y una interfaz fácil de usar como aprenderás en esta lección. Repositorio Ver el código de esta lección en GitHub Notas Por Clemir Rondón. Los principios SOLID, acrónimo de Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion, representan cinco principios básicos de la programación orientada a objetos. En caso que quieras eliminar el archivo helpers.php recuerda eliminarlo también del archivo composer.json y, luego ejecuta en consola: composer dump-autoload Para guardar el contenido del mensaje del Log en un archivo usa el método file_put_contents de PHP con la bandera FILE_APPEND para en caso que ya el archivo exista, añada la información en él, en vez de sobrescribirlo. El principio SOLID: Open-closed (principio abierto/cerrado) establece que las clases u otra entidad de software deben estar abiertas para su extensión y cerradas para su modificación. El principio SOLID Dependency inversion (principio de inversión de la dependencia) nos indica que debemos depender de abstracciones en vez de implementaciones concretas. En el curso Curso de creación de componentes para PHP y Laravel puedes estudiar cómo el framework Laravel implementa la inyección de dependencias, facades y muchos otros temas que te ayudarán a entender cómo funciona Laravel por dentro. Actividad Implementa el concepto de facade para la clase translator que se desarrolló en la lección Métodos y propiedades estáticos. *** En la siguiente lección aprenderemos sobre el uso de constantes en PHP. Cómo declarar y usar constantes de clases En esta lección aprenderás a declarar constantes dentro de tus clases y a usarlas en cualquier lugar de tu aplicación. Además hablaremos del anti-patrón de números mágicos, y algunos adelantos de la versión 7.1 de PHP. Repositorio Ver el código de esta lección en GitHub Notas Pronto agregaremos notas a esta lección. *** En la siguiente parte de este curso aprenderemos sobre el uso de métodos mágicos en PHP y algunos trucos usados por el ORM Eloquent de Laravel Uso de los métodos mágicos __get, __set, __isset y __unset con PHP PHP es un lenguaje muy dinámico y ofrece a los desarrolladores la posibilidad de declarar cualquier lógica para acceder a propiedades o llamar a métodos dentro de una clase, aunque estos no estén definidos previamente. Esto es lo que se conoce como métodos mágicos y algunos componentes de PHP como por ejemplo el ORM Eloquent de Laravel, hacen uso de esta característica para mostrarnos una API más dinámica y fácil de usar, y por supuesto este tema lo veremos a partir de hoy en nuestro curso de programación orientada a objetos con PHP. Repositorio Ver el código de esta lección en GitHub Notas Por Clemir Rondón. Puedes trabajar con las propiedades dinámicamente, por ejemplo, si se llama a $this>$key = $value; PHP está considerando a $key como una variable que será evaluada antes de asignarle un valor. Los métodos mágicos en PHP son aquellos métodos especiales que se llaman automáticamente. Estos comienzan con el símbolo (__) seguido de una palabra reservada tales como:__construct, __get, __set, __isset, etc. Estos debe ser declarados y definidos por el desarrollador dentro de la clase y no serán llamados explícitamente sino que se invocarán según el evento que ocurra. __get() es el método mágico con el cual podemos obtener el valor de una propiedad de una clase dinámicamente, cuando ésta es inaccesible (la propiedad está declarada como private o protected) o no existe en la clase. public __get ( string $name ) __set() es el método mágico que PHP llamará automáticamente (si está declarada en la clase) cuando queremos definir una propiedad inaccesible o inexistente. Este método recibe dos argumentos: el nombre de la propiedad que intentas acceder y el valor que le quieres asignar. public __set (string $name, mixed $value) Llamadas dinámicas a métodos con PHP Además de disponer de métodos mágicos, PHP posee mecanismos para comprobar si un método está definido en una clase y llamarlo aunque el nombre de dicho método sea variable, y esta es otra de las técnicas que usa el ORM Eloquent para crear los modificadores de atributos get*Attribute y set*Attribute, como veremos en la lección de hoy. Repositorio Ver el código de esta lección en GitHub Notas Por Clemir Rondón. En la lección Herencia y abstracción con PHP puedes repasar sobre por qué usamos una clase abstracta para la clase Model. Por defecto, si una función en PHP se omite el return, el valor retornado será NULL. Con la función de PHP method_exists() podemos comprobar si existe un método de una clase, la cual recibe como argumentos el objeto o nombre de la clase y el nombre del método a evaluar. Cuando una cadena o string contiene variable escalar, un elemento de un arreglo o la propiedad de un objeto que queremos sea evaluada podemos usar la sintaxis compleja de PHP, encerrando entre llaves la expresión a evaluar: "<p>Nickname: {$user->nickname}</p>" Puedes estudiar cómo la clase Model del framework Laravel implementa los modificadores de atributos vistos en esta lección. Material relacionado Uso de los métodos mágicos __get, __set, __isset y __unset con PHP Encapsulamiento, getters y setters en PHP Llamadas a métodos mágicos con __call y __callStatic en PHP PHP nos permite interactuar con propiedades de objetos aunque éstas no estén definidas dentro de la clase, a través del uso de los métodos mágicos __get, __set, etc. como aprendimos en la lección anterior. Pero… ¿No sería genial si además pudiésemos interactuar con métodos dinámicos aunque estos no estén definidos en la clase? Esto quizás nos pueda ser útil para desarrollar algunas API y afortunadamente es posible con el uso de __call y __callStatic que veremos en detalle en la lección de hoy. Repositorio Ver el código de esta lección en GitHub Notas Por Clemir Rondón. __call() es el método mágico de PHP que se invocará cuando se intenta llamar a un método que no está definido en el objeto. __callStatic() es el método mágico que se invocará cuando se intenta llamar a un inaccesible o no disponible método en el contexto de una clase, es decir, estamos llamando un método estático que no existe en la clase. Tanto __call() como __callStatic() reciben dos argumentos: un string o cadena con el nombre del método y un array que contiene los argumentos que se están llamando con el método. Toma en cuenta que siempre que definas el método __callStatic() en la clase, éste debe ser también un método estático, es decir, public static __callStatic (string $name, array $arguments) Un ejemplo básico del uso de estos métodos mágicos es: class UnaClase { public function __call($metodo, array $args) { var_dump($metodo, $args); } public static function __callStatic($metodo, array $args) { var_dump($metodo, $args); } } $unObjeto = new UnaClase; //Llama al método mágico __call $unObjeto->unMetodo('Un argumento', 'otro argumento'); //Llama al método mágico __callStatic UnaClase::unMetodo('todos', 'los', 'argumentos'); Con sprintf de PHP podemos devolver una cadena según un formato dado. Como PHP no verifica que el método llamado, que invoca a los métodos mágicos __call() o __callStatic(), necesita de al menos un argumento, entonces se puede lanzar una excepción para dar mayor información al momento de la depuración o para ayudar al entendimiento de la clase y sus métodos. Recuerda retornar $this luego de cada función para crear interfaces fluidas como vimos en la lección Constructores semánticos e interfaces fluidas. Para el ejemplo de la lección se usó el método mágico __callStatic() para crear un nodo HTML, es decir, llamamos un método estático cuyo nombre es la etiqueta que queremos crear, por ejemplo: HTMLNode::input(); y para agregarle los atributos y su correspondiente valor al nodo HTML se usó el método mágico __call(), es decir, encadenamos un método con un argumento, donde el método es el atributo y el argumento es su valor, por ejemplo: HTMLNode::input() ->name('content') →type('text'); Material relacionado Uso de los métodos mágicos __get, __set, __isset y __unset con PHP Autocarga de clases y nombres de espacio con PHP Propiedades y métodos estáticos Uso de los métodos mágicos __toString y __invoke en PHP En la lección de hoy aprenderemos a usar 2 nuevos métodos mágicos con PHP. Primero el método __toString el cual nos permite transformar un objeto en una cadena, y segundo el método __invoke que nos permite “invocar” o llamar a un objeto como si se tratara de una función. Todo esto junto con algunos tips adicionales lo podrás ver detalladamente en el siguiente video tutorial: Repositorio Ver el código de esta lección en GitHub Notas Por Clemir Rondón Las lecciones anteriores sobre métodos mágicos: Uso de los métodos mágicos __get, __set, __isset y __unset con PHP Llamadas dinámicas a métodos con PHP Llamadas a métodos mágicos con __call y __callStatic en PHP Con el método mágico __toString() una clase puede decidir qué comportamiento tendrá cuando sea usada como si fuera una cadena o transformada en una cadena. Importante: el método __toString() no debe lanzar una excepción pues se producirá un error fatal. Si estás usando PHP 7 sabrás que puedes usar el operador de fusión de null (??) , donde en vez de escribir: $attribute = isset($this->attributes[$name]) ? $this->attributes[$name] : null; 1 2 3 $attribute = isset($this->attributes[$name]) ? $this->attributes[$name] : null; Puedes usar: $attribute = isset($this->attributes[$name]) ?? null; 1 $attribute = isset($this->attributes[$name]) ?? null; Cuando se intenta llamar a un objeto como si fuera una función se invoca al método __invoke. Métodos mágicos __sleep y __wakeup en PHP La serialización es una técnica que nos permite convertir un valor, por ejemplo un objeto, en una representación que facilite su almacenamiento en una base de datos, en un archivo o su transferencia a través de la red, como en el caso de una API. En esta lección aprenderás a trabajar con los métodos mágicos __sleep() y __wakeup() que son llamados cuando un objeto en PHP es serializado y deserializado. Repositorio Ver el código de esta lección en GitHub Notas Por Clemir Rondón La serialización de objetos en PHP se realiza con la función serialize() que devuelve un string que contiene un flujo de bytes que representa el valor que se pueda almacenar en PHP, esto sin perder su estructura y tipo. Cuando el método __sleep() está definido en una clase, éste será llamado antes de realizar la serialización con el fin de limpiar el objeto y serializar solo lo que se necesite. Este método no acepta ningún argumento y debe devolver un array con los nombres de las propiedades del objeto que deben ser tomadas en cuenta al serializarlo. Por ejemplo: public function __sleep() { return ['attributes', 'table']; } Es decir, si no quieres que una propiedad en particular del objeto sea serializada, se debe omitir en el array que se está retornando. Recuerda además que solo se serializan las propiedades de un objeto, no sus métodos. Un objeto serializado sigue la siguiente estructura: O:strlen(nombre del objeto):"nombre del objeto":tamaño del objeto:{s:strlen(nombre de propiedad): "nombre de propiedad":s:strlen(valor propiedad):"valor de propiedad";(repite por cada propiedad)} Por ejemplo: O:10:"Styde\User":1{s:13:"*attributes";a:2{s:4:"name";s:6:"Duilio";s:5:"email";s:15:"admin@styd e.net"}} Con el resultado obtenido de la función serialize() podemos persistirlo en la base de datos o almacenarlo en un archivo como se hizo en la lección con la función file_put_contents de PHP La función complementaria a serialize() es unserialize(), es decir, que nos permite reconstruir una variable serializada a un valor PHP. En cuyo caso, si el valor es un objeto se ejecutará el método mágico __wakeup() (si dicho método está definido en la clase), con la finalidad de realizar operaciones de reinicialización o de restablecimiento que pudieron haberse dejado a un lado en el proceso de serializar el objeto. El método __wakeup() no recibe ningún parámetro ni devuelve nada, simplemente realiza las operaciones necesarias luego de deserializar el objeto, como por ejemplo, hacer un llamado a la base de datos para recuperar las propiedades que fueron omitidas al serializarlo. Como alternativa al método mágico __sleep y __wakeup se puede usar la interface Serializable de PHP para el proceso de serialización de un objeto. Continua aprendiendo: Autocarga de clases y nombres de espacio con PHP Autocarga de clases con Composer y PSR-4 Uso de los métodos mágicos __toString y __invoke en PHP Cómo clonar objetos y usar el método mágico __clone En la lección siguiente aprenderemos más sobre cómo los objetos colaboran y trabajan entre sí cuando se pasa un objeto como argumento del método de otro objeto, además aprenderemos cómo clonar objetos y sobre el uso del método mágico __clone: Repositorio Ver el código de esta lección en GitHub Notas Por Clemir Rondón En esta lección se sigue trabajando con la clase User que se empezó a trabajar en Uso de los métodos mágicos __get, __set, __isset y __unset con PHP Como recomendación evita usar condicionales else para simplificar tu código, es decir, en vez de hacer: if ($esCorrecto) { //proceso } else { //otro proceso } Te recomendamos escribir lo siguiente: if (! $esCorrecto) { // finaliza con un return o una excepción. } // proceso En la lección siguiente aprenderemos más sobre cómo los objetos colaboran y trabajan entre sí cuando se pasa un objeto como argumento del método de otro objeto, además aprenderemos cómo clonar objetos y sobre el uso del método mágico __clone: Repositorio Ver el código de esta lección en GitHub Notas Por Clemir Rondón En esta lección se sigue trabajando con la clase User que se empezó a trabajar en Uso de los métodos mágicos __get, __set, __isset y __unset con PHP Como recomendación evita usar condicionales else para simplificar tu código, es decir, en vez de hacer: if ($esCorrecto) { //proceso } else { //otro proceso } if ($esCorrecto) { 1 //proceso 2 } else { 3 4 //otro proceso 5 } Te recomendamos escribir lo siguiente: if (! $esCorrecto) { // finaliza con un return o una excepción. } // proceso if (! $esCorrecto) { 1 // finaliza con un return o una excepción. 2 } 3 4 // proceso Por otro lado, con la función array_shift de PHP obtienes y eliminas el primer elemento de un array. Para repasar sobre el uso del método parent::__construct puedes ver la lección Interacción entre objetos. Los objetos son pasados por referencia, ya sea cuando hacemos asignación o cuando son pasados a través de un método, es decir, no funciona como una variable que cuando hacemos $b = $a, $b contiene el valor que tiene $a, sino que al hacer $objeto2 = $objeto1 estamos guardando es la referencia al objeto original, es decir, que $objeto1 y $objeto2 están apuntando al mismo espacio de memoria, por tanto, si se cambia una propiedad, ésta también cambiaría en el objeto original. Así que si queremos clonar un objeto, es decir, duplicarlo sin que altere el original, debemos usar la palabra reservada clone de PHP: $objetoCopia = clone $objeto; Con ello estamos creando $objetoCopia con en un nuevo espacio de memoria con las mismas propiedades que tiene $objeto. Si la clase del objeto tiene definido el método mágico __clone entonces se invocará para realizar los cambios necesarios a las propiedades del objeto clonado. Nota adicional: no se puede llamar al método mágico __clone directamente, en este caso __clone solo se invocará cuando se use la palabra reservada clone. Con esta lección terminamos de estudiar los principales métodos mágicos en las clases de PHP, si quieres conocer el resto puedes hacerlo desde la documentación de PHP: __destruct() __set_state() __debugInfo() Si aún tienes dudas sobre este tema, por favor escríbenos para complementar el material. Nos vemos en la parte 4 del curso. Material relacionado Uso de los métodos mágicos __get, __set, __isset y __unset con PHP Llamadas dinámicas a métodos con PHP Llamadas a métodos mágicos con __call y __callStatic en PHP Uso de los métodos mágicos __toString y __invoke en PHP Métodos mágicos __sleep y __wakeup en PHP Iteración de objetos en PHP A partir de PHP 5 es posible iterar objetos como si se tratara de un array asociativo. En este caso la iteración se hará por defecto sobre las propiedades públicas del objeto. Pero en algunos casos queremos un comportamiento diferente y para ello PHP ofrece 2 interfaces: Iterator y IteratorAggregate las cuales aprenderemos a usar en la siguiente lección. Repositorio Ver el código de esta lección en GitHub Notas Por Clemir Rondón. La iteración es el proceso de recorrer una lista de elementos. Con PHP se puede iterar objetos usando la sentencia foreach y por defecto, se podrá acceder a todas propiedades del objeto que tengan visibilidad pública. Es decir, si quisieramos iterar también sobre las propiedades del objeto con visibilidad protected o private tendríamos que hacerlos públicos, pero esto iría en contra del encapsulamiento en la programacion orientada a objetos. La solución a esto es usar un iterador, el cual es un objeto que puede recorrer una lista. Para ello PHP tiene una Biblioteca Estándar (SPL por las siglas de Standard PHP Library en inglés) que nos proporciona una colección de interfaces y clases pensadas para solucionar problemas comunes. Así que en caso de iterar un objeto podemos implementar una de las interfaces de la biblioteca Estándar de PHP con las cuales se le permite a una clase decidir qué y cómo sus objetos serán iterados: A partir de PHP 5 es posible iterar objetos como si se tratara de un array asociativo. En este caso la iteración se hará por defecto sobre las propiedades públicas del objeto. Pero en algunos casos queremos un comportamiento diferente y para ello PHP ofrece 2 interfaces: Iterator y IteratorAggregate las cuales aprenderemos a usar en la siguiente lección. Repositorio Ver el código de esta lección en GitHub Notas Por Clemir Rondón. La iteración es el proceso de recorrer una lista de elementos. Con PHP se puede iterar objetos usando la sentencia foreach y por defecto, se podrá acceder a todas propiedades del objeto que tengan visibilidad pública. Es decir, si quisieramos iterar también sobre las propiedades del objeto con visibilidad protected o private tendríamos que hacerlos públicos, pero esto iría en contra del encapsulamiento en la programacion orientada a objetos. La solución a esto es usar un iterador, el cual es un objeto que puede recorrer una lista. Para ello PHP tiene una Biblioteca Estándar (SPL por las siglas de Standard PHP Library en inglés) que nos proporciona una colección de interfaces y clases pensadas para solucionar problemas comunes. Así que en caso de iterar un objeto podemos implementar una de las interfaces de la biblioteca Estándar de PHP con las cuales se le permite a una clase decidir qué y cómo sus objetos serán iterados: Interfaz Iterator class MiClase implements Iterator { } class MiClase implements Iterator 1 { 2 3 } Interfaz IteratorAggregate class MiClase implements IteratorAggregate { } class MiClase implements IteratorAggregate 1 { 2 3 } Con la interfaz Iterator se deben definir los siguientes 5 métodos públicos en la clase que la está implementando: current(): devuelve el elemento actual. key(): devuelve la clave del elemento actual. next(): Avanza al siguiente elemento. rewind(): Rebobine la Iterator al primer elemento. valid(): Comprueba si la posición actual es válida. Es decir que cuando se usa un foreach con un objeto que implementa la interfaz Iterator internamente hace lo siguiente: 1. Al comienzo de la ejecución del foreach llama al método rewind() para reiniciar la iteración. 2. Antes de cada iteración se llama al método valid() para comprobar si hay un dato válido. 3. Dependiendo del resultado de valid() toma una decisión: Si devuelve falso el bucle termina. Si devuelve verdadero llama a los métodos current() y key() para devolver el elemento actual y su clave. 4. Se ejecutan las operaciones del bucle con los elementos devueltos. 5. Después de cada iteración se llama al método next() hallar el siguiente elemento y se repiten las instrucciones desde el paso 2. La implementación de los métodos de esta interfaz usada en el video se encuentra en http://php.net/manual/es/language.oop5.iterations.php Como alternativa se puede usar la interfaz IteratorAggregate que solo tiene un método que se debe definir getIterator() el cual devuelve un iterador que se encargará de hacer las operaciones para iterar el objeto. ¿Cuál es la diferencia? La diferencia entre las 2 interfaces es que un objeto que implemente Iterator puede iterar directamente con las propiedades del objeto o crear un iterador externo, en cambio con un objeto que implementa IteratorAggregate solo crea un iterador externo que se encargará de iterar las propiedades del objeto. Por tanto si se quiere tener un mayor control y personalización del comportamiento de la iteración de las propiedades de un objeto se usa la interfaz Iterator pero si las propiedades del objeto son sencillas como por ejemplo un array, podemos resolverlo usando la interfaz IteratorAggregate que de manera sencilla y fácil se delega la iteración a otra clase para así reusar métodos de iteración comunes como por ejemplo la clase ArrayIterator. ArrayIterator es una clase que te permite crear un iterador de cualquier array de PHP. Esta clase también es proveída por la biblioteca SPL. Countable es una interfaz también perteneciente a la Biblioteca Estándar de PHP con la cual podemos contar el número de elementos de un objeto. Se implementa de la siguiente manera: class MiClase implements Countable { } class MiClase implements Countable 1 { 2 3 } y se debe definir el método público count() que retorne el número de elementos del objeto. Material relacionado Encapsulamiento, getters y setters en PHP Interfaces y Polimorfismo Objetos Inmutables en PHP Cuando trabajamos con objetos algunas veces es necesario que los métodos que interactuan con ellos no modifiquen su estado, esto es lo que llamamos objetos inmutables. En la lección de hoy veremos como crear y trabajar con objetos de este tipo. Repositorio Ver el código de esta lección en GitHub Notas En caso de que no estés trabajando con PHP 7 donde tenemos el operador de fusión de null (??) esto return $this->attributes['beverage'] ?? null; 1 return $this->attributes['beverage'] ?? null; lo puedes sustituir con: return $this->attributes['beverage'] ? $this->attributes['beverage'] : null; 1 2 3 return $this->attributes['beverage'] ? $this->attributes['beverage'] : null; Aunque te recomendamos que actualices para aprovechar las mejoras y las nuevas funcionalidades que trae. Con la función array_filter de PHP obtenemos un array resultante luego de iterar cada elemento del array pasándolo a un callback que evalúa si permanecerá en el array resultante o no. El problema presentado en la lección también se puede solucionar usando el método mágico __clone. Material relacionado Cómo clonar objetos y usar el método mágico __clone Uso de los métodos mágicos __toString y __invoke en PHP Comparación entre objetos en PHP En la lección de hoy conoceremos cómo trabajar con la comparación de objetos en PHP mediante los operadores == y ===. También aprenderás sobre la manera adecuada de comparar objetos dependiendo de si son value objetos o si representan una entidad del mundo real. Notas Por Clemir Rondón En PHP la comparación de objetos se puede hacer por medio de: Operador de comparación (==) donde dos objetos son iguales si tienen los mismos atributos y valores y son instancias de una misma clase. Operador de igualdad (===) es una comparación estricta, es decir, dos variables serán iguales solo si hacen referencia a la misma instancia de la misma clase. Recuerda que los objetos se pasan y se asignan por referencia como vimos en la lección Cómo clonar objetos y usar el método mágico __clone Sobre Value Objects se habló en la lección 9 de este curso. Otro ejemplo de un value object es un objeto dirección donde: $address = new Address('Avenida 1'); $address2 = new Address('Avenida 1'); $address == $address2; // true son iguales $address === $address2; // false pero no son idénticos $address = new Address('Avenida 1'); 1 $address2 = new Address('Avenida 1'); 2 3 4 $address == $address2; // true son iguales 5 $address === $address2; // false pero no son idénticos La forma de distinguir si un objeto es Value Object o no es comprobando si posee un atributo de identificación como un id que sea único y lo diferencie de otro objeto aunque tenga los demás atributos iguales. Por ejemplo, el número de cédula o pasaporte en una clase Person. La forma para comparar a objetos que tiene un atributo de identificación es comparando ese atributo de cada objecto para así estar seguro que haga referencia al mismo objeto o entidad del mundo real, es decir, dos objetos instancias de Person serán iguales si y solo si su atributo passport son idénticos. $person1->passport == $person2->passport 1 $person1->passport == $person2->passport Material relacionado Patrón Factory y Value Objects Cómo clonar objetos y usar el método mágico __clone Traits en PHP PHP como muchos de los lenguajes de programación no permite el uso de herencia múltiple; por ello recurre a un mecanismo llamado Traits o rasgos que hace posible que clases independientes y pertenecientes a distintas jerarquías puedan disponer de los mismos métodos. En la lección de hoy aprenderás a crear y trabajar con traits, y sus particularidades como el orden preferencia de los métodos y la resolución de conflictos cuando dos traits implementan métodos con el mismo nombre. Notas Por Clemir Rondón Algunas características de los Traits son: Están disponible desde PHP 5.4 Un Trait es similar a una clase, pero con el único objetivo de agrupar funcionalidades muy específicas y de una manera coherente. No se puede instanciar directamente un Trait. Habilita la composición horizontal de comportamientos, es decir, permite reusar código sin tener que usar herencia y así evitando los problemas de duplicidad para clases que tienen los mismos comportamientos pero no pueden derivarse de una única clase padre. Orden de precedencia para los métodos: los métodos de la clase actual sobrescriben los métodos del Trait, que a su vez sobrescriben los métodos heredados. Se pueden insertar múltiples Traits en una clase, mediante una lista separada por comas en la sentencia use class Post { use Trait1, Trait2; } Recuerda tener presente que los traits también usan nombres de espacio (namespaces), por los tanto debes importarlos igual que las clases cuando no compartan el mismo namespace. Si un trait define una propiedad, en la clase no se puede redefinir dicha propiedad o se producirá un error. Los traits soportan el uso de métodos abstractos para imponer requisitos a las clases que implementen dicho trait. Conflictos de nombres de métodos Si dos o más traits poseen un método con el mismo nombre y ambos traits se asignan a una clase, se producirá un error fatal como resultado del conflicto. Para resolverlo se debe indicar explícitamente cual método usar con el operador insteadof, ejemplo: trait CanWalk { public function move() { echo 'camina'; } } trait CanRun { public function move() { echo 'corre'; } } class Person { use CanWalk, CanRun { CanRun::move insteadof CanWalk; } } y en caso de querer trabajar con los 2 métodos debes asignarle un alias, sin olvidar usar el operador insteadof para resolver el conflicto, de esta manera: class Person { use CanWalk, CanRun { CanRun::move insteadof CanWalk; CanWalk::move as walk CanRun::move as run } } Material relacionado Herencia y abstracción con PHP Interfaces y Polimorfismo Documentación oficial sobre traits en PHP Uso de Traits en Laravel Creación de macros en PHP usando traits, métodos estáticos y __call En esta lección pondrás en práctica los conocimientos adquiridos durante este curso sobre métodos estáticos, el método mágico __call y Traits en PHP para aprender a agregar nuevos métodos a una clase por medio de un concepto llamado macros que es usado en frameworks como Laravel o Symfony. Notas Por Clemir Rondón La primera opción para agregar nuevos métodos a una clase que no podemos modificar directamente (porque, por ejemplo, pertenece a un paquete de terceros) es crear una clase con el mismo nombre y que extendida la clase que queremos modificar, haciendo uso de los namespace y la herencia simple como ya hemos aprendido. Recuerda siempre que toda clase que se encuentre en el directorio vendor, generado por Composer, no debe ser modifica directamente porque los cambios se borrarán al hacer composer update . El concepto de macro explicado en está lección es un conjunto de instrucciones que realizan alguna tarea, que Laravel implementa como una manera de poder dar opción a los desarrolladores para que puedan agregar nuevas funcionalidades o métodos dinámicos a clases como Collection, Filesystem, Arr, Translator, etc. del framework sin necesidad de tener que extender dichas clases. Closure es una clase de PHP para representar las funciones anónimas por tanto para llamarla correctamente se debe importar la clase: 1 use Closure; o usar la barra invertida \ para indicarle a PHP que la busque en el namespace global, de esta manera \Closure Con la función de PHP call_user_func_array se llama a un callback o llamada de retorno que es enviada como primer parámetro a la función y el segundo parámetro un array que se pasará como argumentos al callback. Si quieres repasar el concepto del método mágico __call que ya vimos en este curso consulta la lección relacionada. La clase BadMethodCallException de PHP trabaja junto con el método mágico __call para lanzar una excepción cuando el método llamado por el callback no existe o no es válido o falta alguno de los argumentos. Al igual que Closure es una clase de PHP por tanto debes agregar la barra invertida para que PHP la busque es su nombre de espacio global. Usando un trait para agregar toda funcionalidad de macro que vimos está lección podemos reutilizar el código y hacer que cualquier otra clase del proyecto se pueda crear métodos dinámicos, tan solo agregando el macro a la clase como se vio en la lección anterior. Con el método Closure::bindTo estamos atando el Closure que usaremos en el método mágico __call al ámbito de la clase actual. Actividad Puedes revisar el trait Macroable de Laravel y ver que clases del framework lo usan. Material relacionado Propiedades y métodos estáticos Llamadas a métodos mágicos con __call y __callStatic en PHP Traits en PHP PHP Traits en Laravel 5.1 Instalación y uso de componentes de terceros con Composer Cuando escribes aplicaciones con PHP no es necesario que escribas todo el código por ti mismo, pues hay problemas comunes que ya tienen una o varias soluciones y no tiene sentido que dediques parte de tu tiempo a recrear las mismas soluciones desde cero. En vez de esto, podemos apoyarnos en paquetes de terceros y por medio de Composer (el manejador de dependencias de PHP) podemos integrarlos a nuestra aplicación de forma sencilla como veremos en la lección de hoy. Notas Por Clemir Rondón. Walter White (personaje principal de Breaking Bad) nació el 7 de Septiembre de 1959.Esperamos que te haya gustado esta nueva parte del curso, si tienes dudas o quieres aprender algo nuevo, por favor escríbenos en el canal #php de nuestro Slack: Recuerda que estamos usando propiedades y métodos mágicos al estilo de Eloquent como aprendimos con el uso del método mágico __get y llamadas dinámicas a métodos. Composer a parte de ayudarnos con la autocarga de clases como vimos en una lección anterior también nos permite la gestión de dependencias, es decir, nos ayuda a instalar los paquetes de terceros y sus respectivas dependencias, sin necesidad que nosotros intervengamos en el proceso. Con esto podremos usar casi sin esfuerzo las clases y métodos de componentes de terceros que además serán cargados de forma automática, gracias nuevamente al autoloader incluido con Composer. Para trabajar con Composer como manejar de dependencias en un proyecto de PHP (como vimos en la lección Autocarga de clases con Composer y PSR-4) debemos: Instalar Composer ya sea local o globalmente. Agregar el archivo composer.json con los datos del proyecto (autoload, paquetes, etc.). Agregar la siguiente línea en el archivo inicial de tu proyecto: require '/vendor/autoload.php'; autoload.php es el archivo creado por Composer que se encarga de la autocarga de clases de tu proyecto y las dependencias del mismo. Las 2 maneras de instalar un paquete con Composer son: En la consola: ejecutando composer require nombre-vendor/nombre-paquete, donde nombre-vendor es el nombre del grupo, empresa o desarrollador que creó el paquete y nombre-paquete es el nombre del proyecto en sí. Por ejemplo: $ composer require nesbot/carbon Agregando manualmente en el archivo composer.json en la opción 'require' el nombre del paquete y la versión a instalar. Por ejemplo: { "require": { "nesbot/carbon": "~1.18" } } y luego ejecutar composer install en la consola. Para conocer cómo Composer maneja las versiones puedes consultar Controla las versiones de los componentes de tu proyecto con Composer Al instalarse el paquete en un proyecto con Composer, éste se encontrará en directorio vendor. Algo importante a tomar en cuenta es que no debes modificar los archivos que se encuentren en ese directorio puesto que al volver a ejecutar composer install o composer update se perderán los cambios hechos. Si tienes dudas revisa sobre Diferencias entre composer install y composer update. Carbon es un componente que extiende la clase de PHP DateTime y su documentación oficial se encuentra en http://carbon.nesbot.com/docs/. También en Styde hemos hablado de este componente: Manipular fechas con el componente Carbon en Laravel 5 Para usar las clases o archivos de un paquete instalado con Composer debemos importar la clase a utilizar usando la sentencia use de PHP con el nombre de la clase incluyendo su namespace. Por ejemplo para usar Carbon: use Carbon\Carbon y de esta manera empezar a usar el componente para resolver lo que necesitamos. El principal repositorio donde puedes encontrar paquetes o componentes instalables con Composer que puedes usar en tus proyectos de PHP es Packagist.org. Además, en Styde tenemos el Curso de creación de componentes para PHP y Laravel donde aprenderás a crear tus propios componentes y la última parte se dedica a explicar cómo crearlos y publicarlos en Packagist. Material relacionado Composer, el gestor de dependencias de PHP Autocarga de clases con Composer y PSR-4 Manipular fechas con el componente Carbon en Laravel 5 Desarrollo de clases y métodos con pruebas automatizadas Cuando desarrollamos debemos asegurarnos que las clases y métodos que creamos funcionen correctamente, por lo tanto, es necesario probar siempre el código escrito pero en algunos casos no es tan fácil o resulta una tarea tediosa de hacer de forma manual. En la lección de hoy te explicaremos cómo desarrollar usando pruebas automatizadas con PHPUnit en minutos. Notas Por Clemir Rondón. En este curso trabajamos con la clase Str para convertir cadenas como por ejemplo de full_name a FullName durante las lecciones sobre métodos mágicos en PHP, es decir, a partir de la lección Uso de los métodos mágicos __get, __set, __isset y __unset con PHP sin embargo, en ese momento no nos detuvimos a ver detalladamente cómo estaba desarrollada. La función de PHP ucfirst convierte el primer carácter de una cadena a mayúsculas. Para crear pruebas podemos hacer uso del componente PHPUnit que podemos instalar con Composer como vimos en la lección anterior de esta manera agregando al composer.json: { "require-dev": { "phpunit/phpunit": "5.5.*" } } Lo agregamos en la opción require-dev puesto que es un componente que solo necesitamos durante el desarrollo del proyecto. y luego ejecutamos composer update en la consola para que se instale. El siguiente paso es crear el directorio tests en la raíz del proyecto donde colocaremos las clases que se encargarán de probar el código automáticamente. Además, estas clases deben extender de la clase PHPUnit\Framework\TestCase del componente PHPUnit Para correr las pruebas ejecutamos en consola: vendor/bin/phpunit tests/ 1 vendor/bin/phpunit tests/ donde tests/ es el directorio donde le indicamos a PHPUnit que busque las pruebas a ejecutar. Adicionamente, puedes agregar alguna otra opción disponible como por ejemplo –colors para indicar que el resultado use colores. Las Aserciones o Assertions son un conjunto de métodos que nos permiten ejecutar las pruebas de una mejor manera y proporcionando resultados con mayor información sobre la ejecución de prueba. En PHPUnit disponemos de un gran conjunto de aserciones que podemos ver en su documentación oficial. Para esta lección usamos el método assertSame() que acepta como argumentos el valor esperado, el valor que está produciendo nuestro código y opcionalmente un mensaje que se mostrará en caso que falle la aserción. Se recomienda que cada método que creamos en una clase de prueba tenga un nombre descriptivo de lo que se está evaluando, así como el nombre de la clase. Con la función de PHP ucwords podemos convertir a mayúsculas el primer carácter de cada palabra de una cadena y con la función str_replace reemplazamos todas las apariciones de la cadena que enviamos como primer argumento con la cadena de reemplazo que enviamos como segundo argumento en la cadena a evaluar enviada como tercer argumento. Bien, con esta lección culminamos el curso Curso de programación orientada a objetos con PHP esperamos que hayas disfrutado del contenido y te invitamos a continuar preparándote con el Curso de creación de componentes para PHP y Laravel donde aprenderás a crear tus propios componentes y conocerás temas más avanzados como la inyección de dependencias, patrones de diseño, el uso de pruebas unitarias, entre otros. Material relacionado Las pruebas automatizadas son perfectas para tus proyectos personales Cómo escribir pruebas unitarias y de aplicación y por qué es importante Introducción al diseño de clases con pruebas unitarias en PHPUnit Array Access en PHP – Parte 1 Con PHP, es posible interactuar con un objeto como si éste fuese un array, y esto lo podemos lograr de forma muy sencilla implementando la interfaz ArrayAccess. A partir de esta lección aprenderás cómo usar esta interfaz de forma detallada, para agregar esta funcionalidad a la clase Model que hemos venido usando. Además vamos a aplicar los conocimientos de la lección 27 (Desarrollo de clases y métodos con pruebas automatizadas) para crear esta funcionalidad con el apoyo de una prueba unitaria escrita en PHPUnit Repositorio Ver el código de esta lección en GitHub Si cometes un error en el código, cuando ejecutes la prueba es posible que no lo veas, y esto no es lo más óptimo para depurar y corregir errores. Así que puedes solucionar esto agregando este archivo bootstrap.php en la raíz de tu proyecto y haciendo referencia a éste desde el archivo phpunit.xml. De esta forma podrás ver los errores que arroje PHP (en caso de que exista alguno) directamente desde tu terminal. Notas Por Clemir Rondón. Las correcciones que debes hacer en la lección Desarrollo de clases y métodos con pruebas automatizadas son: Agregar el archivo phpunit.xml, el cual es el archivo de configuración que permite definir que acciones se realizarán al ejecutar las pruebas. Además, puedes configurar tu entorno de desarrollo para crear un alias del comando de la ejecución de las pruebas: Alias de comandos para la consola Windows/Linux/Mac Por otro lado, ArrayAccess es una interfaz predefinida de PHP como Iterator e IteratorAgreggate que vimos en la lección Iteración de objetos en PHP, la cual nos permite dar acceso a objetos como si fuesen arrays. Para trabajar con ArrayAccess la clase debe implementar dicha interfaz, como por ejemplo: class User implements ArrayAccess { } Sin embargo, como toda interfaz es un contrato, tenemos que además implementar cuatro métodos que nos permitirán simular trabajar con objetos usando la sintaxis de array: abstract public boolean offsetExists ( mixed $offset ) que se ejecutará cuando queremos comprobar si existe un índice. abstract public mixed offsetGet ( mixed $offset ) que nos permitirá recuperar el valor de una clave o $offset. abstract public void offsetSet ( mixed $offset , mixed $value ) para asignar una clave $offset con valor $value al objeto. abstract public void offsetUnset ( mixed $offset ) que destruye un $offset o clave. Para crear una nueva prueba usando PHPUnit, creamos un nuevo archivo con nombre ArrayAccessTest.php dentro del directorio test Revisa cómo configurar PHPUnit en un proyecto de PHP con la lección: Desarrollo de clases y métodos con pruebas automatizadas Además, crea la clase llamada ArrayAccessTest sin olvidar extender de la clase PHPUnit\Framework\TestCase que nos proporciona los métodos para escribir las pruebas. Al ser la clase Model una clase abstracta no la podemos instanciar directamente, es por ello que para probar el uso de la interfaz ArrayAccess creamos una clase UserTest que extienda de la clase Model, la cual si podemos instanciar en la prueba. Para repasar sobre clases abstractas consulta la lección Herencia y abstracción con PHP Puesto que el método offsetGet a implementar nos permite obtener el valor de una propiedad, en ese método debemos colocar la manera con el objeto permite acceder a una propiedad de forma dinámica y esto es a través del método getAttribute visto en las lecciones: Uso de los métodos mágicos __get, __set, __isset y __unset con PHP y Llamadas dinámicas a métodos con PHP Laravel, como framework de PHP, implementa la interfaz ArrayAccess en algunas de sus clases para acceder a las propiedad como si fuese un array como por ejemplo: Illuminate\Http\Request, y es por ello que para poder acceder a las variables de la petición de esta manera $request['name'] que es equivalente a $request->input('name') o $request->name. Otras clases que implementan la interfaz son Illuminate\Database\Eloquent\Model, Illuminate\Support\Collection, Illuminate\Container\Container, etc. Material relacionado Curso básico de Terminal Desarrollo de clases y métodos con pruebas automatizadas Iteración de objetos en PHP Llamadas dinámicas a métodos con PHP Array Access en PHP – Parte 2 En esta segunda parte acerca de la interfaz ArrayAccess estudiaremos cómo implementar los métodos offsetExists, offsetSet y offsetUnset para completar la funcionalidad de acceso de array en nuestra clase de prueba. Además seguiremos utilizando PHPUnit y pruebas automatizadas para comprobar nuestro progreso en el desarrollo de la funcionalidad y veremos algunos tips sobre cómo nombrar y especificar los métodos de nuestras pruebas unitarias. Repositorio Ver el código de esta lección en GitHub Compatibilidad con array_key_exists Es importante tener en cuenta que la función array_key_exists no funciona con objetos que tengan la interfaz ArrayAccess, solamente con arrays. Puedes comprobarlo tú mismo en una prueba unitaria con el siguiente código: <?php $user = new UserTest(['name' => 'Duilio Palacios']); $this->assertFalse(array_key_exists('name', $user)); $user = ['name' => 'Duilio']; $this->assertTrue(array_key_exists('name', $user)); Uso de ArrayAccess en el framework Laravel Es interesante notar que los modelos de Eloquent implementan la interfaz ArrayAccess, por lo que lo que has aprendido en estas 2 últimas lecciones es posible usarlo en Eloquent. Sin embargo es más común ver el acceso con propiedades mágicas: <?php $user = new User(['name' => 'Duilio']); echo $user->name; //Esto funciona y es muy comun echo $user['name']; //Pero esto tambien va a funcionar También es posible usar ArrayAccess en el contenedor de inyección de dependencias de Laravel: <?php $app = app(); $app['message'] = new Message; //es equivalente a: $app->bind('message', function () { return new Message; }); $app['message']->show('Styde Rules!'); //es equivalente a: $app->make('message')->show('Styde Rules!'); En la próxima lección vamos a aprender sobre clases anónimas y a mover el código creado hasta ahora a nuestra clase abstracta Model. Clases anónimas en PHP Como vimos en las lecciones sobre la interfaz ArrayAccess no podíamos probar la implementación de esta interfaz directamente con la clase Model. Debido a que la clase Model por ser una clase abstracta no se puede instanciar directamente. Cuando esto ocurre, la solución más común es crear una clase que extienda de la clase abstracta. Sin embargo a partir de PHP 7 podemos sacarle provecho a un nuevo tipo de clase llamada Clase Anónima y esto es lo que vamos a aprender en esta lección: el uso de clases anónimas en pruebas unitarias. Repositorio Ver el código de esta lección en GitHub Notas Por Clemir Rondón. Para hacer que todas las clases que extiendan de la clase Model tengan la facultad de poder acceder a sus propiedades como si fuera un array, debemos trasladar lo desarrollado en las lecciones anteriores a la clase Model, esto es: que implemente la interfaz ArrayAccess y además tenga los 4 métodos de la interfaz. Las clases anónimas fueron incluidas en la versión 7 de PHP y permiten crear objetos de una clase abstracta, o una interfaz, o de cualquier otra clase de forma sencilla. Lo usual para instanciar una clase es $objeto = new UnaClaseAbstracta(); pero cuando la clase que queremos instanciar no nos permite hacerlo directamente o nos interesa crear otras clases para poder instanciarlas podemos hacer lo siguiente: $objeto = new class ($foo, $bar) extends UnaClaseAbstracta implements UnaInterfaz {} donde new class son las palabras reservadas para indicar que queremos crear una nueva clase e instanciarla, pero sin indicar el nombre de la clase, tal y como sucede con las funciones anónimas. ($foo, $bar) son las variables que pasamos a través del constructor, si no es necesario simplemente se omiten. Luego podemos extender o implementar otras clases en caso de ser requerido. Como por ejemplo en nuestro caso: $user = new class ($attribute) extends Model { }; De igual forma, podemos sobrescribir atributos y métodos de las otras clases a las que extiende o implementan. Puedes hacer la prueba en el navegador con el siguiente código: <?php require "../vendor/autoload.php"; use Styde\User; $user = new User([ 'first_name' => 'Walter', 'last_name' => 'White', 'birthDate' => '07/09/1959', ]); echo "{$user['full_name']} tiene {$user['age']} años"; Esperamos que te haya gustado esta nueva parte del curso, si tienes dudas o quieres aprender algo nuevo, por favor escríbenos en el canal #php de nuestro Slack: