Spanish (Español) translation by Elías Nicolás (you can also view the original English article)
No se puede negar que la nuestra es una industria increíblemente difícil. ¿Alguna vez has considerado aprender un segundo idioma? Bueno, ¿qué tal cinco? Eso es lo que se requerirá de usted, si tiene la intención de convertirse en un desarrollador web moderno. Considerando esto, si no tiene cuidado, muy rápidamente, puede sentirse abrumado al mirar ciegamente innumerables artículos de blog confusos o libros técnicos.
La clave, como con cualquier cosa, es dar un paso a la vez.
La clave, como con cualquier cosa, es dar un paso a la vez. ¿Podrías culparte a ti mismo por no haber aprendido un idioma hablado en un mes? Por supuesto no. Luego aplique ese mismo nivel de pensamiento a su viaje de programación. Estas cosas toman tiempo, pero, mientras sigas adelante, estarás allí en muy poco tiempo. ¡No pares!
El
primer paso es HTML. Comprende a qué sirve un <div>. Aprenda a
estructurar el contenido usando etiquetas semánticas. Construye una
página web básica y sin estilo.
El segundo paso, como habrás adivinado, es CSS. Aprenda a diseñar elementos en la página. Aprecia a qué se refiere la "separación de preocupaciones" y cómo esto se aplica a tu HTML y CSS. Complete su primer sitio web simple.
El tercer paso es cuando los desarrolladores comienzan a ramificarse en sus propias especialidades. En este punto, puedes sumergirte en el mundo de JavaScript, que está en auge como nunca antes. O bien, podría centrar sus esfuerzos en el back-end.
¿Confundido por la diferencia entre frontend y backend? Piense en la interfaz como la punta del iceberg que derribó el Titanic. Es la parte de la aplicación que es visible para el usuario y con la que se puede interactuar. El backend, por otro lado, maneja todo, desde la persistencia hasta las validaciones y el enrutamiento.
Para los fines de este artículo, asumamos que ha elegido la última opción; el lado del servidor, ¡lo es!
No se puede negar el hecho de que PHP domina la web.
Desafortunadamente, una vez más, te encuentras con un puñado de caminos por tomar. ¿Deberías elegir la opción más popular, PHP? ¿Qué hay de Ruby? Los niños geniales parecen preferir eso en estos días. Por otra parte, ¿y si tienes una barba? Es Python la elección correcta. Lo más importante, sin embargo, ¿cómo podría hacer una selección, cuando no tiene experiencia?
En situaciones como esta, y en opinión de este autor, no hay una elección incorrecta. Y, ciertamente, no hay nada que te prohíba cambiar de ruta. De hecho, se anima a todos los desarrolladores a aprender varios idiomas. Por ahora, sin embargo, la clave es elegir solo uno, y aprenderlo bien.
Si bien es cierto que PHP no es el más bello de los idiomas, no se puede negar el hecho de que domina la web. De hecho, es el lenguaje de scripting más popular del mundo. El beneficio de esto es que puede estar seguro de que cada pregunta de PHP ya se ha preguntado, resuelto y documentado. Hay consuelo en saber esto. A pesar de que se encuentra en la etapa más frágil de su aprendizaje, una comunidad masiva y amigable está a su alcance, lista para ayudar. Aún mejor, PHP está experimentando un renacimiento moderno como nunca antes, gracias a herramientas como Composer y Laravel.
¿Qué es PHP?
PHP,
un acrónimo de PHP: el preprocesador de hipertexto (sí, los
desarrolladores adoran sus chistes recursivos), es un lenguaje de
scripts creado específicamente para la web. Sin embargo, hay muchas
posibilidades de que esto todavía no signifique nada para ti. ¿Lenguaje
de escritura? ¿Huh? ¿Cuándo alcanzarías PHP sobre HTML simple? Bueno,
quizás un ejemplo está en orden. Asumiendo que ha instalado PHP
exitosamente, cree un archivo index.php dentro de una nueva carpeta en
su escritorio, y agregue:
1 |
<?php
|
2 |
|
3 |
echo 'Hello world'; |
Sí, es el omnipresente ejemplo de "hola mundo" con el que se familiarizará a medida que avancen sus habilidades. ¡Cada lenguaje / marco / herramienta tiene uno!
Para ejecutar este
código, use el servidor incorporado de PHP. Cambie
a su herramienta de línea de comando favorita (Terminal, para usuarios
de Mac), cd a la carpeta del proyecto y arranque el servidor con php -S
localhost: 8888. Este comando se traduce a "Ejecutar un servidor y hacer
que sea accesible desde mi navegador en localhost, puerto 8888". ¡Adelante, pruébalo! Abre Google Chrome, navega a localhost:8888, y
verás "Hello world*" en la página. ¡Hábil! echo es una construcción de
lenguaje que no hace más que generar un valor dado.
Consejo: MAMP y WAMP son excelentes soluciones de un solo clic para instalar PHP, MySQL y Apache en su Mac o PC, sin tener que buscar a tientas la línea de comandos. Pueden ser opciones útiles en las primeras etapas de su aprendizaje.



WampServer es un entorno de desarrollo que permite la instalación con un solo clic de PHP, Apache y MySQL.
Es cierto que esto no es lo más emocionante del mundo. De hecho, es probable que piense para sí mismo: "¿Por qué no podría escribir 'Hola mundo' directamente en la página HTML, y eliminar la necesidad de PHP todo junto?" Es verdad; para este ejemplo, no sirve para nada. Sin embargo, un lenguaje de scripts como PHP se vuelve particularmente útil cuando el resultado debe ser de naturaleza dinámica. ¿Qué pasa si, en lugar de mundo, desea que el saludo haga referencia a un valor pasado a través de la cadena de consulta de la URL (el texto en la barra de direcciones que viene después del signo de interrogación). Aquí hay un ejemplo actualizado, ¡que logra eso!
1 |
<?php
|
2 |
|
3 |
echo 'Hello, ' . $_GET['person']; |
Ahh, esto introduce algunas nuevas técnicas. En primer lugar, el único período que separa la cadena Hello y el
confuso $ _GET le permite concatenar (o agrupar) los valores. En este
caso, deseamos imprimir "Hola, *" y luego el valor representado por $_GET ['person']. Esto es a lo que nos referimos como una matriz
superglobal. En aras de la simplicidad, piense en esto como una forma de
*GET un valor de la cadena de consulta de la URL.
Pruebe esto
cargando localhost:8888/?person=Joe. Si está configurado
correctamente, la página web ahora debería mostrar "Hola, Joe". Juega
con él reemplazando a Joe con tu propio nombre. ¿Observe cómo se
actualiza la salida cada vez que se actualiza la página? Esto
simplemente no sería posible con HTML estático.
Una de las claves para
una programación madura es considerar todas las rutas posibles a través
de su código. Por ejemplo, ¿qué pasa si no hay una clave person
disponible? Quizás la cadena de consulta se haya omitido por completo. En ese caso, se generará un error, ya que la clave person no
existirá. ¿Cual es la solución? Si bien es cierto que esto no es más que
un simple ejemplo, es importante considerar todos los posibles
resultados. Vamos a proporcionar un valor predeterminado.
1 |
<?php
|
2 |
|
3 |
if (isset($_GET['person'])) { |
4 |
$person = $_GET['person']; |
5 |
} else { |
6 |
$person = 'Joe'; |
7 |
}
|
8 |
|
9 |
echo 'Hello, ' . $person; |
Aunque hay formas más simplificadas para permitir esto, el ejemplo anterior es un excelente punto de partida. También es tu primera introducción a las declaraciones condicionales. Acérquese a su código de la misma manera que manejaría los escenarios en la vida real. Por ejemplo, "Si nos quedamos sin leche, entonces ve a la tienda. De lo contrario, quédate en casa". Esta línea de pensamiento podría traducirse a PHP, usando la siguiente lógica:
1 |
$outOfMilk = true; |
2 |
|
3 |
if ($outOfMilk) { |
4 |
echo 'Going out to the store.'; |
5 |
} else { |
6 |
echo 'Breakfast is served.' |
7 |
}
|
En este trozo de código, solo se imprimirá una sola línea de texto en la pantalla. El valor de la variable (un valor dinámico), $outOfMilk, determinará el
flujo de control.
Consejo: para declarar variables en PHP, preceda cualquier nombre con un signo $. Como práctica recomendada, opte por nombres de variables legibles en lugar de alternativas crípticas.
Volviendo
al ejemplo anterior, siempre que $_GET ['person'] esté configurado
(piense en esto como un pseudo-nombre para "está disponible"), entonces
cree una nueva variable $person igual a su valor. De lo contrario,
aplique un valor predeterminado. Si regresa al navegador, ahora debería
funcionar correctamente,
independientemente de si la clave person existe en la cadena de
consulta.
Seguridad
Desafortunadamente, todavía no estamos en casa gratis. Una de las mejores prácticas de programación clave es colocar la
seguridad al frente de cada acción. Incluso
con este ejemplo increíblemente básico, hemos abierto la puerta a uno
de los problemas de seguridad más extendidos en la web: XSS (Cross-Site
Scripting). Una
verdadera comprensión de esto está absolutamente fuera del alcance de
esta lección introductoria (libros enteros han sido escritos en ella),
sin embargo, aquí hay una ilustración básica: ¿qué pasa si $_GET['person'] es igual a, no una cadena, sino un ¿script?
1 |
http://localhost:8888/?person=<script>alert('ATTACK!')</script> |
Debido a que este valor no se ha desinfectado, en la ejecución, en algunos navegadores, se mostrará un cuadro de alerta.
Los navegadores basados en Webkit (piensen en Chrome y Safari) ahora brindan protección contra este tipo de ataques. Sin embargo, este no siempre fue el caso, y aún no está en Firefox e Internet Explorer.


¡Ay! No podemos tener eso. Mientras que la sociedad moderna dica que un hombre es inocente hasta que se pruebe lo contrario, no ocurre lo mismo con el mundo de la programación. ¡Toda la entrada del usuario es culpable hasta que se desinfecte! Aquí hay un ejemplo actualizado que hace esto mismo:
1 |
<?php
|
2 |
|
3 |
if (isset($_GET['person'])) { |
4 |
$person = $_GET['person']; |
5 |
} else { |
6 |
$person = 'Joe'; |
7 |
}
|
8 |
|
9 |
echo 'Hello, ' . htmlspecialchars($person, ENT_QUOTES); |
Con esta modificación, si alguien intenta un ataque XSS, ¡estaremos listos! htmlspecialchars es una función PHP nativa que traduce varios símbolos a sus contrapartes de entidad. & se convierte en &, < se convierte en <, etc. Esto lo convierte en la herramienta perfecta para proporcionar ese poco más de seguridad. <script> no tiene sentido si se convierte en <script> antes de ser ejecutado. El usuario simplemente verá:
1 |
Hello, <script>alert('ATTACK!')</script> |
Estupendo; ¡ningún daño hecho!
Funciones
Mientras PHP se envía con una gran cantidad de funciones nativas, sin duda habrá momentos en los que necesite la suya propia. Afortunadamente, son muy fáciles de escribir.
Piense en una función como una pieza de lógica reutilizable que puede abstraerse, de modo que pueda identificarse y llamarse, utilizando un nombre legible.
Tal vez manejas un club nocturno (¡no es probable si estás leyendo esto!), Y necesitas una manera fácil de aceptar la fecha de nacimiento de una persona, y calcular si él o ella tienen al menos veintiún años. Una función personalizada sería una excelente manera de lograr esta tarea.
El
primer paso es definir una nueva función, llamada isAdult. Las
funciones pueden aceptar entrada externa, que luego puede ser operada. Esto permite que los datos devueltos de la función sean dinámicos. En
este caso, para determinar si una persona es adulta, necesitamos saber
su año de nacimiento. El
último paso es devolver verdadero true o falso false, dependiendo de si el año
actual menos la fecha de nacimiento de la persona es al menos veintiuno.
1 |
function isAdult($yob) { |
2 |
$currentYear = 2013; |
3 |
|
4 |
return $currentYear - $yob >= 21; |
5 |
}
|
¡Es realmente bastante simple! Ahora, solo tenemos que pasarlo al gorila. Una función puede ser
activada, o llamada, al hacer referencia a su nombre, seguida de un
conjunto de paréntesis: isAdult(). Sin
embargo, si la función requiere un argumento, entonces puede
especificarlo dentro de estos paréntesis, como se ilustra a
continuación:
1 |
if (isAdult(1985)) { |
2 |
echo 'Come on in!'; |
3 |
} else { |
4 |
echo 'Please leave now, before I call your mother.'; |
5 |
}
|
Hay un problema evidente con esta función de isAdult. El año actual ha sido codificado. Claro, funcionará a lo largo de 2013, pero ¿qué pasa el próximo año? Parece que este valor también debe ser dinámico. PHP proporciona una
función de fecha date, que se puede usar para calcular el año actual. Como
tal, la función se puede actualizar a:
1 |
function isAdult($yob) { |
2 |
$currentYear = date('Y'); |
3 |
|
4 |
return $currentYear - $yob >= 21; |
5 |
}
|
Arrays
Avance rápido unos meses y, ahora, el club nocturno está mejor que nunca. De hecho, está yendo tan bien que el gorila no puede seguir el ritmo. Su trabajo podría ser más fácil si pudiera filtrar a través de un grupo de personas a la vez.
Piense en una matriz como contenedor de datos relacionados. Incluso podría consultarlo como una lista: una lista de tweets, un grupo de miembros de la familia, una serie de fechas de nacimiento.
Una matriz en la última versión de PHP (5.4) se puede definir usando una lista separada por comas entre corchetes, como sigue:
1 |
$group = [1985, 1990, 1992, 1997]; |
Esta única variable de $group ahora contiene múltiples fechas de nacimiento. Se puede acceder a los valores dentro de él especificando un índice,
como $group[0]. Las matrices son a las que nos referimos como basadas
en cero. En la traducción, esto significa que el primer elemento, o
clave, en la matriz tendrá un índice de cero. Como tal, para acceder al
valor de 1992, debe hacer referencia a $group[2].
Ahora,
el gorila puede filtrar rápidamente a través de estas fechas de
nacimiento y calcular si permite que la persona entre o las rechace. Una
declaración foreach puede usarse para este tipo de filtrado.
1 |
$group = [1985, 1990, 1992, 1997]; |
2 |
foreach($group as $yob) { |
3 |
if (isAdult($yob)) { |
4 |
echo 'Come on in!'; |
5 |
} else { |
6 |
echo 'Please leave now, before I call your mother.'; |
7 |
}
|
8 |
}
|
Observe
cómo el gorila declara que la persona foreach del año de nacimiento en
el grupo group debe estar contenida dentro de la variable, $yob. Luego, como lo hizo antes, pasa ese valor a la función isAdult y procede en consecuencia.
Sin embargo, es posible que el gorila se confunda cuando no tiene un vínculo entre el año de nacimiento de la persona y su nombre. PHP también permite matrices asociativas, que proporcionan la funcionalidad necesaria para asociar un valor determinado con una clave. Aquí hay un ejemplo:
1 |
$group = [ |
2 |
'John' => 1985, |
3 |
'Susan' => 1990, |
4 |
'Joe' => 1992, |
5 |
'Sara' => 1997 |
6 |
];
|
Eso es mejor. Como una ventaja adicional, el gorila puede ser un poco más amigable con la persona, ahora que sabe su nombre.
1 |
$group = [ |
2 |
'John' => 1985, |
3 |
'Susan' => 1990, |
4 |
'Joe' => 1992, |
5 |
'Sara' => 1997 |
6 |
];
|
7 |
|
8 |
foreach($group as $name => $yob) { |
9 |
if (isAdult($yob)) { |
10 |
echo "Come on in, $name!"; |
11 |
} else { |
12 |
echo "Please leave now, $name, before I call your mother."; |
13 |
}
|
14 |
}
|
Al almacenar cadenas entre comillas dobles, puede anidar variables en lugar de usar concatenación. Esto puede prestarse para una sintaxis más legible.
Clases
La programación orientada a objetos está más allá del alcance de este tutorial, pero, no obstante, las clases merecen una mención. Por ahora, piense en ellos como simples contenedores para propiedades y métodos relacionados. Por ejemplo, así es como podría verse una clase que representa a una sola persona:
1 |
class Person { |
2 |
public $name; |
3 |
public $age; |
4 |
|
5 |
public function __construct($name, $age) |
6 |
{
|
7 |
$this->name = $name; |
8 |
$this->age = $age; |
9 |
}
|
10 |
}
|
¿Noto el método __construct()? Esto se conoce como un método mágico, y se activará inmediatamente
después de la instanciación. Cuando se activa este método, aceptará un
nombre y una antigüedad, y luego lo adjuntará al objeto.
Para usar esta clase, intente:
1 |
$me = new Person('Jeffrey', 28); |
Esto creará una nueva instancia de la clase Person. Esta instancia, que se almacena en la variable $me, se puede denominar
como un objeto. Ahora, no hay nada que te prohíba crear varias
instancias de esta clase, y, de hecho, en proyectos del mundo real, ¡lo
harás! La clase es solo un plano.
En este punto, sin embargo, la clase no es demasiado útil. Agreguemos un método o función para designar al cónyuge de una persona.
1 |
class Person { |
2 |
public $name; |
3 |
public $age; |
4 |
public $spouse; |
5 |
|
6 |
public function __construct($name, $age) |
7 |
{
|
8 |
$this->name = $name; |
9 |
$this->age = $age; |
10 |
}
|
11 |
|
12 |
public function marry(Person $spouse) |
13 |
{
|
14 |
$this->spouse = $spouse; |
15 |
}
|
16 |
}
|
17 |
|
18 |
$me = new Person('Jeff', 28); |
19 |
$her = new Person('Allison', 28); |
20 |
$me->marry($her); |
Este código modificado ahora incluye un método de marry() que actualizará una propiedad $spouse en el objeto. Ahora, tienes un enlace directo entre las dos personas.
Si un argumento de método está precedido por un nombre de clase (
Person $spouse), referido como sugerencia de tipo, esto designa que el parámetro debe ser una instancia de la clase dada, o se producirá un error.
Para buscar el nombre de mi cónyuge, puede escribir:
1 |
echo $me->spouse->name; // Allison |
El concepto de programación orientada a objetos es mucho más profundo que esto, pero manténgalo simple por ahora. Ayuda a pensar en las clases como sustantivos singulares: un tweet, o usuario, o cliente, o archivo.
La verdadera apreciación de este patrón solo vendrá con el tiempo.
A trabajar
Pon a prueba tus nuevas
habilidades encontradas. ¿Cómo puede registrar y mostrar tweets para un
usuario en la página? Bueno, el primer paso podría ser definir una clase
que represente un solo Tweet. Esta clase debe almacenar propiedades
para el cuerpo del tweet, así como su fecha de publicación. Además, debe
garantizar que el cuerpo del tweet no exceda los 140 caracteres. Aquí
hay una primera en una clase así:
1 |
class Tweet { |
2 |
public $body; |
3 |
public $pubDate; |
4 |
|
5 |
public function __construct($body) |
6 |
{
|
7 |
$this->setBody($body); |
8 |
$this->setPubDate(new DateTime); |
9 |
}
|
10 |
|
11 |
public function setBody($body) |
12 |
{
|
13 |
if (strlen($body) > 140) { |
14 |
throw new InvalidArgumentException; |
15 |
}
|
16 |
|
17 |
$this->body = $body; |
18 |
}
|
19 |
|
20 |
public function setPubDate(DateTime $date) |
21 |
{
|
22 |
$this->pubDate = $date->format('Y/m/d H:i:s'); |
23 |
}
|
24 |
}
|
Aunque inicialmente puede parecer abrumador, dedícale un poco de tiempo a este fragmento de código y trata de comprender lo que está sucediendo en cada paso del camino. ¡Puede encontrar que es bastante legible!
Una nueva pieza de
funcionalidad interesante proviene del método setBody. Si
el texto proporcionado excede los 140 caracteres, lo cual podemos
calcular usando la función strlen de PHP, entonces deberíamos tomar una
excepción a eso, porque rompe las reglas de un tweet. Se puede lanzar
una excepción usando la sintaxis, throw new ExceptionType.
Ahora
que tenemos un contenedor lo suficientemente decente para un tweet,
podemos crear un par de tweets, almacenarlos en una matriz, y luego,
finalmente, renderizarlos en la página, usando una declaración foreach.
1 |
$tweets = []; |
2 |
|
3 |
# add two new tweets to the array
|
4 |
$tweets[] = new Tweet('Going to the store.'); |
5 |
$tweets[] = new Tweet('Back from the store!'); |
6 |
|
7 |
# Filter through, and display on page.
|
8 |
foreach($tweets as $tweet) { |
9 |
echo "<h2>{$tweet->body}</h2>"; |
10 |
echo "<p>Posted on: {$tweet->pubDate}</p>"; |
11 |
}
|
Al ver la salida en el navegador, debería ver algo en la línea de:


Excelente, pero ¿cómo guardamos esos tweets?
Almacenamiento
Hasta ahora, has aprendido lo esencial: variables, condicionales, funciones, matrices, clases. Hay más para cubrir, pero debe investigar eso por su cuenta, cuando surja la necesidad. El siguiente paso en tu aprendizaje es la persistencia. Por ejemplo, ¿cómo podría mantener un registro de todos los tweets? ¡Un servicio de tweet que no recuerda tweets es terrible! Aquí es cuando la idea de las bases de datos entran en juego.
Piense en una tabla de base de datos como una hoja de cálculo de Excel. Puede tener cualquier cantidad de campos, como el nombre, la edad o la dirección postal de la persona. Sin embargo, PHP no proporciona este tipo de almacenamiento de forma nativa. En cambio, la opción más común es MySQL, que es la base de datos de código abierto más popular del mundo.
Instalar MySQL no está dentro del alcance de este tutorial. En su lugar, consulte este tutorial en Tuts+ para un recorrido completo.
Aquí hay un ejemplo simplificado para comenzar a buscar filas de forma segura desde una tabla de base de datos. No te preocupes si parece abrumador. MySQL es tu segundo idioma nuevo para aprender. La API PDO de PHP, así como también el lenguaje de consulta, requieren tiempo para aprender.
Primero, necesitarás una forma de conectarte a la base de datos.
1 |
function connect() { |
2 |
$conn = new PDO( |
3 |
'mysql:host=localhost;dbname=DB_NAME', |
4 |
'USERNAME', |
5 |
'PASSWORD'
|
6 |
);
|
7 |
|
8 |
$conn->setAttribute( |
9 |
PDO::ATTR_ERRMODE, |
10 |
PDO::ERRMODE_EXCEPTION |
11 |
);
|
12 |
|
13 |
return $conn; |
14 |
}
|
PDO es una de las tres API disponibles de PHP para conectarse a una base de datos MySQL.
A continuación, agregaremos una función auxiliar para recuperar todos
los registros de la tabla de tweets. Preste mucha atención al argumento
del método de consulta query , SELECT * FROM tweets. Este es un lenguaje
especial para consultar la base de datos. En este caso, estamos usando
el símbolo * para hacer referencia a todas las filas. Como tal, estamos
seleccionando todas las filas de la tabla, llamadas tweets.
Esta función prepara la consulta y luego recupera el conjunto de resultados completo.
1 |
function fetchTweets($conn) { |
2 |
$stmt = $conn->query('SELECT * FROM tweets'); |
3 |
|
4 |
return $stmt->fetchAll(PDO::FETCH_OBJ); |
5 |
}
|
Ahora, con el escenario establecido, solo necesitamos llamar a las funciones, en consecuencia.
1 |
# Connect to the DB
|
2 |
$conn = connect(); |
3 |
|
4 |
# Fetch all rows from attendees table
|
5 |
var_dump(fetchTweets($conn)); |
Una forma fácil de volcar el contenido de una variable es mediante el uso de la función var_dump. Si pasa la salida de fetchTweets($conn) a esta función, al verla en el navegador, verá algo como:


var_dump
es útil para la depuración, pero para fines de producción, es mejor
filtrar a través de los resultados y presentarlos en la página
correctamente. ¡La declaración de foreach, con la que ya está familiarizado, manejará el trabajo bastante bien!
1 |
$tweets = fetchTweets($conn); |
2 |
|
3 |
foreach($tweets as $tweet) { |
4 |
echo "<h2>{$tweet->body}</h2>"; |
5 |
echo "<p>{$tweet->pubDate}</p>"; |
6 |
}
|
Conclusión
Al igual que con cualquier habilidad, escribir PHP con fluidez requiere nada más que tu tiempo. Puede tomar cientos de horas para asimilar completamente, pero está bien. Esto es divertido, ¿verdad? ¡Debería serlo!
La mejor forma de aprender es hacer. ¡Construye proyectos desechables como si estuvieran pasando de moda! Las técnicas descritas en este tutorial lo guiarán a lo largo de la primera fase, pero, ciertamente, a medida que avancen sus habilidades, avanzará a temas más avanzados, como marcos de PHP, patrones de diseño y desarrollo basado en pruebas. ¡Que te diviertas!



