Académique Documents
Professionnel Documents
Culture Documents
Buscando artculos de calidad para compartir con ustedes, encuentro un reciente y magnfico artculo (Object-Oriented PHP for Beginners) extrado del libro Pro PHP and jQuery, que a continuacin se los muestro, pero en nuestro idioma. He incluido los ejemplos online, para que podamos apreciarlos mejor. Y es que, para muchos programadores, la programacin orientada a objetos es un concepto aterrador, con una sintaxis muy complicada y otros temores. As pues, en este artculo aprenderemos los conceptos detrs de la Programacin Orientada a Objetos con PHP (PHP OOP), un estilo de programacin en el que las acciones relacionadas son agrupadas en clases para facilitar la creacin de un cdigo ms compacto y efectivo...
Fotos de Instant Jefferson y John Wardell De buenas a primeras, hay una confusin en OOP: Desarrolladores experimentados empiezan a hablar sobre objetos y clases, y estos parecen ser trminos intercambiables. Esto no es as, sin embargo, la diferencia puede ser un poco complicada de entender a primera vista. Una clase, por ejemplo, es como el plano de una casa. Define la forma de la casa en un papel, y establece las conexiones entre las diferentes partes de la casa claramente. An cuando la casa todava no existe. Un objeto, entonces, es como una casa, construida de acuerdos al plano. Los datos que se almacenan en el objeto son como la madera, cables y concreto que componen la casa: Si la casa se hubiera montado sin un plano, slo sera una pila de cosas. Sin embargo, cuando todo se junta, se convierte en una organizada y til casa. Las clases son la estructura de los datos y acciones, y usan esta informacin para construir objetos. Ms de un objeto puede ser construido de la misma clase y al mismo tiempo; estos objetos son independientes los unos de los otros. Continuando con nuestra construccin analgica, esto es similar a un condominio que puede ser construido con el mismo plano: 150 casas distintas, que lucen igual, pero que tienes diferentes familias y decoraciones por dentro.
Despus de crear la clase, una nueva clase puede ser instanciada y almacenada en una variable usando la palabra new:
<?php $obj = new MyClass; ?>
Prueba este proceso, colocando el siguiente cdigo en un nuevo archivo llamado test.php (en tu localhost):
<?php class MyClass { // Los mtodos y propiedades de la clase van aqu } $obj = new MyClass; var_dump($obj); ?>
La palabra public determina la visibilidad de la propiedad, aprenders un poco ms sobre esto en unos momentos. Luego, la propiedad es llamada usando la sintaxis estndar de una variable, y un valor le es asignado (Aunque, las propiedades de una clase no necesitan de un valor inicial). Para leer esta propiedad e imprimirla en el navegador, vamos a hacer referencia al objeto que la contiene:
<?php echo $obj->prop1; ?>
Debido a que pueden existir mltiples instancias de una clase, si el objeto no es referenciado individualmente, el script ser incapaz de determinar cual objeto leer. El uso de la flecha (->) es un operador OOP que accede al contenido de propiedades y mtodos de un objeto dado. Modifica el script en test.php para imprimir la propiedad en vez de volcar toda la clase, modificando el cdigo como se muestra:
<?php class MyClass { public $prop1 = "Soy la propiedad de una clase!"; } $obj = new MyClass; echo $obj->prop1; // Imprime la propiedad ?>
Nota: OOP permite a los objetos referenciarse a si mismos utilizando $this. Cuando trabajamos con un mtodo, usa $this de la misma manera que usaras el nombre del objeto fuera de la clase. Para usar estos mtodos, hay que llamarlos como a cualquier funcin; pero primero, hay que referenciar al objeto que lo contiene. Imprime la propiedad de MyClass, cambia su valor e imprmelo otra vez, realizando las modificaciones que a continuacin se indican.
<?php class MyClass { public $prop1 = "Soy la propiedad de una clase!"; public function setProperty($newval) { $this->prop1 = $newval; }
public function getProperty() { return $this->prop1 . "<br />"; } } $obj = new MyClass; echo $obj->getProperty(); // Imprime el valor de la propiedad $obj->setProperty("Soy el nuevo valor de la propiedad!"); // Establece un valor nuevo echo $obj->getProperty(); // Imprime nuevamente para ver el cambio ?>
El poder de la programacin orientada a objetos se hace evidente cuando se utilizan varias instancias de la misma clase.
<?php class MyClass { public $prop1 = "Soy la propiedad de una clase!"; public function setProperty($newval) { $this->prop1 = $newval; } public function getProperty() { return $this->prop1 . "<br />"; } } // Create two objects $obj = new MyClass; $obj2 = new MyClass; // Get the value of $prop1 from both objects echo $obj->getProperty(); echo $obj2->getProperty(); // Set new values for both objects $obj->setProperty("Soy el nuevo valor de la propiedad!"); $obj2->setProperty("Yo pertenezco a la segunda instancia!"); // Output both objects' $prop1 value echo $obj->getProperty(); echo $obj2->getProperty(); ?>
Como puedes ver, OOP mantiene los objetos como entidades separadas, lo que facilita la separacin de diferentes piezas de cdigo en pequeos, pero relacionados, paquetes. De esta manera ya tenemos claro lo que es la programacin orientada a objetos, el concepto de Clases y Objetos, la distincin entre ellos, y las definiciones de propiedades y mtodos. Todo breve y claramente explicado. Ahora vamos a conocer sobre los mtodos mgicos y observar como funcionan los constructores y destructores, como podemos convertir un objeto en un string y como usar las herencias. Y todo explicado de una manera sencilla y prctica...
public function getProperty() { return $this->prop1 . "<br />"; } } // Crea un nuevo objeto $obj = new MyClass; // Imprime el valor de $prop1 echo $obj->getProperty(); // Imprime un mensaje al final del archivo echo "Fin del archivo.<br />"; ?>
Toma nota: __CLASS__ retorna el nombre de la clase que fue llamada; esto es conocido como una constante mgica. Hay una amplia gama de constantes mgicas disponibles, puedes leer un poco ms de ellas en el manual de PHP. Ahora recarga el archivo en tu navegador y vers el siguiente resultado:
La clase "MyClass" fue iniciada! Soy la propiedad de una clase! Fin del archivo.
Para llamar a una funcin cuando el objeto se destruye, podemos hacer uso del mtodo mgico __destruct(). Este es muy til para la limpieza de una clase (cerrar la conexin a una base de datos, por ejemplo). Continuando con nuestro ejemplo, imprimiremos un mensaje cuando el objeto es destruido. Para ello, definimos el mtodo __destruct() en MyClass:
<?php class MyClass { public $prop1 = "Soy la propiedad de una clase!"; public function __construct() { echo 'La clase "', __CLASS__, '" fue iniciada!<br />'; } public function __destruct() { echo 'La clase "', __CLASS__, '" fue destruida!<br />'; } public function setProperty($newval) { $this->prop1 = $newval; } public function getProperty()
{ return $this->prop1 . "<br />"; } } // Crea un nuevo objeto $obj = new MyClass; // Imprime el valor de $prop1 echo $obj->getProperty(); // Imprime un mensaje al final del archivo echo "Fin del archivo.<br />"; ?>
Con el destructor definido, recarga la pgina de ejemplo y deber aparecerte el siguiente resultado:
La clase "MyClass" fue iniciada! Soy la propiedad de una clase! Fin del archivo. La clase "MyClass" fue destruida!
PHP libera todos los recursos, cuando llega al final del archivo. Para entender mejor como funciona el destructor, ahora destruye manualmente el objeto usando la funcin unset():
<?php class MyClass { public $prop1 = "Soy la propiedad de una clase!"; public function __construct() { echo 'La clase "', __CLASS__, '" fue iniciada!<br />'; } public function __destruct() { echo 'La clase "', __CLASS__, '" fue destruida!<br />'; } public function setProperty($newval) { $this->prop1 = $newval; } public function getProperty() { return $this->prop1 . "<br />"; } } // Crea un nuevo objeto
$obj = new MyClass; // Imprime el valor de $prop1 echo $obj->getProperty(); // Destruye el objeto unset($obj); // Imprime un mensaje al final del archivo echo "Fin del archivo.<br />"; ?>
// Crea un nuevo objeto $obj = new MyClass; // Imprime el objeto como un string echo $obj; // Destruye el objeto unset($obj); // Imprime un mensaje al final del archivo echo "Fin del archivo.<br />"; ?>
$obj = new MyClass; // Imprime el objeto como un string echo $obj; // Destruye el objeto unset($obj); // Imprime un mensaje al final del archivo echo "Fin del archivo.<br />"; ?>
En este caso, el intentar convertir el objeto en un string nos devuelve una llamada al mtodo getProperty(). Carga el script en un archivo y prubalo en el navegador. Vers que aparece lo siguiente:
La clase "MyClass" fue iniciada! Usando el mtodo toString: Soy la propiedad de una clase! La clase "MyClass" fue destruida! Fin del archivo.
Tip: Adicionalmente a los mtodos mgicos comentados en este artculo, existen ms de ellos. Para una revisin completa de los mtodos mgicos, puedes darle una mirada a esta pgina del manual de PHP.
{ $this->prop1 = $newval; } public function getProperty() { return $this->prop1 . "<br />"; } } class MyOtherClass extends MyClass { public function newMethod() { echo "Desde un nuevo mtodo en " . __CLASS__ . ".<br />"; } } // Crea un nuevo objeto $newobj = new MyOtherClass; // Imprime el objeto como un string echo $newobj->newMethod(); // Usa el mtodo de la clase padre echo $newobj->getProperty(); ?>
public function __toString() { echo "Usando el mtodo toString: "; return $this->getProperty(); } public function setProperty($newval) { $this->prop1 = $newval; } public function getProperty() { return $this->prop1 . "<br />"; } } class MyOtherClass extends MyClass { public function __construct() { echo "Un nuevo constructor en " . __CLASS__ . ".<br />"; } public function newMethod() { echo "Desde un nuevo mtodo en " . __CLASS__ . ".<br />"; } } // Crea un nuevo objeto $newobj = new MyOtherClass; // Imprime el objeto como un string echo $newobj->newMethod(); // Usa el mtodo de la clase padre echo $newobj->getProperty(); ?>
<?php class MyClass { public $prop1 = "Soy la propiedad de una clase!"; public function __construct() { echo 'La clase "', __CLASS__, '" fue iniciada!<br />'; } public function __destruct() { echo 'La clase "', __CLASS__, '" fue destruida!<br />'; } public function __toString() { echo "Usando el mtodo toString: "; return $this->getProperty(); } public function setProperty($newval) { $this->prop1 = $newval; } public function getProperty() { return $this->prop1 . "<br />"; } } class MyOtherClass extends MyClass { public function __construct() { parent::__construct(); // LLama al constructor de la clase padre echo "Un nuevo constructor en " . __CLASS__ . ".<br />"; } public function newMethod() { echo "Desde un nuevo mtodo en " . __CLASS__ . ".<br />"; } } // Crea un nuevo objeto $newobj = new MyOtherClass; // Imprime el objeto como un string echo $newobj->newMethod(); // Usa el mtodo de la clase padre echo $newobj->getProperty(); ?>
Esto muestra el resultado de ambos: Del constructor de la clase padre y del constructor de la nueva clase:
La clase "MyClass" fue iniciada! Un nuevo constructor en MyOtherClass. Desde un nuevo mtodo en MyOtherClass. Soy la propiedad de una clase! La clase "MyClass" fue destruida!
En este artculo hemos visto, rpida y claramente, cmo funcionan los mtodos mgicos en OOP y lo prctico que pueden sernos, cmo usar los constructores y destructores y un mtodo que nos ser muy til para imprimir nuestros objetos. Tambin hemos comprendido el concepto de herencias y de que maneras podemos aplicarla. Ahora vamos a conocer sobre la visibilidad de los mtodos y propiedades, cmo utilizar DocBlocks para documentar nuestros scripts y vamos a comparar la programacin orientada a objetos con la procedimental, todo con ejemplos sencillos y fciles de entender...
<?php class MyClass { public $prop1 = "Soy la propiedad de una clase!"; public function __construct() { echo 'La clase "', __CLASS__, '" fue iniciada!<br />'; } public function __destruct() { echo 'La clase "', __CLASS__, '" fue destruida!<br />'; } public function __toString() { echo "Usando el mtodo toString: "; return $this->getProperty(); } public function setProperty($newval) { $this->prop1 = $newval; } protected function getProperty() { return $this->prop1 . "<br />"; } } class MyOtherClass extends MyClass { public function __construct() { parent::__construct(); echo "Un nuevo constructor en " . __CLASS__ . ".<br />"; } public function newMethod() { echo "Desde un nuevo mtodo en " . __CLASS__ . ".<br />"; } } // Crea un nuevo objeto $newobj = new MyOtherClass; // Falla al usar el mtodo protegido
parent::__construct(); echo "Un nuevo constructor en " . __CLASS__ . ".<br />"; } public function newMethod() { echo "Desde un nuevo mtodo en " . __CLASS__ . ".<br />"; } public function callProtected() { return $this->getProperty(); } } // Crea un nuevo objeto $newobj = new MyOtherClass; // Usa el mtodo protegido desde un mtodo pblico echo $newobj->callProtected(); ?>
{ echo 'La clase "', __CLASS__, '" fue destruida!<br />'; } public function __toString() { echo "Usando el mtodo toString: "; return $this->getProperty(); } public function setProperty($newval) { $this->prop1 = $newval; } private function getProperty() { return $this->prop1 . "<br />"; } } class MyOtherClass extends MyClass { public function __construct() { parent::__construct(); echo "Un nuevo constructor en " . __CLASS__ . ".<br />"; } public function newMethod() { echo "Desde un nuevo mtodo en " . __CLASS__ . ".<br />"; } public function callProtected() { return $this->getProperty(); } } // Crea un nuevo objeto $newobj = new MyOtherClass; // Usa el mtodo protegido desde un mtodo pblico echo $newobj->callProtected(); ?>
} private function getProperty() { return $this->prop1 . "<br />"; } public static function plusOne() { return "La cuenta es " . ++self::$count . ".<br />"; } } class MyOtherClass extends MyClass { public function __construct() { parent::__construct(); echo "Un nuevo constructor en " . __CLASS__ . ".<br />"; } public function newMethod() { echo "Desde un nuevo mtodo en " . __CLASS__ . ".<br />"; } public function callProtected() { return $this->getProperty(); } } do { // Llama al mtodo plusOne sin instanciar MyClass echo MyClass::plusOne(); } while ( MyClass::$count < 10 ); ?>
Nota: Cuando se acceden a las propiedades estticas, el signo de dlar($) va despus del operador de resolucin de alcance. Cuando cargues el script en el navegador, vers lo siguiente:
La La La La La cuenta cuenta cuenta cuenta cuenta es es es es es 1. 2. 3. 4. 5.
La La La La La
es es es es es
6. 7. 8. 9. 10.
@author: El autor del elemento actual (que podra ser una clase, un archivo, un mtodo o cualquier otro trozo de cdigo) se enumera con esta etiqueta. Mltiples etiquetas de autor pueden ser usadas en el mismo DocBlock si hay ms de un autor. El formato para el nombre de autor es John Doe <john.doe@email.com>. @copyright: Esto significa el ao del copyright y el nombre del titular del copyright del elemento actual. El formato es 2010 Titular del Copyright. @license: Esto enlaza a la licencia del elemento actual. El formato para la informacin de la licencia es http://www.example.com/path/to/license.txt Nombre de la licencia. @var: Esto define el tipo y descripcin de una variable o propiedad de una clase. El formato es tipo y descripcin del elemento. @param: Este tag muestra el tipo y descripcin del parmetro de una funcin o mtodo. El formato es el tipo $nombre_del_elemento y descripcin del elemento.
@return: El tipo y descripcin del valor de retorno de una funcin o mtodo es provisto por este tag. El formato es el tipo y descripcin del elemento. Una clase de ejemplo comentada con DocBlock podra tener este aspecto:
<?php /** * Una clase sencilla * * Esta es una larga descripcin de la clase, * la cual ocupar tantas lineas como sea necesario. Esta * no ser necesaria, mientras la descripcin corta sea * necesaria. * * Esta descripcin puede dividirse en varios prrafos si la * descripcin merece mucha verborrea. * * @author Jason Lengstorf <jason.lengstorf@ennuidesign.com> * @copyright 2010 Ennui Design * @license <a href="http://www.php.net/license/3_01.txt" title="http://www.php.net/license/3_01.txt">http://www.php.net/license/3_ 01.txt</a> PHP License 3.01 */ class SimpleClass { /** * Una variable pblica * * @var string almacena data para la clase */ public $foo; /** * Define $foo con un nuevo valor al instanciarse la clase * * @param string $val un valor requerido por la clase * @return void */ public function __construct($val) { $this->foo = $val; } /** * Multiplica dos nmeros enteros * * Acepta un par de enteros y devuelve el * producto de ambos. *
* @param int $bat un nmero a ser multiplicado * @param int $baz un nmero a ser multiplicado * @return int el propudcto de los dos parmetros */ public function bar($bat, $baz) { return $bat * $baz; } } ?>
Una vez que observes la clase anterior, los beneficios de DocBlock te parecern evidentes: todo est definido claramente para que el siguiente desarrollador pueda captar el cdigo y nunca tener que preguntarse lo que un fragmento de cdigo hace o lo que debe contener.
Este es el enfoque procedimental de nuestro ejemplo: Cuando los ejecutes, el cdigo nos mostrar lo siguiente:
<?php function changeJob($person, $newjob) { $person['job'] = $newjob; // Cambiar el trabajo de la persona return $person; }
function happyBirthday($person) { ++$person['age']; // Aade 1 a la edad de la persona return $person; } $person1 = array( 'name' => 'Tom', 'job' => 'Button-Pusher', 'age' => 34 ); $person2 = array( 'name' => 'John', 'job' => 'Lever-Puller', 'age' => 41 ); // Imprime los valores iniciales de las personas echo "<pre>Person 1: ", print_r($person1, TRUE), "</pre>"; echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>"; // Tom obtiene un ascenso y cumple aos $person1 = changeJob($person1, 'Box-Mover'); $person1 = happyBirthday($person1); // John slo cumple aos $person2 = happyBirthday($person2); // Imprime los nuevos valores de las personas echo "<pre>Person 1: ", print_r($person1, TRUE), "</pre>"; echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>"; ?>
Person 1: Array ( [name] => Tom [job] => Box-Mover [age] => 35 ) Person 2: Array ( [name] => John [job] => Lever-Puller [age] => 42 )
Aunque este cdigo no sea necesariamente malo, hay que tener varias cosas en cuenta mientras se programa. El array de atributos de la persona afectada deben ser pasados y devueltos en cada llamada a la funcin, lo que deja un margen de error. Para limpiar este ejemplo, sera conveniente dejarle menos cosas al desarrollador. Slo la informacin esencial para la operacin debe necesitar ser transmitida a las funciones. Aqu es donde la POO avanza y puede ayudarte a limpiar las cosas.
El enfoque POO
// Crea dos personas nuevas $person1 = new Person("Tom", "Button-Pusher", 34); $person2 = new Person("John", "Lever Puller", 41); // Imprime su punto de inicio echo "<pre>Person 1: ", print_r($person1, TRUE), "</pre>"; echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>"; // Da a Tom un ascenso y un cumpleaos $person1->changeJob("Box-Mover"); $person1->happyBirthday(); // John slo obtiene un ao ms $person2->happyBirthday(); // Imprime los valores finales echo "<pre>Person 1: ", print_r($person1, TRUE), "</pre>"; echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>"; ?>
Hay unos bits adicionales involucrados en el enfoque orientado a objetos, pero luego de que la clase es definida, crear y modificar personas es muy sencillo; la informacin de la persona no necesitar ser pasada o devuelta de los mtodos y slo la informacin escencial es transmitida a cada mtodo. En menor medida, esta diferencia puede no parecer mucha, pero conforme las aplicaciones crezcan en tamao, POO reducir significativamente la carga de trabajo, si se aplica correctamente. Consejo: No todo tiene que ser orientado a objetos. Una funcin rpida que se ocupa de algo pequeo en un lugar o dentro de una aplicacin no necesita tener que estar envuelta en una clase. Utiliza tu juicio para decidir que enfoque utilizar, el orientado a objetos o el procedimental.
Tener cada clase en un archivo separado, tambin permite que el cdigo sea ms porttil y ms fcil de reutilizar en nuevas aplicaciones, sin necesidad de copiar y pegar.
Si un array de informacin obtiene un nuevo atributo, el enfoque procedimental podr requerir (en el peor de los casos) que el nuevo atributo se aada en cada funcin que utilice el array. Una aplicacin POO podra actualizarse con la misma facilidad con la que se agrega una nueva propiedad y, a continuacin, aade los mtodos que tienen que ver con dicha propiedad. Muchos de los beneficios cubiertos en esta serie de artculos son el producto de la POO en combinacin con las prcticas de desarrollo DRY. Definitivamente, es posible crear un cdigo procedimental que no nos de pesadillas; pero, es igualmente posible crear un cdigo orientado a objetos terrible. Por eso, hemos intentado demostrar como combinar los buenos hbitos de programacin relacionados con la programacin orientada a objetos para generar un cdigo limpio, que sea fcil de leer y mantener.
Conclusiones
En este momento, debes sentirte cmodo con el estilo de la programacin orientada a objetos. Aprender POO es una muy buena forma de llevar tu programacin al siguiente nivel. Cuando se implementa correctamente, la programacin orientada a objetos te ayudar a leer y mantener el cdigo fcilmente; y te salvar (y a los desarrolladores que trabajan contigo) de horas extra de trabajo. Estas metido en algo que no ha sido tratado en esta serie de artculos? Ya utilizas la POO y tienes algunos ejemplos para principiantes? Comprtelos en los comentarios!