Bienvenido

Hay mucha información obsoleta en la Web que lleva por mal camino a los nuevos usuarios de PHP, propagando malas prácticas y el código inseguro. PHP: La Manera Correcta es una referencia rápida y fácil de leer, referencia rápida para los estándares de codificación PHP más populares, enlaces a tutoriales autorizados y lo que los colaboradores consideran las mejores prácticas en la actualidad.

No existe una fórmula única para utilizar PHP. Este sitio web pretende introducir a los nuevos desarrolladores de PHP PHP a algunos temas que pueden no descubrir hasta que sea demasiado tarde, y tiene como objetivo dar a los profesionales experimentados algunas ideas frescas sobre los temas que han estado haciendo durante años sin reconsiderarlo nunca. Este sitio web tampoco le dirá qué herramientas utilizar, sino que sino que le sugerirá varias opciones y, cuando sea posible, explicará las diferencias de enfoque y uso.

Este es un documento vivo y seguirá actualizándose con más información útil y ejemplos a medida que estén disponibles. y ejemplos útiles a medida que estén disponibles.

Traducciones

PHP: La Manera Correcta está traducido a muchos idiomas:

Libro

La versión más reciente de PHP: La Manera Correcta también está disponible en formato PDF, EPUB y MOBI. Ir a Leanpub

¿Cómo contribuir?

¡Ayude a hacer de este sitio web el mejor recurso para los nuevos programadores de PHP! Contribuir en GitHub

Volver al inicio

Primeros pasos

Use la versión estable actual (8.3)

Si se está iniciando en PHP, comience con la versión estable actual de PHP 8.3. PHP 8.x añade muchas características nuevas con respecto a las versiones anteriores 7.x y 5.x. El motor se ha reescrito en gran medida, y PHP es ahora incluso más rápido que las versiones anteriores. PHP 8 es una actualización importante del lenguaje y contiene muchas nuevas funciones y optimizaciones.

Deberías intentar actualizar a la última versión estable rápidamente - PHP 7.4 ya ha llegado al final de su vida útil. Actualizar es fácil, ya que no hay muchas diferencias de compatibilidad con versiones anteriores. PHP 8.0, PHP 8.1, PHP 8.2, PHP 8.3. Si no está seguro de en qué versión se encuentra una función o característica, puede consultar la documentación de PHP en el sitio web php.net.

Servidor Web Integrado

Con PHP 5.4 o posteriores, puede empezar a aprender PHP sin necesidad de instalar y configurar un servidor web completo. Para iniciar el servidor, ejecute el siguiente comando desde su terminal en la raíz web de su proyecto:

> php -S localhost:8000

Instalación en macOS

macOS 12 (Monterey) y posteriores no vienen preempaquetados con PHP. Las versiones anteriores de macOS incluyen PHP pero no incluyen la última versión estable. Hay varias formas de instalar la última versión de PHP en macOS.

Instalar PHP vía Homebrew

Homebrew es un gestor de paquetes para macOS que te ayuda a instalar fácilmente PHP y varias extensiones. El repositorio central de Homebrew proporciona “fórmulas” para PHP 7.4, 8.0, 8.1, 8.2 y PHP 8.3. Instala la última versión con este comando:

brew install php@8.3

Puede cambiar entre las versiones de PHP de Homebrew modificando su variable PATH. Alternativamente, puede usar brew-php-switcher para cambiar de versión de PHP automáticamente.

También puede cambiar manualmente entre versiones de PHP desvinculando y vinculando la versión deseada:

brew unlink php
brew link --overwrite php@8.2
brew unlink php
brew link --overwrite php@8.3

Instalar PHP vía Macports

El proyecto MacPorts es una iniciativa de código abierto para diseñar un sistema fácil de usar paracompilar, instalar y actualizar software de código abierto basado en línea de comandos, X11 o Aqua en el sistema operativo macOS.

MacPorts soporta binarios pre-compilados, por lo que no necesitas recompilar cada dependencia desde los archivos tarball fuente, esto te salva la vida si no tienes ningún paquete instalado en tu sistema.

En la actualidad, puede instalar php54, php55, php56, php70, php71, php72, php73, php74, php80, php81, php82 o php83 utilizando el comando port install, por ejemplo:

    sudo port install php74
    sudo port install php83

And you can run select command to switch your active PHP:

    sudo port select --set php php83

Instalar PHP vía phpbrew

phpbrew es una herramienta para instalar y gestionar múltiples versiones de PHP. Esto puede ser realmente útil si dos aplicaciones/proyectos requieren diferentes versiones de PHP, y no está utilizando máquinas virtuales.

Instalar PHP vía el instalador de binarios Liip

Otra opción popular es php-osx.liip.ch que proporciona métodos de instalación de una línea para las versiones 5.3 a 7.3. No sobrescribe los binarios PHP instalados por Apple, sino que instala todo en una ubicación separada. (/usr/local/php5).

Compilar desde el código fuente

Otra opción que le da control sobre la versión de PHP que instala, es compilarlo usted mismo. En ese caso asegúrese de tener instalado Xcode o el sustituto de Apple “Command Line Tools for XCode” descargable desde el Centro de Desarrolladores de Apple.

Instaladores Todo en Uno

Las soluciones listadas arriba se encargan principalmente del propio PHP, y no suministran cosas como Apache, Nginx o un servidor SQL. Las soluciones “todo en uno” como MAMP y XAMPP instalarán estas otras partes del software por usted y las unirán todas, pero la facilidad de configuración tiene como contrapartida la falta de flexibilidad.

Instalación en Windows

Puede descargar los binarios en windows.php.net/download. Después de la extracción de PHP, se recomienda establecer el PATH a la raíz de su carpeta PHP (donde se encuentra php.exe) para que pueda ejecutar PHP desde cualquier lugar.

Para el aprendizaje y el desarrollo local, puede utilizar el construido en el servidor web con PHP 5.4 + por lo que no necesita preocuparse de configurarlo. Si desea una solución “todo-en-uno” que incluya un servidor web completo y MySQL también entonces herramientas como como XAMPP, EasyPHP, OpenServer y WAMP le ayudará a poner en marcha rápidamente un entorno de desarrollo Windows. Dicho esto, estas herramientas serán un poco diferentes de las de producción, así que ten cuidado con las diferencias de entorno si estás trabajando en Windows y desplegando en Linux.

Si necesita ejecutar su sistema de producción en Windows, IIS7 le proporcionará la mayor estabilidad y el mejor rendimiento. Puedes usar phpmanager (un plugin GUI para IIS7) para que la configuración y gestión de PHP sea más sencilla. IIS7 viene con FastCGI integrado y listo para usar, sólo necesitas configurar PHP como manejador. Para soporte y recursos adicionales existe un área dedicada en iis.net para PHP.

Por lo general, la ejecución de su aplicación en diferentes entornos en el desarrollo y la producción puede dar lugar a errores extraños que aparecen cuando usted va a producción. Si estás desarrollando en Windows y desplegando en Linux (o en cualquier otro sistema que no sea Windows) entonces deberías considerar el uso de una Máquina Virtual.

Chris Tankersley tiene una entrada de blog muy útil sobre qué herramientas utiliza para hacer desarrollo PHP usando Windows.

Instalación en Linux

La mayoría de las distribuciones GNU/Linux vienen con PHP disponible desde los repositorios oficiales, pero esos paquetes usualmente están un poco atrasados con respecto a la versión estable actual. Existen múltiples formas de obtener versiones más recientes de PHP en dichas distribuciones. En las distribuciones GNU/Linux basadas en Ubuntu y Debian, por ejemplo, las mejores alternativas para paquetes nativos las proporcionadas y mantenidas Ondřej Surý, a través de su Archivo Personal de Paquetes (PPA) en Ubuntu y DPA/bikeshed en Debian. Encontrarás las instrucciones para cada uno de ellos más abajo. Dicho esto, siempre puedes usar contenedores, compilar el código fuente PHP, etc.

Distribuciones basadas en Ubuntu

Para distribuciones Ubuntu, el PPA de Ondřej Surý proporciona versiones de PHP soportadas junto con muchas extensiones PECL. Para añadir este PPA a su sistema, realice los siguientes pasos en su terminal:

  1. En primer lugar, añada el PPA a las fuentes de software de su sistema mediante el comando

    sudo add-apt-repository ppa:ondrej/php
    
  2. Después de añadir el PPA, actualice la lista de paquetes de su sistema:

    sudo apt update
    

Esto asegurará que su sistema pueda acceder e instalar los últimos paquetes PHP disponibles en el PPA.

Distribuciones basadas en Debian

Para las distribuciones basadas en Debian, Ondřej Surý también proporciona un bikeshed (equivalente en Debian a un PPA). Para añadir el bikeshed a su sistema y actualizarlo, siga estos pasos:

  1. Asegúrese de que tiene acceso de root. Si no es así, es posible que tenga que utilizar sudo para ejecutar los siguientes comandos.

  2. Actualice la lista de paquetes de su sistema:

    sudo apt-get update
    
  3. Instale lsb-release, ca-certificates, y curl:

    sudo apt-get -y install lsb-release ca-certificates curl
    
  4. Descargue la clave de firma del repositorio:

    sudo curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg
    
  5. Añada el repositorio a las fuentes de software de su sistema:

    sudo sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
    
  6. Por último, actualice de nuevo la lista de paquetes de su sistema:

    sudo apt-get update
    

Con estos pasos, su sistema será capaz de instalar los últimos paquetes PHP desde bikeshed.

Estructura Común de Directorios

Una pregunta común entre los que empiezan a escribir programas para la web es: “¿dónde pongo mis archivos?”. A lo largo de los años, la respuesta ha sido consistentemente “donde está el DocumentRoot”. Aunque esta respuesta no es del todo completa, es un buen punto de partida.

Por razones de seguridad, los archivos de configuración no deben ser accesibles por los visitantes de un sitio; por lo tanto, los scripts públicos se guardan en un directorio público y las configuraciones y datos privados se guardan fuera de ese directorio.

En cada equipo, CMS o framework en el que uno trabaja, cada uno de ellos utiliza una estructura de directorios estándar. Sin embargo, si uno está empezando un proyecto por si mismo, saber qué estructura de sistema de archivos utilizar puede ser intimidante.

Paul M. Jones ha realizado una fantástica investigación sobre las prácticas comunes de decenas de miles de proyectos de github en el ámbito de PHP. Ha compilado una estructura estándar de archivos y directorios, el Standard PHP Package Skeleton, basado en esta investigación. En esta estructura de directorios, DocumentRoot debe apuntar a public/, las pruebas unitarias deben estar en el directorio tests/, y las librerías de terceros, instaladas por composer, deben estar en el directorio vendor/. Para otros archivos y directorios, seguir el Standard PHP Package Skeleton tendrá más sentido para los contribuidores de un proyecto.

Volver al inicio

Guía de Estilos del Código

La comunidad PHP es grande y diversa, compuesta por innumerables bibliotecas, frameworks y componentes. Es habitual que los desarrolladores de PHP elijan varios de ellos y los combinen en un único proyecto. Es importante que el código PHP se adhiera (tanto como sea posible) a un estilo de código común para facilitar a los desarrolladores mezclar y combinar varias librerías para sus proyectos.

El Framework Interop Group ha propuesto y aprobado una serie de recomendaciones de estilo. No todas están relacionadas con el estilo del código, pero las que sí lo hacen son PSR-1, PSR-12, PSR-4 y PER Coding Style. Estas recomendaciones no son más que un conjunto de reglas que muchos proyectos como Drupal, Zend, Symfony, Laravel, CakePHP, phpBB, AWS SDK, FuelPHP, Lithium, etc. adoptan. Puedes utilizarlas para tus propios proyectos, o seguir utilizando tu estilo personal.

Lo ideal sería escribir código PHP que se adhiera a un estándar conocido. Esto podría ser cualquier combinación de PSRs, o uno de los estándares de codificación hechos por PEAR o Zend. Esto significa que otros desarrolladores pueden leer y trabajar fácilmente con su código, y las aplicaciones que implementan los componentes pueden tener consistencia incluso cuando se trabaja con mucho código de terceros.

Puedes usar PHP_CodeSniffer para comprobar el código contra cualquiera de estas recomendaciones, y plugins para editores de texto como Sublime Text para recibir feedback en tiempo real.

Puede corregir la estructura del código automáticamente utilizando una de las siguientes herramientas:

Y puedes ejecutar phpcs manualmente desde el shell:

phpcs -sw --standard=PSR1 file.php

Mostrará los errores y describirá cómo solucionarlos. También puede ser útil incluir el comando phpcs en un git pre-commit hook con el argumento CLI --filter=GitStaged. De este modo, el código que contenga errores contra la norma elegida no podrá entrar en el repositorio hasta que los errores hayan sido corregidos.

Si tiene PHP_CodeSniffer, puede arreglar los problemas de estructura del código reportados por esta herramienta automáticamente, con PHP Code Beautifier and Fixer.

phpcbf -w --standard=PSR1 file.php

Otra opción es usar el PHP Coding Standards Fixer. Mostrará qué tipo de errores tenía la estructura del código antes de corregirlos.

php-cs-fixer fix -v --rules=@PSR1 file.php

Se prefiere el inglés para todos los nombres de símbolos e infraestructura de códigos. Los comentarios pueden redactarse en cualquier idioma fácilmente legible por todas las partes actuales y futuras que puedan trabajar en el código base.

Por último, un buen recurso complementario para escribir código PHP limpio es Clean Code PHP.

Volver al inicio

Aspectos Destacados del Lenguaje

Paradigmas de Programación

PHP es un lenguaje flexible y dinámico que admite diversas técnicas de programación. A lo largo de los años ha evolucionado de forma espectacular, sobre todo con la incorporación de un sólido modelo orientado a objetos en PHP 5.0 (2004), funciones anónimas y namespaces en PHP 5.3 (2009) y traits en PHP 5.4 (2012).

Programación orientada a Objetos

PHP tiene un conjunto muy completo de características de programación orientada a objetos, incluyendo soporte para clases, clases abstractas, interfaces, herencia, constructores, clonación, excepciones y más.

Programación Funcional

PHP soporta funciones de primera clase, lo que significa que una función puede ser asignada a una variable. Tanto las funciones definidas por el usuario como las funciones internas pueden referenciarse mediante una variable e invocarse dinámicamente. Las funciones pueden pasarse como argumentos a otras funciones (característica denominada Funciones de orden superior) y las funciones pueden devolver otras funciones.

La recursión, una característica que permite a una función llamarse a sí misma, está soportada por el lenguaje, pero la mayor parte del código PHP se centra en la iteración.

Las nuevas funciones anónimas (con soporte para closures) están presentes desde PHP 5.3 (2009).

PHP 5.4 añadió la capacidad de enlazar closures al ámbito de un objeto y también mejoró el soporte para callables de tal forma que pueden ser usadas indistintamente con funciones anónimas en casi todos los casos.

Meta Programación

PHP soporta varias formas de meta-programación a través de mecanismos como la API Reflection y los Métodos Mágicos. Hay muchos Métodos Mágicos disponibles tales como __get(), __set(), __clone(), __toString(), __invoke(), etc. que permiten a los desarrolladores conectarse al comportamiento de las clases. Los desarrolladores de Ruby a menudo dicen que PHP carece de method_missing, pero esto esta disponible como __call() y __callStatic().

Namespaces

Como se mencionó anteriormente, la comunidad PHP tiene muchos desarrolladores creando mucho código. Esto significa que el código PHP de una biblioteca puede usar el mismo nombre de clase que otra. Cuando ambas bibliotecas son usadas en el mismo espacio de nombres, estas colisionan y causan problemas.

Los espacios de nombres (Namespaces) resuelven este problema. Como se describe en el manual de referencia de PHP, los espacios de nombres pueden compararse con los directorios del sistema operativo que espacian o separan los archivos; dos archivos con el mismo nombre pueden coexistir en directorios separados. Del mismo modo, dos clases PHP con el mismo nombre pueden coexistir en espacios de nombres PHP separados. Así de simple.

Es importante que asigne un espacio de nombres a su código para que pueda ser utilizado por otros desarrolladores sin temor a colisionar con otras bibliotecas.

Una forma recomendada de utilizar los espacios de nombres es la descrita en PSR-4, cuyo objetivo es proporcionar una convención estándar de archivos, clases y espacios de nombres para permitir un código plug-and-play.

En octubre de 2014 el PHP-FIG dejó obsoleto el anterior estándar de autocarga: PSR-0. Tanto PSR-0 como PSR-4 siguen siendo perfectamente utilizables. Este último requiere PHP 5.3, por lo que muchos proyectos que solo usan PHP 5.2 implementan PSR-0.

Si va a utilizar un estándar de autocargador para una nueva aplicación o paquete, considere utilizar PSR-4.

Biblioteca Estándar de PHP

La Biblioteca Estándar de PHP (SPL) viene empaquetada con PHP y proporciona una colección de clases e interfaces. Se compone principalmente de clases de estructura de datos comúnmente necesarias (pila, cola, montón, etc.), e iteradores que pueden recorrer estas estructuras de datos o sus propias clases que implementan interfaces SPL.

Interfaz de Línea de Comandos (CLI)

PHP fue creado para escribir aplicaciones web, pero también es útil para scripting de programas de interfaz de línea de comandos (CLI). Los programas PHP de línea de comandos pueden ayudar a automatizar tareas comunes como pruebas, despliegue y administración de aplicaciones.

Los programas PHP CLI son poderosos porque puedes usar el código de tu aplicación directamente sin tener que crear y asegurar una web GUI para ella. ¡Solo asegúrate de no poner tus scripts PHP CLI en tu raíz web pública!

Intente ejecutar PHP desde su línea de comandos:

> php -i

La opción -i imprimirá su configuración PHP igual que la función phpinfo().

La opción -a proporciona un shell interactivo, similar al IRB de ruby o al shell interactivo de python. También existen de otras útiles opciones de línea de comandos, también.

Escribamos un simple programa CLI “Hola, $nombre”. Para probarlo, crea un archivo llamado hello.php, como se muestra a continuación.

<?php
if ($argc !== 2) {
    echo "Como usar: php hello.php <name>" . PHP_EOL;
    exit(1);
}
$name = $argv[1];
echo "Hola, $name" . PHP_EOL;

PHP establece dos variables especiales basadas en los argumentos con los que se ejecuta el script. $argc es una variable entera que contiene el conteo de argumentos y $argv es una variable array que contiene el valor de cada argumento. El primer argumento es siempre el nombre de su archivo de script PHP, en este caso hello.php.

La expresión exit() se utiliza con un número distinto de cero para hacer saber al shell que el comando ha fallado. Los códigos de salida más comunes se pueden encontrar aquí.

Para ejecutar nuestro script, antes visto, desde la línea de comandos:

> php hello.php
Como usar: php hello.php <name>
> php hello.php mundo
Hola, mundo

XDebug

Una de las herramientas más útiles en el desarrollo de software es un depurador adecuado. Te permite rastrear la ejecución de tu código y monitorizar el contenido de la pila. Xdebug, el depurador de PHP, puede ser utilizado por varios IDEs para proporcionar puntos de interrupción e inspección de pila. También puede permitir a herramientas como PHPUnit y KCacheGrind realizar análisis de cobertura de código y perfilado de código. y perfiles de código.

Si te encuentras en un aprieto, dispuesto a recurrir a var_dump()/print_r(), y sigues sin encontrar la solución - quizás necesites usar el depurador.

Instalar Xdebug puede ser complicado, pero una de sus características más importantes es la “Depuración Remota” - si desarrollar código localmente y luego probarlo dentro de una máquina virtual o en otro servidor, la depuración remota es la característica que usted querrá habilitar de inmediato.

Tradicionalmente, usted modificaría su Apache VHost o archivo .htaccess con estos valores:

php_value xdebug.remote_host 192.168.?.?
php_value xdebug.remote_port 9000

El “host remoto” y el “puerto remoto” corresponderán a tu ordenador local y al puerto en el que configures tu IDE para escuchar. Entonces es sólo cuestión de poner tu IDE en modo “escuchar conexiones”, y cargar la URL:

http://your-website.example.com/index.php?XDEBUG_SESSION_START=1

Su IDE interceptará ahora el estado actual mientras se ejecuta el script, permitiéndole establecer puntos de interrupción y explorar los valores en memoria.

Los depuradores gráficos hacen que sea muy fácil recorrer el código, inspeccionar variables y evaluar el código contra el tiempo de ejecución en vivo. Muchos IDEs tienen soporte integrado o basado en plugins para la depuración gráfica con Xdebug. MacGDBp es una GUI de Xdebug gratuita e independiente de código abierto para macOS.

Volver al inicio

Gestión de Dependencias

Hay un montón de librerías PHP, frameworks y componentes entre los que elegir. Su proyecto probablemente utilizará varios de ellos - estas son las dependencias del proyecto. Hasta hace poco, PHP no tenía una buena manera de gestionar estas dependencias del proyecto. Incluso si las gestionaba manualmente, aún tenía que preocuparse por los autoloaders. Esto ya no es un problema.

Actualmente existen dos grandes sistemas de gestión de paquetes para PHP - Composer y PEAR. Composer es actualmente el gestor de paquetes más popular para PHP, sin embargo durante mucho tiempo PEAR fue el principal gestor de paquetes en uso. Conocer la historia de PEAR es una buena idea, ya que aún puede encontrar referencias a él aunque nunca lo use.

Composer y Packagist

Composer es el gestor de dependencias recomendado para PHP. Enumera las dependencias de tu proyecto en un archivo composer.json y, con unos simples comandos, Composer descargará automáticamente las dependencias de tu proyecto y configurará la carga automática por ti. Composer es análogo a NPM en el mundo node.js, o Bundler en el mundo Ruby.

Existe una plétora de librerías PHP compatibles con Composer y listas para ser utilizadas en tu proyecto. Estos “paquetes” están listados en Packagist, el repositorio oficial de librerías PHP compatibles con Composer.

Cómo instalar Composer

La forma más segura de descargar Composer es siguiendo las instrucciones oficiales. Esto verificará que el instalador no está corrupto o manipulado. El instalador instala un binario composer.phar en su directorio de trabajo actual.

Recomendamos instalar Composer globalmente (por ejemplo, una única copia en /usr/local/bin). Para ello, ejecute este comando a continuación:

mv composer.phar /usr/local/bin/composer

Nota: Si lo anterior falla por los permisos, prefijar con sudo.

Para ejecutar un Composer instalado localmente se usaría php composer.phar, globalmente es simplemente composer.

Instalar en Windows

Para los usuarios de Windows la forma más fácil de empezar a funcionar es utilizar el instalador ComposerSetup, que realiza una instalación global y configura tu $PATH para que puedas llamar a composer desde cualquier directorio en tu línea de comandos.

Cómo Definir e Instalar Dependencias

Composer mantiene un registro de las dependencias de tu proyecto en un archivo llamado composer.json. Puedes gestionarlo manualmente si lo deseas, o utilizar el propio Composer. El comando composer require añade una dependencia del proyecto y si no tienes un archivo composer.json, se creará uno. Aquí tienes un ejemplo que añade Twig como dependencia de tu proyecto.

composer require twig/twig:^2.0

Como alternativa, el comando composer init le guiará a través de la creación de un archivo completo composer.json para su proyecto. De cualquier manera, una vez que haya creado su archivo composer.json puede decirle a Composer que descargue e instale sus dependencias en el directorio vendor/. Esto también se aplica a los proyectos que ha descargado que ya proporcionan un archivo composer.json:

composer install

A continuación, añada esta línea al archivo PHP principal de su aplicación; esto le dirá a PHP que utilice el autocargador (del Inglés “autoloader”) de Composer para las dependencias de su proyecto.

<?php
require 'vendor/autoload.php';

Ahora puedes utilizar las dependencias de tu proyecto y se cargarán automáticamente cuando las necesites.

Actualizar las dependencias

Composer crea un archivo llamado composer.lock que almacena la versión exacta de cada paquete que descargó cuando ejecutó por primera vez composer install. Si comparte su proyecto con otros, asegúrese de que el archivo composer.lock esté incluido, de modo que cuando ejecuten composer install obtengan las mismas versiones que usted. Para actualizar sus dependencias, ejecute composer update. No utilice composer update al desplegar, sólo composer install, de lo contrario puede terminar con diferentes versiones de paquetes en producción.

Ts muy útil cuando se definen los requisitos de versión de forma flexible. Por ejemplo, un requisito de versión de ~1.8 significa “cualquier cosa más nueva que 1.8.0, pero menos que 2.0.x-dev”. También puede utilizar el comodín * como en 1.8.*. Ahora el comando composer update de Composer actualizará todas tus dependencias a la versión más reciente que se ajuste a las restricciones que definas.

Notificaciones de Actualización

Para recibir notificaciones sobre el lanzamiento de nuevas versiones puede suscribirse a libraries.io, un servicio web que puede supervisar las dependencias y enviarle alertas sobre las actualizaciones.

Comprobar la seguridad de las dependencias

El Comprobador Local de Seguridad PHP (en Inglés “Local PHP Security Checker”) es una herramienta de línea de comandos, que examinará su archivo composer.lock y le dirá si necesita actualizar alguna de sus dependencias.

Gestión de dependencias globales con Composer

Composer también puede gestionar dependencias globales y sus binarios. El uso es sencillo, todo lo que necesitas hacer es anteponer a su comando con global. Si por ejemplo quieres instalar PHPUnit y tenerlo disponible globalmente, ejecutarías el siguiente comando:

composer global require phpunit/phpunit

Esto creará una carpeta ~/.composer donde residen tus dependencias globales. Para que los binarios de los paquetes instalados, añada la carpeta ~/.composer/vendor/bin a la variable $PATH.

PEAR

Un veterano gestor de paquetes que algunos desarrolladores PHP disfrutan es PEAR. Se comporta de manera similar a Composer, pero tiene algunas diferencias notables.

PEAR requiere que cada paquete tenga una estructura específica, lo que significa que el autor del paquete debe prepararlo para uso con PEAR. No es posible utilizar un proyecto que no haya sido preparado para funcionar con PEAR.

PEAR instala los paquetes globalmente, lo que significa que después de instalarlos una vez están disponibles para todos los proyectos en ese servidor. Esto puede ser bueno si muchos proyectos dependen del mismo paquete con la misma versión, pero puede llevar a problemas si surgen conflictos de versión entre dos proyectos.

Instalación de PEAR

Puede instalar PEAR descargando el instalador .phar y ejecutándolo. La documentación de PEAR tiene detalladas instrucciones de instalación para cada sistema operativo.

Si usas Linux, también puedes echar un vistazo al gestor de paquetes de tu distribución. Debian y Ubuntu, por ejemplo, tienen un paquete apt php-pear.

¿Cómo instalar un paquete?

Si el paquete aparece en la lista de paquetes de PEAR, puede instalarlo especificando el nombre oficial:

pear install foo

Si el paquete se aloja en otro canal, deberá “descubrir” primero el canal y especificarlo también al instalar. Consulta la documentación sobre el uso de canales para obtener más información sobre este tema.

Manejo de dependencias PEAR con Composer

Si ya está utilizando Composer y desea instalar algún código PEAR también, puede utilizar Composer para manejar sus dependencias PEAR. Los repositorios PEAR ya no son soportados directamente por Composer versión 2, por lo que debes añadir manualmente un repositorio para instalar paquetes PEAR:

{
    "repositories": [
        {
            "type": "package",
            "package": {
                "name": "pear2/pear2-http-request",
                "version": "2.5.1",
                "dist": {
                    "url": "https://github.com/pear2/HTTP_Request/archive/refs/heads/master.zip",
                    "type": "zip"
                }
            }
        }
    ],
    "require": {
        "pear2/pear2-http-request": "*"
    },
    "autoload": {
        "psr-4": {"PEAR2\\HTTP\\": "vendor/pear2/pear2-http-request/src/HTTP/"}
    }
}

La primera sección "repositorios" se utilizará para que Composer sepa que debe “inicializar” (o “descubrir” en terminología PEAR) el repositorio pear. Entonces la sección require prefijará el nombre del paquete así:

pear-channel/package

El prefijo “pear” está codificado para evitar conflictos, ya que un canal pear puede ser el mismo que el nombre de proveedor de otro paquete, por ejemplo, entonces el nombre corto del canal (o URL completa) puede ser usado para referenciar en qué canal está el paquete.

Cuando se instala este código estará disponible en su directorio de proveedores y automáticamente disponible a través del autocargador de Composer:

vendor/pear2/pear2-http-request/pear2/HTTP/Request.php

Para utilizar este paquete PEAR simplemente haga referencia a él de la siguiente manera:

<?php
require __DIR__ . '/vendor/autoload.php';

use PEAR2\HTTP\Request;

$request = new Request();

Volver al inicio

Buenas Prácticas

Conceptos Básicos

PHP is a vast language that allows coders of all levels the ability to produce code not only quickly, but efficiently. However, while advancing through the language, we often forget the basics that we first learnt (or overlooked) in favor of short cuts and/or bad habits. To help combat this common issue, this section is aimed at reminding coders of the basic coding practices within PHP.

Fecha y Hora

PHP has a class named DateTime to help you when reading, writing, comparing or calculating with date and time. There are many date and time related functions in PHP besides DateTime, but it provides nice object-oriented interface to most common uses. DateTime can handle time zones, but that is outside the scope of this short introduction.

To start working with DateTime, convert raw date and time string to an object with createFromFormat() factory method or do new DateTime to get the current date and time. Use format() method to convert DateTime back to a string for output.

<?php
$raw = '22. 11. 1968';
$start = DateTime::createFromFormat('d. m. Y', $raw);

echo 'Start date: ' . $start->format('Y-m-d') . PHP_EOL;

Calculating with DateTime is possible with the DateInterval class. DateTime has methods like add() and sub() that take a DateInterval as an argument. Do not write code that expects the same number of seconds in every day. Both daylight saving and time zone alterations will break that assumption. Use date intervals instead. To calculate date difference use the diff() method. It will return new DateInterval, which is super easy to display.

<?php
// create a copy of $start and add one month and 6 days
$end = clone $start;
$end->add(new DateInterval('P1M6D'));

$diff = $end->diff($start);
echo 'Difference: ' . $diff->format('%m month, %d days (total: %a days)') . PHP_EOL;
// Difference: 1 month, 6 days (total: 37 days)

You can use standard comparisons on DateTime objects:

<?php
if ($start < $end) {
    echo "Start is before the end!" . PHP_EOL;}

One last example to demonstrate the DatePeriod class. It is used to iterate over recurring events. It can take two DateTime objects, start and end, and the interval for which it will return all events in between.

<?php
// output all thursdays between $start and $end
$periodInterval = DateInterval::createFromDateString('first thursday');
$periodIterator = new DatePeriod($start, $periodInterval, $end, DatePeriod::EXCLUDE_START_DATE);
foreach ($periodIterator as $date) {
    // output each date in the period
    echo $date->format('Y-m-d') . ' ';
}

A popular PHP API extension is Carbon. It inherits everything in the DateTime class, so involves minimal code alterations, but extra features include Localization support, further ways to add, subtract and format a DateTime object, plus a means to test your code by simulating a date and time of your choosing.

Patrones de Diseño

When you are building your application it is helpful to use common patterns in your code and common patterns for the overall structure of your project. Using common patterns is helpful because it makes it much easier to manage your code and lets other developers quickly understand how everything fits together.

If you use a framework then most of the higher level code and project structure will be based on that framework, so a lot of the pattern decisions are made for you. But it is still up to you to pick out the best patterns to follow in the code you build on top of the framework. If, on the other hand, you are not using a framework to build your application then you have to find the patterns that best suit the type and size of application that you’re building.

You can learn more about PHP design patterns and see working examples at:

Trabajar con UTF-8

This section was originally written by Alex Cabal over at PHP Best Practices and has been used as the basis for our own UTF-8 advice.

There’s no one-liner. Be careful, detailed, and consistent.

Right now PHP does not support Unicode at a low level. There are ways to ensure that UTF-8 strings are processed OK, but it’s not easy, and it requires digging in to almost all levels of the web app, from HTML to SQL to PHP. We’ll aim for a brief, practical summary.

UTF-8 at the PHP level

The basic string operations, like concatenating two strings and assigning strings to variables, don’t need anything special for UTF-8. However, most string functions, like strpos() and strlen(), do need special consideration. These functions often have an mb_* counterpart: for example, mb_strpos() and mb_strlen(). These mb_* strings are made available to you via the Multibyte String Extension, and are specifically designed to operate on Unicode strings.

You must use the mb_* functions whenever you operate on a Unicode string. For example, if you use substr() on a UTF-8 string, there’s a good chance the result will include some garbled half-characters. The correct function to use would be the multibyte counterpart, mb_substr().

The hard part is remembering to use the mb_* functions at all times. If you forget even just once, your Unicode string has a chance of being garbled during further processing.

Not all string functions have an mb_* counterpart. If there isn’t one for what you want to do, then you might be out of luck.

You should use the mb_internal_encoding() function at the top of every PHP script you write (or at the top of your global include script), and the mb_http_output() function right after it if your script is outputting to a browser. Explicitly defining the encoding of your strings in every script will save you a lot of headaches down the road.

Additionally, many PHP functions that operate on strings have an optional parameter letting you specify the character encoding. You should always explicitly indicate UTF-8 when given the option. For example, htmlentities() has an option for character encoding, and you should always specify UTF-8 if dealing with such strings. Note that as of PHP 5.4.0, UTF-8 is the default encoding for htmlentities() and htmlspecialchars().

Finally, If you are building a distributed application and cannot be certain that the mbstring extension will be enabled, then consider using the symfony/polyfill-mbstring Composer package. This will use mbstring if it is available, and fall back to non UTF-8 functions if not.

UTF-8 at the Database level

If your PHP script accesses MySQL, there’s a chance your strings could be stored as non-UTF-8 strings in the database even if you follow all of the precautions above.

To make sure your strings go from PHP to MySQL as UTF-8, make sure your database and tables are all set to the utf8mb4 character set and collation, and that you use the utf8mb4 character set in the PDO connection string. See example code below. This is critically important.

Note that you must use the utf8mb4 character set for complete UTF-8 support, not the utf8 character set! See Further Reading for why.

UTF-8 at the browser level

Use the mb_http_output() function to ensure that your PHP script outputs UTF-8 strings to your browser.

The browser will then need to be told by the HTTP response that this page should be considered as UTF-8. Today, it is common to set the character set in the HTTP response header like this:

<?php
header('Content-Type: text/html; charset=UTF-8')

The historic approach to doing that was to include the charset <meta> tag in your page’s <head> tag.

<?php
// Tell PHP that we're using UTF-8 strings until the end of the script
mb_internal_encoding('UTF-8');
$utf_set = ini_set('default_charset', 'utf-8');
if (!$utf_set) {
    throw new Exception('could not set default_charset to utf-8, please ensure it\'s set on your system!');
}

// Tell PHP that we'll be outputting UTF-8 to the browser
mb_http_output('UTF-8');

// Our UTF-8 test string
$string = 'Êl síla erin lû e-govaned vîn.';

// Transform the string in some way with a multibyte function
// Note how we cut the string at a non-Ascii character for demonstration purposes
$string = mb_substr($string, 0, 15);

// Connect to a database to store the transformed string
// See the PDO example in this document for more information
// Note the `charset=utf8mb4` in the Data Source Name (DSN)
$link = new PDO(
    'mysql:host=your-hostname;dbname=your-db;charset=utf8mb4',
    'your-username',
    'your-password',
    array(
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_PERSISTENT => false
    )
);

// Store our transformed string as UTF-8 in our database
// Your DB and tables are in the utf8mb4 character set and collation, right?
$handle = $link->prepare('insert into ElvishSentences (Id, Body, Priority) values (default, :body, :priority)');
$handle->bindParam(':body', $string, PDO::PARAM_STR);
$priority = 45;
$handle->bindParam(':priority', $priority, PDO::PARAM_INT); // explicitly tell pdo to expect an int
$handle->execute();

// Retrieve the string we just stored to prove it was stored correctly
$handle = $link->prepare('select * from ElvishSentences where Id = :id');
$id = 7;
$handle->bindParam(':id', $id, PDO::PARAM_INT);
$handle->execute();

// Store the result into an object that we'll output later in our HTML
// This object won't kill your memory because it fetches the data Just-In-Time to
$result = $handle->fetchAll(\PDO::FETCH_OBJ);

// An example wrapper to allow you to escape data to html
function escape_to_html($dirty){
    echo htmlspecialchars($dirty, ENT_QUOTES, 'UTF-8');
}

header('Content-Type: text/html; charset=UTF-8'); // Unnecessary if your default_charset is set to utf-8 already
?><!doctype html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>UTF-8 test page</title>
    </head>
    <body>
        <?php
        foreach($result as $row){
            escape_to_html($row->Body);  // This should correctly output our transformed UTF-8 string to the browser
        }
        ?>
    </body>
</html>

Further reading

Internacionalización (i18n) y Localización (l10n)

Disclaimer for newcomers: i18n and l10n are numeronyms, a kind of abbreviation where numbers are used to shorten words - in our case, internationalization becomes i18n and localization, l10n.

First of all, we need to define those two similar concepts and other related things:

Common ways to implement

The easiest way to internationalize PHP software is by using array files and using those strings in templates, such as <h1><?=$TRANS['title_about_page']?></h1>. This way is, however, hardly recommended for serious projects, as it poses some maintenance issues along the road - some might appear in the very beginning, such as pluralization. So, please, don’t try this if your project will contain more than a couple of pages.

The most classic way and often taken as reference for i18n and l10n is a Unix tool called gettext. It dates back to 1995 and is still a complete implementation for translating software. It is easy enough to get running, while still sporting powerful supporting tools. It is about Gettext we will be talking here. Also, to help you not get messy over the command-line, we will be presenting a great GUI application that can be used to easily update your l10n source.

Other tools

There are common libraries used that support Gettext and other implementations of i18n. Some of them may seem easier to install or sport additional features or i18n file formats. In this document, we focus on the tools provided with the PHP core, but here we list others for completion:

Other frameworks also include i18n modules, but those are not available outside of their codebases:

If you decide to go for one of the libraries that provide no extractors, you may want to use the gettext formats, so you can use the original gettext toolchain (including Poedit) as described in the rest of the chapter.

Gettext

Installation

You might need to install Gettext and the related PHP library by using your package manager, like apt-get or yum. After installed, enable it by adding extension=gettext.so (Linux/Unix) or extension=php_gettext.dll (Windows) to your php.ini.

Here we will also be using Poedit to create translation files. You will probably find it in your system’s package manager; it is available for Unix, macOS, and Windows, and can be downloaded for free on their website as well.

Structure

Types of files

There are three files you usually deal with while working with gettext. The main ones are PO (Portable Object) and MO (Machine Object) files, the first being a list of readable “translated objects” and the second, the corresponding binary to be interpreted by gettext when doing localization. There’s also a POT (Template) file, which simply contains all existing keys from your source files, and can be used as a guide to generate and update all PO files. Those template files are not mandatory: depending on the tool you are using to do l10n, you can go just fine with only PO/MO files. You will always have one pair of PO/MO files per language and region, but only one POT per domain.

Domains

There are some cases, in big projects, where you might need to separate translations when the same words convey different meaning given a context. In those cases, you split them into different domains. They are, basically, named groups of POT/PO/MO files, where the filename is the said translation domain. Small and medium-sized projects usually, for simplicity, use only one domain; its name is arbitrary, but we will be using “main” for our code samples. In Symfony projects, for example, domains are used to separate the translation for validation messages.

Locale code

A locale is simply a code that identifies one version of a language. It is defined following the ISO 639-1 and ISO 3166-1 alpha-2 specs: two lower-case letters for the language, optionally followed by an underline and two upper-case letters identifying the country or regional code. For rare languages, three letters are used.

For some speakers, the country part may seem redundant. In fact, some languages have dialects in different countries, such as Austrian German (de_AT) or Brazilian Portuguese (pt_BR). The second part is used to distinguish between those dialects - when it is not present, it is taken as a “generic” or “hybrid” version of the language.

Directory structure

To use Gettext, we will need to adhere to a specific structure of folders. First, you will need to select an arbitrary root for your l10n files in your source repository. Inside it, you will have a folder for each needed locale, and a fixed LC_MESSAGES folder that will contain all your PO/MO pairs. Example:

<project root>
 ├─ src/
 ├─ templates/
 └─ locales/
    ├─ forum.pot
    ├─ site.pot
    ├─ de/
    │  └─ LC_MESSAGES/
    │     ├─ forum.mo
    │     ├─ forum.po
    │     ├─ site.mo
    │     └─ site.po
    ├─ es_ES/
    │  └─ LC_MESSAGES/
    │     └─ ...
    ├─ fr/
    │  └─ ...
    ├─ pt_BR/
    │  └─ ...
    └─ pt_PT/
       └─ ...

Plural forms

As we said in the introduction, different languages might sport different plural rules. However, gettext saves us from this trouble once again. When creating a new .po file, you will have to declare the plural rules for that language, and translated pieces that are plural-sensitive will have a different form for each of those rules. When calling Gettext in code, you will have to specify the number related to the sentence, and it will work out the correct form to use - even using string substitution if needed.

Plural rules include the number of plurals available and a boolean test with n that would define in which rule the given number falls (starting the count with 0). For example:

Now that you understood the basis of how plural rules works - and if you didn’t, please look at a deeper explanation on the LingoHub tutorial -, you might want to copy the ones you need from a list instead of writing them by hand.

When calling out Gettext to do localization on sentences with counters, you will have to provide it the related number as well. Gettext will work out what rule should be in effect and use the correct localized version. You will need to include in the .po file a different sentence for each plural rule defined.

Sample implementation

After all that theory, let’s get a little practical. Here’s an excerpt of a .po file - don’t mind with its format, but with the overall content instead; you will learn how to edit it easily later:

msgid ""
msgstr ""
"Language: pt_BR\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

msgid "We are now translating some strings"
msgstr "Nós estamos traduzindo algumas strings agora"

msgid "Hello %1$s! Your last visit was on %2$s"
msgstr "Olá %1$s! Sua última visita foi em %2$s"

msgid "Only one unread message"
msgid_plural "%d unread messages"
msgstr[0] "Só uma mensagem não lida"
msgstr[1] "%d mensagens não lidas"

The first section works like a header, having the msgid and msgstr especially empty. It describes the file encoding, plural forms and other things that are less relevant. The second section translates a simple string from English to Brazilian Portuguese, and the third does the same, but leveraging string replacement from sprintf so the translation may contain the user name and visit date. The last section is a sample of pluralization forms, displaying the singular and plural version as msgid in English and their corresponding translations as msgstr 0 and 1 (following the number given by the plural rule). There, string replacement is used as well so the number can be seen directly in the sentence, by using %d. The plural forms always have two msgid (singular and plural), so it is advised not to use a complex language as the source of translation.

Discussion on l10n keys

As you might have noticed, we are using as source ID the actual sentence in English. That msgid is the same used throughout all your .po files, meaning other languages will have the same format and the same msgid fields but translated msgstr lines.

Talking about translation keys, there are two main “schools” here:

  1. msgid as a real sentence. The main advantages are:
    • if there are pieces of the software untranslated in any given language, the key displayed will still maintain some meaning. Example: if you happen to translate by heart from English to Spanish but need help to translate to French, you might publish the new page with missing French sentences, and parts of the website would be displayed in English instead;
    • it is much easier for the translator to understand what’s going on and do a proper translation based on the msgid;
    • it gives you “free” l10n for one language - the source one;
    • The only disadvantage: if you need to change the actual text, you would need to replace the same msgid across several language files.
  2. msgid as a unique, structured key. It would describe the sentence role in the application in a structured way, including the template or part where the string is located instead of its content.
    • it is a great way to have the code organized, separating the text content from the template logic.
    • however, that could bring problems to the translator that would miss the context. A source language file would be needed as a basis for other translations. Example: the developer would ideally have an en.po file, that translators would read to understand what to write in fr.po for instance.
    • missing translations would display meaningless keys on screen (top_menu.welcome instead of Hello there, User! on the said untranslated French page). That is good it as would force translation to be complete before publishing - however, bad as translation issues would be remarkably awful in the interface. Some libraries, though, include an option to specify a given language as “fallback”, having a similar behavior as the other approach.

The Gettext manual favors the first approach as, in general, it is easier for translators and users in case of trouble. That is how we will be working here as well. However, the Symfony documentation favors keyword-based translation, to allow for independent changes of all translations without affecting templates as well.

Everyday usage

In a typical application, you would use some Gettext functions while writing static text in your pages. Those sentences would then appear in .po files, get translated, compiled into .mo files and then, used by Gettext when rendering the actual interface. Given that, let’s tie together what we have discussed so far in a step-by-step example:

1. A sample template file, including some different gettext calls

<?php include 'i18n_setup.php' ?>
<div id="header">
    <h1><?=sprintf(gettext('Welcome, %s!'), $name)?></h1>
    <!-- code indented this way only for legibility -->
    <?php if ($unread): ?>
        <h2><?=sprintf(
            ngettext('Only one unread message',
                     '%d unread messages',
                     $unread),
            $unread)?>
        </h2>
    <?php endif ?>
</div>

<h1><?=gettext('Introduction')?></h1>
<p><?=gettext('We\'re now translating some strings')?></p>

2. A sample setup file (i18n_setup.php as used above), selecting the correct locale and configuring Gettext

<?php
/**
 * Verifies if the given $locale is supported in the project
 * @param string $locale
 * @return bool
 */
function valid($locale) {
   return in_array($locale, ['en_US', 'en', 'pt_BR', 'pt', 'es_ES', 'es']);
}

//setting the source/default locale, for informational purposes
$lang = 'en_US';

if (isset($_GET['lang']) && valid($_GET['lang'])) {
    // the locale can be changed through the query-string
    $lang = $_GET['lang'];    //you should sanitize this!
    setcookie('lang', $lang); //it's stored in a cookie so it can be reused
} elseif (isset($_COOKIE['lang']) && valid($_COOKIE['lang'])) {
    // if the cookie is present instead, let's just keep it
    $lang = $_COOKIE['lang']; //you should sanitize this!
} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
    // default: look for the languages the browser says the user accepts
    $langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
    array_walk($langs, function (&$lang) { $lang = strtr(strtok($lang, ';'), ['-' => '_']); });
    foreach ($langs as $browser_lang) {
        if (valid($browser_lang)) {
            $lang = $browser_lang;
            break;
        }
    }
}

// here we define the global system locale given the found language
putenv("LANG=$lang");

// this might be useful for date functions (LC_TIME) or money formatting (LC_MONETARY), for instance
setlocale(LC_ALL, $lang);

// this will make Gettext look for ../locales/<lang>/LC_MESSAGES/main.mo
bindtextdomain('main', '../locales');

// indicates in what encoding the file should be read
bind_textdomain_codeset('main', 'UTF-8');

// if your application has additional domains, as cited before, you should bind them here as well
bindtextdomain('forum', '../locales');
bind_textdomain_codeset('forum', 'UTF-8');

// here we indicate the default domain the gettext() calls will respond to
textdomain('main');

// this would look for the string in forum.mo instead of main.mo
// echo dgettext('forum', 'Welcome back!');
?>

3. Preparing translation for the first run

One of the great advantages Gettext has over custom framework i18n packages is its extensive and powerful file format. “Oh man, that’s quite hard to understand and edit by hand, a simple array would be easier!” Make no mistake, applications like Poedit are here to help - a lot. You can get the program from their website, it’s free and available for all platforms. It’s a pretty easy tool to get used to, and a very powerful one at the same time - using all features Gettext has available. This guide is based on PoEdit 1.8.

In the first run, you should select “File > New…” from the menu. You’ll be asked straight ahead for the language: here you can select/filter the language you want to translate to, or use that format we mentioned before, such as en_US or pt_BR.

Now, save the file - using that directory structure we mentioned as well. Then you should click “Extract from sources”, and here you’ll configure various settings for the extraction and translation tasks. You’ll be able to find all those later through “Catalog > Properties”:

After setting those points it will run a scan through your source files to find all the localization calls. After every scan PoEdit will display a summary of what was found and what was removed from the source files. New entries will fed empty into the translation table, and you’ll start typing in the localized versions of those strings. Save it and a .mo file will be (re)compiled into the same folder and ta-dah: your project is internationalized.

4. Translating strings

As you may have noticed before, there are two main types of localized strings: simple ones and those with plural forms. The first ones have simply two boxes: source and localized string. The source string cannot be modified as Gettext/Poedit do not include the powers to alter your source files - you should change the source itself and rescan the files. Tip: you may right-click a translation line and it will hint you with the source files and lines where that string is being used. On the other hand, plural form strings include two boxes to show the two source strings, and tabs so you can configure the different final forms.

Whenever you change your sources and need to update the translations, just hit Refresh and Poedit will rescan the code, removing non-existent entries, merging the ones that changed and adding new ones. It may also try to guess some translations, based on other ones you did. Those guesses and the changed entries will receive a “Fuzzy” marker, indicating it needs review, appearing golden in the list. It is also useful if you have a translation team and someone tries to write something they are not sure about: just mark Fuzzy, and someone else will review later.

Finally, it is advised to leave “View > Untranslated entries first” marked, as it will help you a lot to not forget any entry. From that menu, you can also open parts of the UI that allow you to leave contextual information for translators if needed.

Tips & Tricks

Possible caching issues

If you are running PHP as a module on Apache (mod_php), you might face issues with the .mo file being cached. It happens the first time it is read, and then, to update it, you might need to restart the server. On Nginx and PHP5 it usually takes only a couple of page refreshes to refresh the translation cache, and on PHP7 it is rarely needed.

Additional helper functions

As preferred by many people, it is easier to use _() instead of gettext(). Many custom i18n libraries from frameworks use something similar to t() as well, to make translated code shorter. However, that is the only function that sports a shortcut. You might want to add in your project some others, such as __() or _n() for ngettext(), or maybe a fancy _r() that would join gettext() and sprintf() calls. Other libraries, such as php-gettext’s Gettext also provide helper functions like these.

In those cases, you’ll need to instruct the Gettext utility on how to extract the strings from those new functions. Don’t be afraid; it is very easy. It is just a field in the .po file, or a Settings screen on Poedit. In the editor, that option is inside “Catalog > Properties > Source keywords”. Remember: Gettext already knows the default functions for many languages, so don’t be afraid if that list seems empty. You need to include there the specifications of those new functions, following a specific format:

After including those new rules in the .po file, a new scan will bring in your new strings just as easy as before.

References

Volver al inicio

Inyección de Dependencias

De Wikipedia:

Inyección de dependencias (Dependency Injection, DI) es un patrón de diseño en el que se suministran objetos a una clase en lugar de ser la propia clase la que cree dichos objetos. Esos objetos cumplen contratos que necesitan nuestras clases para poder funcionar (de ahí el concepto de dependencia).

Esta cita hace que el concepto suene mucho más complejo de lo que es en realidad. La inyección de dependencias consiste en proporcionar a un componente sus dependencias a través de la inyección en constructor, llamadas a métodos o la configuración de propiedades. Es así de simple.

Concepto Básico

Podemos demostrar el concepto con un simple pero a la vez inofencivo ejemplo.

Tenemos una clase Database que requiere un adaptador para comunicarse con la base de datos. Instanciamos el adaptador en el constructor y creamos una dependencia rígida. Esto dificulta las pruebas y significa que la clase Database esta fuertemente acoplada al adaptador.

<?php
namespace Database;

class Database
{
    protected $adapter;

    public function __construct()
    {
        $this->adapter = new MySqlAdapter;
    }
}

class MysqlAdapter {}

Este código se puede refactorizar para usar Inyección de Dependencia y de esta manera desacoplamos la dependencia. Aquí, inyectamos la dependencia en un constructor haciendo uso de la promoción de propiedades en el constructor y de esta manera estará disponible como una propiedad de la clase:

<?php
namespace Database;

class Database
{
    public function __construct(protected MySqlAdapter $adapter)
    {
    }
}

class MysqlAdapter {}

Ahora le estamos dando a la clase Database su dependencia en lugar de crearla directamente. Incluso podríamos crear un método que acepte un argumento de la dependencia y configurarla de esa manera, o si la propiedad $adapter fuera public podríamos configurarla directamente.

Problema Complejo

Si alguna vez has leído sobre Inyección de Dependencias es probable que hayas visto los términos “Inversión de control” o “Principio de Inversión de Dependencias”. Estos son los problemas complejos que resuelve la Inyección de Dependencias.

Inversión de Control

Inversión de Control, como su nombre lo indica, es “invertir el control” de un sistema al mantener el control organizacional completamente separado de nuestros objetos. En términos de Inyección de Dependencias, esto significa desacoplar nuestras dependencias al controlarlas e instanciarlas en otras partes del sistema.

Durante años, los frameworks PHP han estado logrando la Inversión de Control, sin embargo, surge la pregunta, ¿qué parte del control estamos invirtiendo y hacia dónde? Por ejemplo, los frameworks MVC generalmente proveen un superobjeto o controlador base que otros controladores deben extender para obtener acceso a sus dependencias. Esto es Inversión de Control, sin embargo, en lugar de desacoplar las dependencias, este método simplemente las mueve.

La Inyección de Dependencia nos permite resolver este problema de una forma mas elegante al inyectar solamente las dependencias que necesitamos, cuando la necesitamos sin la necesidad de tener dependencias en duro.

S.O.L.I.D.

Principio de Responsabilidad Única

El Principio de Responsabilidad Única (en inglés Single Responsibility Principle) se refiere a los actores y a la arquitectura de alto nivel. Establece que “Una clase debería tener solo una razón para cambiar”. Esto significa que cada clase debería solo tener responsabilidad sobre una única parte de la funcionalidad proporcionada por el software. El mayor beneficio de este enfoque es que permite una mejor reutilización del código. Al diseñar nuestras clases para que hagan solo una cosa, podemos utilizar (o reutilizarlas) en cualquier otro programa sin cambiarlas.

Principio Abierto/Cerrado

El Principio abierto/cerrado (Open/Closed Principle) se refiere al diseño de clases y la extensión de funcionalidades. Establece que “Las entidades de software (clases, módulos, funciones, etc.) deben estar abiertas a la extensión, pero cerradas a la modificación”. Esto significa que debemos diseñar nuestros módulos, clases y funciones de manera que cuando se necesite una nueva funcionalidad, no deberíamos modificar nuestro código existente sino que escribamos código nuevo que será utilizado por el código existente. En términos prácticos, esto significa que deberíamos escribir clases que implementen y respeten las interfaces, luego hacer sugerencias de tipos hacía esas interfaces en lugar de clases específicas.

El mayor beneficio de este enfoque es que podemos extender nuestro código muy fácilmente con soporte para algo nuevo sin tener que modificar el código existente, lo que significa que podemos reducir el tiempo de Control de Calidad (QA) y el riesgo de un impacto negativo a la aplicación se reduce considerablemente. Podemos desplegar nuevo código más rápido y con más confianza.

Principio de Sustitución de Liskov

El Principio de Sustitución de Liskov (Liskov Substitution Principle) se refiere a la subtipificación y la herencia. Establece que “Las clases hijas nunca deben romper las definiciones de tipo de la clase padre”. O, en palabras de Robert C. Martin, “Los subtipos deben ser sustituibles por sus tipos base”.

Por ejemplo, si tenemos una interfaz FileInterface que define un método embed(), y contamos con las clases Audio y Video las cuales implementan esta interfaz FileInterface, podemos esperar que el uso del método embed() siempre haga lo que pretendemos. Si más adelante creamos una clase PDF o una clase Gist que también implementen la interfaz FileInterface, ya sabremos y entenderemos lo que hará el método embed(). El mayor beneficio de este enfoque es que tenemos la capacidad de construir programas flexibles y fácilmente configurables, ya que cuando cambiamos un objeto de un tipo (por ejemplo, FileInterface) por otro, no necesitamos modificar más nada en nuestro programa.

Principio de Segregación de Interfaces

El Principio de Segregación de Interfaces (Interface Segregation Principle, ISP por sus siglas en inglés) trata sobre la comunicación entre la lógica de negocio y los clientes. Establece que “Ningún cliente debería verse obligado a depender de métodos que no utiliza”. Esto quiere decir que en lugar de tener una única interfaz monolítica que todas las clases conformantes deben implementar, deberíamos en su lugar proporcionar un conjunto de interfaces más pequeñas y específicas por concepto que una clase conformante implemente una o más de ellas.

Por ejemplo, una clase Car o Bus estaría interesada en un método steeringWheel(), pero una clase Motorcycle o Tricycle no lo estaría. Por otro lado, una clase Motorcycle o Tricycle se interesaría en un método handlebars(), mientras que una clase Car o Bus no. No es necesario que todos estos tipos de vehículos implementen soporte tanto para steeringWheel() como para handlebars(), por lo que deberíamos descomponer la interfaz original.

Principio de Inversión de Dependencias

El Principio de Inversión de Dependencias (Dependency Inversion Principle) trata sobre eliminar los vínculos rígidos entre clases discretas para que se pueda aprovechar nueva funcionalidad al pasar una clase diferente. Establece que se debe “depender de abstracciones. No depender de implementaciones”. En términos simples, esto significa que nuestras dependencias deberían ser interfaces/contratos o clases abstractas en lugar de implementaciones concretas. Podemos fácilmente refactorizar el ejemplo anterior para seguir este principio.

<?php
namespace Database;

class Database
{
    public function __construct(protected AdapterInterface $adapter)
    {
    }
}

interface AdapterInterface {}

class MysqlAdapter implements AdapterInterface {}

Hay varios beneficios al hacer que la clase Database ahora dependa de una interfaz en lugar de una implementación concreta.

Consideremos que estamos trabajando en un equipo y el adaptador está siendo desarrollado por un colega. En nuestro primer ejemplo, tendríamos que esperar a que dicho colega termine el adaptador antes de poder simularlo adecuadamente para nuestras pruebas unitarias. Ahora que la dependencia es una interfaz/contrato, podemos simular esa interfaz sin problemas, sabiendo que nuestro colega construirá el adaptador basado en ese contrato.

Un beneficio incluso aún mayor de este enfoque es que nuestro código ahora es mucho más escalable. Si dentro de un año decidimos que queremos migrar a un tipo diferente de base de datos, podemos escribir un adaptador que implemente la interfaz original e inyectar eso en su lugar, no se requeriría más refactorización ya que podemos asegurar que el adaptador sigue el contrato establecido por la interfaz.

Contenedores

Lo primero que deberías entender sobre los Contenedores de Inyección de Dependencias es que no son lo mismo que la Inyección de Dependencias. Un contenedor es una herramienta de conveniencia que nos ayuda a implementar la Inyección de Dependencias; sin embargo, pueden ser y a menudo se usan de forma incorrecta para implementar un anti-patrón, Localización de Servicios. Inyectar un contenedor de DI como Localizador de Servicios en tus clases puede crear una dependencia más fuerte en el contenedor que la dependencia que estás reemplazando. Esto además hace que tu código sea mucho menos transparente y en última instancia más difícil de probar.

La mayoría de los frameworks modernos tienen su propio Contenedor de Inyección de Dependencias que te permite conectar tus dependencias a través de la configuración. Lo que esto significa en la práctica es que puedes escribir código de aplicación que sea tan limpio y desacoplado como el framework en el que se basa.

Lectura Adicional

Volver al inicio

Bases de Datos

Muchas veces su código PHP utilizará una base de datos para persistir información. Tiene algunas opciones para conectarse e interactuar con su base de datos. La opción recomendada hasta PHP 5.1.0 era usar controladores nativos como mysqli, pgsql, mssql, etc.

Los drivers nativos son geniales si sólo estás usando una base de datos en tu aplicación, pero si, por ejemplo, estás usando MySQL y un poco de MSSQL, o necesitas conectarte a una base de datos Oracle, entonces no podrás usar los mismos drivers. Tendrás que aprender una API completamente nueva para cada base de datos — y eso puede llegar a ser una tontería.

Extensión MySQL

La extensión mysql para PHP es increíblemente antigua y ha sido reemplazada por otras dos extensiones:

No sólo el desarrollo de mysql se detuvo hace mucho tiempo, sino que además ha sido oficialmente eliminado en PHP 7.0.

Para ahorrarse la búsqueda en su php.ini para ver qué módulo está utilizando, una opción es buscar mysql_* en el editor de su elección. Si aparecen funciones como mysql_connect() y mysql_query(), entonces está usando mysql.

Incluso si aún no está utilizando PHP 7.x o posterior, no considerar esta actualización lo antes posible le acarreará mayores dificultades cuando se produzca la actualización de PHP. La mejor opción es reemplazar el uso de mysql por mysqli o PDO en tus aplicaciones dentro de tus propios calendarios de desarrollo para que no te veas apurado más adelante.

Si está actualizando de mysql a mysqli, tenga cuidado con las guías de actualización perezosas que sugieren que simplemente puede encontrar y reemplazar mysql_* con mysqli_*. No sólo es una simplificación excesiva, sino que pierde las ventajas que mysqli proporciona, como la vinculación de parámetros, que también se ofrece en PDO.

Extensión PDO

PDO es una librería de abstracción de conexión a bases de datos — incorporada en PHP desde 5.1.0 — que proporciona una interfaz común común para hablar con muchas bases de datos diferentes. Por ejemplo, puede utilizar código básicamente idéntico para interactuar con MySQL o SQLite:

<?php
// PDO + MySQL
$pdo = new PDO('mysql:host=example.com;dbname=database', 'user', 'password');
$statement = $pdo->query("SELECT some_field FROM some_table");
$row = $statement->fetch(PDO::FETCH_ASSOC);
echo htmlentities($row['some_field']);

// PDO + SQLite
$pdo = new PDO('sqlite:/path/db/foo.sqlite');
$statement = $pdo->query("SELECT some_field FROM some_table");
$row = $statement->fetch(PDO::FETCH_ASSOC);
echo htmlentities($row['some_field']);

PDO no traducirá sus consultas SQL o emulará las características que faltan; es puramente para la conexión a múltiples tipos de base de datos con la misma API.

Y lo que es más importante, PDO le permite inyectar de forma segura entradas ajenas (por ejemplo, IDs) en sus consultas SQL sin preocuparse por ataques de inyección SQL a bases de datos. Esto es posible utilizando sentencias PDO y parámetros vinculados.

Supongamos que un script PHP recibe un ID numérico como parámetro de consulta. Este ID debe ser usado para obtener un registro de usuario de una base de datos. Esta es la forma incorrecta de hacer esto:

<?php
$pdo = new PDO('sqlite:/path/db/users.db');
$pdo->query("SELECT name FROM users WHERE id = " . $_GET['id']); // <-- NO!

Este código es terrible. Estás insertando un parámetro de consulta sin procesar en una consulta SQL. Esto hará que te hackeen en un santiamén, usando una práctica llamada Inyección SQL. Imagínese si un hacker pasa un parámetro id inventivo llamando a una URL como http://domain.com/?id=1%3BDELETE+FROM+users. Esto establecerá la variable $_GET['id'] a 1;DELETE FROM users lo que borrará todos los usuarios. En su lugar, debería desinfectar/sanitizar la entrada de ID utilizando parámetros vinculados a PDO.

<?php
$pdo = new PDO('sqlite:/path/db/users.db');
$stmt = $pdo->prepare('SELECT name FROM users WHERE id = :id');
$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); // <-- filtre primero los datos (ver [Filtrado de Datos](#filtrado_de_datos)), especialmente importante para INSERT, UPDATE, etc.
$stmt->bindParam(':id', $id, PDO::PARAM_INT); // <-- SQL saneado automáticamente por PDO
$stmt->execute();

Este código es correcto. Utiliza un parámetro vinculado en una sentencia PDO. Esto escapa el ID de entrada externo antes de que sea introducido en la base de datos previniendo potenciales ataques de inyección SQL.

Para escrituras, como INSERT o UPDATE, es especialmente crítico filtrar sus datos primero y sanearlos para otras cosas (eliminación de etiquetas HTML, JavaScript, etc). PDO sólo lo desinfectará para SQL, no para su aplicación.

También hay que tener en cuenta que las conexiones a bases de datos consumen recursos y no era raro que se agotaran los recursos si las conexiones no se cerraban implícitamente, aunque esto era más común en otros lenguajes. Usando PDO puedes cerrar implícitamente la conexión destruyendo el objeto asegurándote de que todas las referencias restantes a él son borradas, es decir, establecidas a NULL. Si no hace esto explícitamente, PHP cerrará automáticamente la conexión cuando su script termine - a menos que esté usando conexiones persistentes.

Interacción con Bases de Datos

Cuando los desarrolladores comienzan a aprender PHP, a menudo terminan mezclando su interacción con la base de datos con su lógica de presentación, utilizando código que podría parecerse a esto:

<ul>
<?php
foreach ($db->query('SELECT * FROM table') as $row) {
    echo "<li>".$row['field1']." - ".$row['field1']."</li>";
}
?>
</ul>

Esta es una mala práctica por todo tipo de razones, principalmente que es difícil de depurar, difícil de probar, difícil de leer y que va a dar salida a un montón de campos si no pones un límite.

Aunque hay muchas otras soluciones para hacer esto - dependiendo de si prefieres POO o programación funcional - debe haber algún elemento de separación.

Considere el paso más básico:

<?php
function getAllFoos($db) {
    return $db->query('SELECT * FROM table');
}

$results = getAllFoos($db);
foreach ($results as $row) {
    echo "<li>".$row['field1']." - ".$row['field1']."</li>"; // BAD!!
}

Es un buen comienzo. Pon esos dos elementos en dos archivos diferentes y tendrás una separación limpia.

Crea una clase en la que colocar ese método y tendrás un “Modelo”. Crea un simple archivo .php para colocar la lógica de presentación y tendrás una “Vista”, que es muy parecido a MVC - una arquitectura OOP común para la mayoría de los frameworks.

foo.php

<?php
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8mb4', 'username', 'password');

// Ponga su modelo a disposición
include 'models/FooModel.php';

// Crear una instancia
$fooModel = new FooModel($db);
// Obtener la lista de Foos
$fooList = $fooModel->getAllFoos();

// Mostrar la vista
include 'views/foo-list.php';

models/FooModel.php

<?php
class FooModel
{
    public function __construct(protected PDO $db)
    {
    }

    public function getAllFoos() {
        return $this->db->query('SELECT * FROM table');
    }
}

views/foo-list.php

<?php foreach ($fooList as $row): ?>
    <li><?= $row['field1'] ?> - <?= $row['field1'] ?></li>
<?php endforeach ?>

Esto es esencialmente lo mismo que hacen la mayoría de los frameworks modernos, aunque un poco más manual. Puede que no necesites hacer todo eso cada vez, pero mezclar demasiada lógica de presentación e interacción con la base de datos puede ser un verdadero problema si alguna vez quieres hacer test unitarios a tu aplicación.

Capas de Abstracción

Muchos frameworks proporcionan su propia capa de abstracción que puede o no sentarse encima de PDO. Estos a menudo emulan características de un sistema de base de datos que falta en otro envolviendo sus consultas en métodos PHP, dándole abstracción de base de datos real en lugar de sólo la abstracción de conexión que PDO proporciona. Esto, por supuesto, añadirá un poco de sobrecarga, pero si usted está construyendo una aplicación portable que necesita trabajar con MySQL, PostgreSQL y SQLite entonces un poco de sobrecarga valdrá la pena por el bien de la limpieza del código.

Algunas capas de abstracción se han construido utilizando los estándares de espacio de nombres PSR-0 o PSR-4, por lo que pueden instalarse en cualquier aplicación que se desee:

Volver al inicio

Plantillas

Las Plantillas proporcionan una manera conveniente de separar la lógica de tu controlador y dominio de tu lógica de presentación. Las plantillas suelen contener el código HTML de tu aplicación, pero también se pueden utilizar otros formatos, como XML. A menudo se hace referencia a las plantillas como “vistas”, las cuales forman parte del segundo componente del patrón de arquitectura de software modelo-vista-controlador (MVC).

Beneficios

El principal beneficio de utilizar plantillas es la clara separación que crean entre la lógica de presentación y el resto de tu aplicación. Las plantillas tienen la única responsabilidad de mostrar contenido formateado. No son responsables de la búsqueda de datos, la persistencia u otras tareas más complejas. Esto lleva a un código más limpio y más legible, lo cual es especialmente útil en un entorno de equipo donde los desarrolladores trabajan en el código del lado del servidor (controladores, modelos) y los diseñadores trabajan en el código del lado del cliente (maquetado).

Las plantillas también mejoran la organización del código de presentación. Por lo general, las plantillas se colocan en una carpeta de “vistas” (“views”), cada una definida en un solo archivo. Este enfoque fomenta la reutilización del código, donde bloques más grandes de código se dividen en piezas más pequeñas y reutilizables, a menudo llamadas parciales (partials). Por ejemplo, el encabezado y el pie de página de tu sitio pueden definirse como plantillas, las cuales se incluyen antes y después de cada plantilla de página.

Finalmente, dependiendo de la biblioteca que uses, las plantillas pueden ofrecer más seguridad al escapar automáticamente el contenido generado por los usuarios. Algunas bibliotecas incluso ofrecen un sistema de “sand-boxing”, donde los diseñadores de plantillas solo tienen acceso a variables y funciones en una lista blanca.

Plantillas en PHP simple

Las plantillas en PHP simple son simplemente plantillas que utilizan código PHP nativo. Son una opción natural ya que PHP es, de hecho, un lenguaje de plantillas. Esto significa que puedes combinar código PHP con otro código, como HTML. Esto es beneficioso para los desarrolladores de PHP ya que no hay nueva sintaxis que aprender, conocen las funciones disponibles y sus editores de código ya tienen resaltado de sintaxis y autocompletado incorporado. Además, las plantillas en PHP simple tienden a ser muy rápidas, ya que no se requiere una etapa de compilación.

Todos los frameworks modernos de PHP emplean algún tipo de sistema de plantillas, la mayoría de los cuales utilizan PHP simple por defecto. Fuera de los frameworks, bibliotecas como Plates o Aura.View facilitan el trabajo con plantillas en PHP simple al ofrecer funcionalidades modernas de plantillas, como herencia, layouts y extensiones.

Ejemplo simple de una plantilla en PHP simple

Usando la biblioteca Plates.

<?php // user_profile.php ?>

<?php $this->insert('header', ['title' => 'User Profile']) ?>

<h1>User Profile</h1>
<p>Hello, <?=$this->escape($name)?></p>

<?php $this->insert('footer') ?>

Ejemplo de plantilla en PHP simple usando herencia

Haciendo uso de la biblioteca Plates.

<?php // template.php ?>

<html>
<head>
    <title><?=$title?></title>
</head>
<body>

<main>
    <?=$this->section('content')?>
</main>

</body>
</html>
<?php // user_profile.php ?>

<?php $this->layout('template', ['title' => 'User Profile']) ?>

<h1>User Profile</h1>
<p>Hello, <?=$this->escape($name)?></p>

Plantillas Compiladas

Aunque PHP ha evolucionado hasta convertirse en un lenguaje maduro, orientado a objetos, no ha mejorado mucho como lenguaje de plantillas. Las plantillas compiladas, como Twig, Brainy o Smarty*, llenan este vacío al ofrecer una nueva sintaxis diseñada específicamente para la creación de plantillas. Desde el escape automático hasta la herencia y estructuras de control simplificadas, las plantillas compiladas están diseñadas para ser más fáciles de escribir, más legibles y más seguras de usar. Las plantillas compiladas incluso pueden compartirse entre diferentes lenguajes, siendo Mustache un buen ejemplo de esto. Dado que estas plantillas deben ser compiladas, hay una ligera pérdida de rendimiento, sin embargo, esto es muy mínimo cuando se utiliza un sistema de caché apropiado.

*Aunque Smarty ofrece escape automático, NO está habilitado por defecto.

Ejemplo de una plantilla compilada

Haciendo uso de Twig.

{% include 'header.html' with {'title': 'User Profile'} %}

<h1>User Profile</h1>
<p>Hello, {{ name }}</p>

{% include 'footer.html' %}

Ejemplo de plantillas compiladas usando herencia

Usando Twig.

// template.html

<html>
<head>
    <title>{% block title %}{% endblock %}</title>
</head>
<body>

<main>
    {% block content %}{% endblock %}
</main>

</body>
</html>
// user_profile.html

{% extends "template.html" %}

{% block title %}User Profile{% endblock %}
{% block content %}
    <h1>User Profile</h1>
    <p>Hello, {{ name }}</p>
{% endblock %}

Lectura Adicional

Artículos & Tutoriales

Bibliotecas

Volver al inicio

Errores y Excepciones

Errores

En muchos lenguajes de programación “dependientes de excepciones”, cada vez que algo sale mal, se lanzará una excepción. Esta es una forma viable de manejar errores, pero PHP es un lenguaje “ligero en excepciones”. Aunque tiene excepciones y cada vez más partes del núcleo las usan cuando se trabaja con objetos, la mayoría de PHP intentará seguir procesando sin importar lo que suceda, a menos que ocurra un error fatal.

Por ejemplo:

$ php -a
php > echo $foo;
Notice: Undefined variable: foo in php shell code on line 1

Esto es solo un aviso, y PHP continuará felizmente. Esto puede ser confuso para aquellos que vienen de lenguajes “dependientes de excepciones”, porque en Python, por ejemplo, hacer referencia a una variable no definida lanzará una excepción:

$ python
>>> print foo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined

La única diferencia real es que Python reaccionará a cualquier pequeño problema, de manera que los desarrolladores puedan estar muy seguros de que cualquier posible problema o caso límite sea detectado, mientras que PHP seguirá procesando a menos que suceda algo extremo, en cuyo caso lanzará un error y lo reportará.

Gravedad de los Errores

PHP tiene varios niveles de gravedad de errores. Los tres tipos más comunes de mensajes son errores, avisos y advertencias. Estos tienen diferentes niveles de severidad: E_ERROR, E_NOTICE, y E_WARNING. Los errores son fallos fatales en tiempo de ejecución y usualmente son causados por fallos en tu código que deben ser corregidos, ya que harán que PHP deje de ejecutarse. Los avisos son mensajes de carácter informativo causados por código que puede o no causar problemas durante la ejecución del script, pero la ejecución no se detiene. Las advertencias son errores no fatales, y la ejecución del script no se detendrá.

Otro tipo de mensaje de error reportado en tiempo de compilación son los mensajes E_STRICT. Estos mensajes se utilizan para sugerir cambios en tu código para ayudar a garantizar la mejor interoperabilidad y compatibilidad con futuras versiones de PHP.

Cambiar el Comportamiento de Reporte de Errores en PHP

El reporte de errores se puede cambiar utilizando configuraciones de PHP y/o llamadas a funciones de PHP. Usando la función incorporada error_reporting(), puedes establecer el nivel de errores durante la ejecución del script pasando una de las constantes predefinidas de nivel de error. Por ejemplo, si solo quieres ver Errores y Advertencias, pero no Avisos (Notices), puedes configurarlo así:

<?php
error_reporting(E_ERROR | E_WARNING);

También puedes controlar si los errores se muestran en pantalla (útil en desarrollo) o si se ocultan y registran (útil en producción). Para más información, revisa la sección de Reporte de Errores.

Supresión de Errores en Línea

También puedes decirle a PHP que suprima errores específicos usando el Operador de Control de Errores @. Pones este operador al inicio de una expresión, y cualquier error que sea resultado directo de la expresión se silenciará.

<?php
echo @$foo['bar'];

Esto imprimirá $foo['bar'] si existe, pero simplemente devolverá nulo y no mostrará nada si la variable $foo o la clave 'bar' no existen. Sin el operador de control de errores, esta expresión podría crear un error PHP Notice: Undefined variable: foo o PHP Notice: Undefined index: bar.

Esto podría parecer una buena idea, pero hay algunas desventajas indeseables. PHP maneja expresiones con un @ de una manera menos eficiente que expresiones sin el @. La optimización prematura puede ser la raíz de todas las discusiones de programación, pero si el rendimiento es particularmente importante para tu aplicación/biblioteca, es importante entender las implicaciones de rendimiento del operador de control de errores.

En segundo lugar, el operador de control de errores suprime completamente el error. El error no se muestra y no se envía al registro de errores. Además, los sistemas PHP estándar/producción no tienen forma de desactivar el operador de control de errores. Aunque puede que tengas razón al considerar que el error que estás viendo es inofensivo, un error diferente y menos inofensivo será igual de silencioso.

Si hay una forma de evitar el uso del operador de supresión de errores, deberías considerarla. Por ejemplo, nuestro código anterior podría reescribirse de la siguiente manera:

<?php
// Null Coalescing Operator
echo $foo['bar'] ?? '';

Un caso donde podría tener sentido suprimir un error es cuando fopen() falla al encontrar un archivo. Podrías verificar la existencia del archivo antes de intentar cargarlo, pero si el archivo se elimina después de la verificación y antes de la llamada a fopen() (lo cual puede parecer imposible, pero puede ocurrir), entonces fopen() devolverá falso y lanzará un error. Esto es algo que PHP debería resolver, pero es un caso donde la supresión de errores podría parecer la única solución válida.

Mencionamos antes que no hay forma en un sistema PHP estándar de desactivar el operador de control de errores. Sin embargo, Xdebug tiene una configuración xdebug.scream en el archivo ini que deshabilitará el operador de control de errores. Puedes configurarlo en tu archivo php.ini de la siguiente manera:

xdebug.scream = On

También puedes establecer este valor en tiempo de ejecución con la función ini_set

<?php
ini_set('xdebug.scream', '1')

Esto es muy útil cuando estás depurando código y sospechas que un error informativo está siendo suprimido. Usa scream con cuidado y como una herramienta temporal de depuración. Hay mucho código de bibliotecas PHP que puede no funcionar con el operador de control de errores deshabilitado.

ErrorException

PHP es perfectamente capaz de ser un lenguaje de programación “dependiente de excepciones” y solo requiere unas pocas líneas de código para hacer el cambio. Básicamente, puedes lanzar tus “errores” como “excepciones” usando la clase ErrorException, que extiende la clase Exception.

Esta es una práctica común implementada por un gran número de frameworks modernos como Symfony y Laravel. En modo de depuración (o modo dev), ambos frameworks mostrarán una stack trace limpia y agradable.

También hay algunos paquetes disponibles para un mejor manejo y reporte de errores y excepciones, como Whoops!, que viene con la instalación predeterminada de Laravel y puede usarse en cualquier framework.

Al lanzar errores como excepciones durante el desarrollo, puedes manejarlos mejor que con el resultado habitual, y si ves una excepción durante el desarrollo, puedes envolverla en una declaración de captura con instrucciones específicas sobre cómo manejar la situación. Cada excepción que captures hace que tu aplicación sea un poco más robusta.

Más información sobre esto y detalles sobre cómo usar ErrorException con el manejo de errores se pueden encontrar en la Clase ErrorException.

Excepciones

Las excepciones son una parte estándar de la mayoría de los lenguajes de programación populares, pero a menudo son pasadas por alto por los programadores de PHP. Lenguajes como Ruby son extremadamente dependientes de excepciones, así que cada vez que algo sale mal, como una falla en una solicitud HTTP, un error en una consulta a la base de datos, o incluso si no se puede encontrar un recurso de imagen, Ruby (o las gems utilizadas) lanzará una excepción a la pantalla, lo que significa que instantáneamente sabrás que hay un error.

PHP, por su parte, es bastante indulgente con esto, y una llamada a file_get_contents() generalmente solo te devolverá un FALSE y un warning. Muchos frameworks antiguos de PHP, como CodeIgniter, simplemente devolverán un falso, registrarán un mensaje en sus registros y tal vez te permitan usar un método como $this->upload->get_error() para ver qué salió mal. El problema aquí es que tienes que ir a buscar un error y consultar la documentación para ver cuál es el método de error para esta clase, en lugar de que se te haga extremadamente obvio.

Otro problema es cuando las clases lanzan automáticamente un error en la pantalla y terminan el proceso. Cuando haces esto, detienes a otro desarrollador de poder manejar dinámicamente ese error. Las excepciones deben lanzarse para hacer que un desarrollador sea consciente de un error; luego puede elegir cómo manejarlo. Por ejemplo:

<?php
$email = new Fuel\Email;
$email->subject('My Subject');
$email->body('How the heck are you?');
$email->to('guy@example.com', 'Some Guy');

try
{
    $email->send();
}
catch(Fuel\Email\ValidationFailedException $e)
{
    // La validación falló
}
catch(Fuel\Email\SendingFailedException $e)
{
    // El controlador no pudo enviar el correo electrónico
}
finally
{
    // Ejecutado independientemente de si se ha lanzado una excepción, y antes de que se reanude la ejecución normal
}

Excepciones SPL

La clase genérica Exception proporciona muy poco contexto de depuración para el desarrollador; sin embargo, para remediar esto, es posible crear un tipo de Exception especializado al sub-clasificar la clase genérica Exception:

<?php
class ValidationException extends Exception {}

Esto significa que puedes agregar múltiples bloques de captura y manejar diferentes excepciones de manera diferente. Esto puede llevar a la creación de un gran número de Excepciones personalizadas, algunas de las cuales podrían haberse evitado utilizando las excepciones SPL proporcionadas en la extensión SPL.

Si, por ejemplo, utilizas el método mágico __call() y se solicita un método no válido, en lugar de lanzar una Excepción estándar que es vaga, o crear una Excepción personalizada solo para eso, podrías simplemente throw new BadMethodCallException;.

Volver al inicio

Seguridad

El mejor recurso que he encontrado sobre seguridad en PHP es The 2018 Guide to Building Secure PHP Software por Paragon Initiative.

Seguridad de Aplicaciones Web

Es muy importante que todo desarrollador PHP aprenda los fundamentos de la seguridad de las aplicaciones web, que pueden dividirse en un puñado de temas generales:

  1. Separación de código y datos.
    • Cuando los datos se ejecutan como código, se obtiene Inyección SQL, Cross-Site Scripting, Inclusión de Archivos Locales/Remotos, etc.
    • Cuando el código se imprime como datos, se producen fugas de información (revelación del código fuente o, en el caso de los programas en C, información suficiente para saltarse ASLR).
  2. Lógica de aplicación.
    • Ausencia de controles de autenticación o autorización.
    • Validación de las entradas.
  3. Entorno operativo.
    • Versiones de PHP.
    • Bibliotecas de terceros.
    • Sistema operativo.
  4. Puntos débiles de la criptografía.

Hay gente mala lista y dispuesta a explotar su aplicación web. Es importante que tome las precauciones necesarias para reforzar la seguridad de su aplicación web. Por suerte, la buena gente de The Open Web Application Security Project (OWASP) ha recopilado una lista exhaustiva de problemas de seguridad conocidos y métodos para protegerse contra ellos. Se trata de una lectura obligatoria para los desarrolladores preocupados por la seguridad. Survive The Deep End: PHP Security de Padraic Brady es también otra buena guía de seguridad de aplicaciones web para PHP.

Hashing de Contraseñas

Al final, todo el mundo crea una aplicación PHP que depende del inicio de sesión del usuario. Los nombres de usuario y las contraseñas se almacenan en una base de datos y luego se utilizan para autenticar a los usuarios al iniciar sesión.

Es importante hacer hash correctamente a las contraseñas antes de almacenarlas. Hacer un hash y cifrar son dos cosas muy diferentes que a menudo se confunden.

El hashing es una función irreversible y unidireccional. Produce una cadena de longitud fija que no se puede invertir. Esto significa que se puede comparar un hash con otro para determinar si ambos proceden de la misma cadena de origen, pero no se puede determinar la cadena original a partir del hash. Si las contraseñas no tienen hash y un tercero no autorizado accede a la base de datos, todas las cuentas de usuario estarán en peligro.

A diferencia del hashing, el cifrado es reversible (siempre que se tenga la clave). El cifrado es útil en otros ámbitos, pero es una mala estrategia para almacenar contraseñas de forma segura.

Las contraseñas también deben ser salados (del inglés “salted”) individualmente, añadiendo una cadena aleatoria a cada contraseña antes de aplicar el hash. Así se evitan los ataques de diccionario y el uso de “tablas arco iris” (una lista inversa de hashes criptográficos para contraseñas comunes).

El hashing y el salting son vitales, ya que a menudo los usuarios utilizan la misma contraseña para varios servicios y la calidad de las contraseñas puede ser deficiente.

Además, debe utilizar un algoritmo especializado de password hashing en lugar de una función hash criptográfica rápida de uso general (por ejemplo, SHA256). La lista corta de algoritmos hash de contraseña aceptables (a junio de 2018) para usar son:

Afortunadamente, hoy en día PHP facilita esta tarea.

Hashear contraseñas con password_hash

En PHP 5.5 se introdujo password_hash(). En este momento usa BCrypt, el algoritmo más fuerte actualmente soportado por PHP. Sin embargo, será actualizado en el futuro para soportar más algoritmos según sea necesario. La librería password_compat fue creada para proporcionar compatibilidad con PHP >= 5.3.7.

A continuación, hacemos un hash de una cadena y lo comparamos con una nueva cadena. Debido a que nuestras dos cadenas de origen son diferentes (‘secret-password’ vs. ‘bad-password’) este login fallará.

<?php
require 'password.php';

$passwordHash = password_hash('secret-password', PASSWORD_DEFAULT);

if (password_verify('bad-password', $passwordHash)) {
    // Contraseña correcta
} else {
    // Contraseña inválida
}

password_hash() se encarga del salado de contraseñas por ti. La sal se almacena, junto con el algoritmo y el “coste”, como parte del hash. password_verify() extrae esto para determinar cómo comprobar la contraseña, por lo que no necesitas un campo de base de datos separado para almacenar tus sales.

Filtrado de Datos

Nuca (nunca) confíes en la introducción de datos ajenos en tu código PHP. Siempre sanee y valide la entrada ajena antes de usarla en el código. Las funciones filter_var() y filter_input() pueden sanear texto y validar formatos de texto (por ejemplo, direcciones de correo electrónico).

La entrada ajena puede ser cualquier cosa: datos de formulario $_GET y $_POST, algunos valores en el superglobal $_SERVER, y el cuerpo de la petición HTTP a través de fopen('php://input', 'r'). Recuerde, la entrada de datos externos no se limita a los datos del formulario enviados por el usuario. Los archivos cargados y descargados, los valores de sesión, los datos de cookies y los datos de servicios web de terceros también son entradas de datos externos.

Aunque la introducción de datos ajenos pueden almacenarse, combinarse y accederse posteriormente, siguen siendo entrada ajena. Cada vez que proceses, des salida, concatenes o incluyas datos en tu código, pregúntate si los datos están filtrados correctamente y si son de confianza.

Los datos pueden filtrarse de forma diferente en función de su finalidad. Por ejemplo, cuando una entrada ajena no filtrada se pasa a la salida de una página HTML, ¡puede ejecutar HTML y JavaScript en su sitio! Esto se conoce como Cross-Site Scripting (XSS) y puede ser un ataque muy peligroso. Una forma de evitar XSS es desinfectar todos los datos generados por el usuario antes de enviarlos a la página eliminando las etiquetas HTML con la función strip_tags() o escapando caracteres con significado especial en sus respectivas entidades HTML con las funciones htmlentities() o htmlspecialchars().

Otro ejemplo es pasar opciones para ser ejecutadas en la línea de comandos. Esto puede ser extremadamente peligroso (y suele ser una mala idea), pero puede utilizar la función integrada escapeshellarg() para desinfectar los argumentos del comando ejecutado.

Un último ejemplo es aceptar una entrada extraña para determinar un fichero a cargar del sistema de ficheros. Esto puede ser explotado cambiando el nombre del archivo a una ruta de archivo. Es necesario eliminar "/", "../", bytes nulos, u otros caracteres de la ruta del archivo para que no pueda cargar archivos ocultos, no públicos o sensibles.

Sanitization

La sanitización elimina (o escapa) caracteres ilegales o inseguros de los datos de entrada ajenos.

Por ejemplo, debe desinfectar los datos de entrada ajenos antes de incluirla en HTML o insertarla en una consulta SQL sin procesar. Cuando use parámetros vinculados con PDO, se limpiarán los datos de entrada por usted.

A veces es necesario permitir algunas etiquetas HTML seguras en los datos de entrada al incluirla en la página HTML. Esto es muy difícil de hacer y muchos lo evitan usando otros formatos más restringidos como Markdown o BBCode, aunque para ello existen librerías de filtrado de datos como HTML Purifier.

Ver Filtros de Sanitización

Deserialización

Es peligroso deserializar datos de usuarios u otras fuentes no confiables utilizando unserialize(). Hacerlo puede permitir a usuarios maliciosos instanciar objetos (con propiedades definidas por el usuario) cuyos destructores serán ejecutados, incluso si los propios objetos no son utilizados. Por lo tanto, debes evitar deserializar datos que no sean de confianza.

Utiliza un formato de intercambio de datos seguro y estándar como JSON (a través de json_decode y json_encode) si necesitas pasar datos serializados al usuario.

Validación

La validación garantiza que la entrada de datos ajenos es la esperada. Por ejemplo, es posible que desee validar una dirección de correo electrónico, un número de teléfono o la edad al procesar un envío de registro.

Ver Filtros de Validación

Archivos de Configuración

Al crear archivos de configuración para sus aplicaciones, las mejores prácticas recomiendan que se siga uno de los siguientes métodos siguientes:

Registro de Globales

NOTA: A partir de PHP 5.4.0 el parámetro register_globals ha sido eliminado y ya no puede ser usado. Esto sólo se incluye como una advertencia para cualquier persona en el proceso de actualización de una aplicación de legado.

Cuando está activada, la configuración register_globals hace que varios tipos de variables (incluyendo las de $_POST, $_GET y $_REQUEST) estén disponibles en el ámbito global de tu aplicación. Esto puede llevar fácilmente a problemas de seguridad ya que tu aplicación no puede saber de dónde vienen los datos.

Por ejemplo: $_GET['foo'] estaría disponible a través de $foo, lo cual puede anular variables que hayan sido declaradas.

Si estás usando PHP < 5.4.0 asegúrate de que register_globals está desactivado.

Reporte de Errores

El registro de errores puede ser útil para encontrar los puntos problemáticos en su aplicación, pero también puede exponer información sobre la estructura de su aplicación al mundo exterior. Para proteger eficazmente su aplicación de los problemas que podrían ser causados por la salida de estos mensajes, es necesario configurar el servidor de manera diferente en el desarrollo frente a la producción (en vivo).

Desarrollo

Para mostrar todos los errores posibles durante el desarrollo, configure los siguientes parámetros en su php.ini:

display_errors = On
display_startup_errors = On
error_reporting = -1
log_errors = On

Pasando el valor -1 se mostrarán todos los errores posibles, incluso cuando se añadan nuevos niveles y constantes en futuras versiones de PHP. La constante E_ALL también se comporta de esta manera a partir de PHP 5.4. - php.net

La constante de nivel de error E_STRICT se introdujo en 5.3.0 y no forma parte de E_ALL, sin embargo pasó a formar parte de E_ALL en 5.4.0. ¿Qué significa esto? En términos de informar de todos los errores posibles en la versión 5.3 significa que debe utilizar -1 o E_ALL | E_STRICT.

Informar de todos los errores posibles por versión de PHP

Producción

Para ocultar los errores en su entorno de producción, configure su php.ini así:

display_errors = Off
display_startup_errors = Off
error_reporting = E_ALL
log_errors = On

Con esta configuración en producción, los errores seguirán registrándose en los registros de errores del servidor web, pero no se mostrarán al usuario. Para más información sobre estos ajustes, consulte el manual de PHP:

Volver al inicio

Pruebas

Escribir pruebas automatizadas para su código PHP se considera una buena práctica y puede conducir a aplicaciones bien construidas. Las pruebas automatizadas son una gran herramienta para asegurarse de que su aplicación no se rompe cuando usted está haciendo cambios o añadiendo nuevas funcionalidades y no debe ser ignorada.

Existen varios tipos de herramientas de pruebas (o frameworks) disponibles para PHP, que utilizan diferentes enfoques - todos los cuales tratan de evitar las pruebas manuales y la necesidad de grandes equipos de Aseguramiento de la Calidad (QA, por sus siglas en inglés), sólo para asegurarse de que los cambios recientes no rompen la funcionalidad existente.

Desarrollo Guiado por Pruebas

De Wikipedia:

El desarrollo dirigido por pruebas (TDD, por sus siglas en inglés) es un proceso de desarrollo de software que se basa en la repetición de un ciclo de desarrollo muy corto: primero, el desarrollador escribe un caso de prueba automatizado que falla y que define una mejora deseada o una nueva función; después, produce código para superar esa prueba y, por último, refactoriza el nuevo código para que cumpla unos estándares aceptables. Kent Beck, a quien se atribuye haber desarrollado o “redescubierto” la técnica, declaró en 2003 que el TDD fomenta los diseños sencillos e inspira confianza.

Hay varios tipos diferentes de pruebas que puede realizar para su aplicación:

Pruebas Unitarios

Las pruebas unitarias son un enfoque de programación para garantizar que las funciones, clases y métodos funcionan como se espera, desde el momento en que se construyen hasta el final del ciclo de desarrollo. Comprobando los valores que entran y salen de varias funciones y métodos, puedes asegurarte de que la lógica interna funciona correctamente. Mediante el uso de la inyección de dependencias y la creación de clases y stubs “simulados”, puede comprobar que las dependencias se utilizan correctamente para mejorar aún más la cobertura de las pruebas.

Cuando creas una clase o función deberías crear una prueba unitaria para cada comportamiento que deba tener. A un nivel muy básico deberías asegurarte de que da error si le envías argumentos erróneos y asegurarte de que funciona si le envías argumentos válidos. Esto ayudará a asegurar que cuando hagas cambios a esta clase o función más adelante en el ciclo de desarrollo, la funcionalidad antigua siga funcionando como se esperaba. La única alternativa a esto sería var_dump() en un test.php, que no es forma de construir una aplicación - grande o pequeña.

La otra utilidad de las pruebas unitarias es contribuir al código abierto. Si puedes escribir una prueba que muestre una funcionalidad rota (es decir, que falla), luego arreglarla, y mostrar que la prueba pasa, es mucho más probable que los parches sean aceptados. Si diriges un proyecto que acepta pull requests, deberías sugerirlo como requisito.

PHPUnit PHPUnit es el marco de pruebas de facto para escribir pruebas unitarias para aplicaciones PHP, pero existen varias alternativas:

Pruebas de Integración

De Wikipedia:

Las pruebas de integración (a veces denominadas integración y pruebas, abreviadas “I&T”) son la fase de las pruebas de software en la que se combinan módulos de software individuales y se prueban como un grupo. Se realiza después de las pruebas unitarias y antes de las de validación. Las pruebas de integración toman como entrada los módulos que se han probado por unidades, los agrupan en conjuntos más grandes, aplican a esos conjuntos las pruebas definidas en un plan de pruebas de integración y entregan como salida el sistema integrado listo para las pruebas del sistema.

Muchas de las mismas herramientas que pueden utilizarse para las pruebas unitarias pueden emplearse para las pruebas de integración, ya que se utilizan muchos de los mismos principios.

Pruebas Funcionales

A veces también conocidas como pruebas de aceptación, las pruebas funcionales consisten en utilizar herramientas para crear pruebas automatizadas que realmente utilizan la aplicación, en lugar de limitarse a verificar que las unidades individuales de código se comportan correctamente y que las unidades individuales pueden hablar entre sí correctamente. Estas herramientas suelen funcionar utilizando datos reales y simulando usuarios reales de la aplicación.

Herramientas de Pruebas Funcionales

Desarrollo Guiado por Comportamiento

Existen dos tipos diferentes de Desarrollo Guiado por Comportamiento (BDD): SpecBDD y StoryBDD. SpecBDD se centra en el comportamiento técnico del código, mientras que StoryBDD se centra en los comportamientos o interacciones del negocio o de las características. PHP tiene frameworks para ambos tipos de BDD.

Con StoryBDD, se escriben historias legibles que describen el comportamiento de la aplicación. Estas historias se pueden ejecutar como pruebas reales contra su aplicación. El framework utilizado en aplicaciones PHP para StoryBDD es Behat, que está inspirado en el proyecto Cucumber de Ruby e implementa el DSL Gherkin para describir el comportamiento de las características.

Con SpecBDD, se escriben especificaciones que describen cómo debe comportarse el código real. En lugar de probar una función o método, usted está describiendo cómo esa función o método debe comportarse. PHP ofrece el framework PHPSpec para este propósito. Este framework está inspirado en el proyecto RSpec para Ruby.

Enlaces de BDD

Herramientas de Prueba Complementarias

Además de las pruebas individuales y los marcos orientados al comportamiento, también hay una serie de marcos genéricos y bibliotecas de ayuda útiles para cualquier enfoque preferido.

Enlaces a Herramientas

Volver al inicio

Servidores y Despliegue

Las aplicaciones PHP se pueden desplegar y correr en servidores web de producción de multiples maneras.

Plataforma como Servicio (PaaS)

Una PaaS (Platform as a Service) proporciona la arquitectura de sistema y de red necesaria para ejecutar aplicaciones PHP en la web. Esto significa que se requiere poca o ninguna configuración para lanzar aplicaciones PHP o frameworks de PHP.

Recientemente, PaaS se ha convertido en un método popular para desplegar, alojar y escalar aplicaciones PHP de todos los tamaños. Puedes encontrar una lista de proveedores PaaS para PHP en nuestra sección de recursos.

Servidores Virtuales o Dedicados

Si te sientes cómodo con la administración de sistemas, o estás interesado en aprender, los servidores virtuales o dedicados te permiten tener control total sobre el entorno de producción de tu aplicación.

nginx y PHP-FPM

PHP, con el gestor de procesos FastCGI (FastCGI Process Manager, FPM por sus siglas en inglés) integrado en PHP, se complementa muy bien con nginx, el cual es un servidor web ligero y de alto rendimiento. Utiliza menos memoria que Apache y puede manejar mejor más solicitudes concurrentes. Esto es especialmente importante en servidores virtuales que no tienen mucha memoria disponible.

Apache y PHP

PHP y Apache tienen una larga historia juntos. Apache es ampliamente configurable y cuenta con muchos módulos disponibles para extender su funcionalidad. Es una elección común para servidores compartidos y una configuración sencilla para frameworks PHP y aplicaciones de código abierto como WordPress. Desafortunadamente, Apache por defecto utiliza más recursos que nginx y no puede manejar tantos visitantes al mismo tiempo.

Apache posee varias configuraciones posibles para ejecutar PHP. La más común y más fácil de configurar es el prefork MPM con mod_php. Aunque no es la más eficiente en términos de memoria, es la más sencilla de hacer funcionar y utilizar. Probablemente sea la mejor opción si no deseas profundizar demasiado en los aspectos de administración del servidor. Ten en cuenta que si usas mod_php DEBES usar el prefork MPM.

Alternativamente, si quieres sacar más rendimiento y estabilidad de Apache, puedes aprovechar el mismo sistema FPM que nginx y ejecutar el worker MPM o el event MPM con mod_fastcgi o mod_fcgid. Esta configuración será significativamente más eficiente en memoria y mucho más rápida, pero requiere más trabajo para configurarla.

Si estás ejecutando Apache 2.4 o posterior, puedes usar mod_proxy_fcgi para obtener un gran rendimiento y es fácil de configurar.

Servidores Compartidos

PHP debe su popularidad a los servidores compartidos. Es difícil encontrar un hosting sin PHP instalado, pero asegúrate de que sea la última versión. Los servidores compartidos permiten que tú y otros desarrolladores puedan desplegar sitios web en una sola máquina. La ventaja de esto es que se ha convertido en una mercancía barata. La desventaja es que nunca sabes qué tipo de problemas pueden causar tus vecinos; sobrecargar el servidor o abrir agujeros de seguridad son las principales preocupaciones. Si el presupuesto de tu proyecto puede permitirse evitar los servidores compartidos, deberías hacerlo.

Asegúrate de que tus servidores compartidos ofrezcan las últimas versiones de PHP.

Construir y Desplegar su Aplicación

Si te encuentras haciendo cambios manuales en el esquema de la base de datos o ejecutando tus pruebas manualmente antes de actualizar tus archivos (manualmente), ¡piénsalo dos veces! Con cada tarea manual adicional necesaria para desplegar una nueva versión de tu aplicación, las posibilidades de cometer errores potencialmente fatales aumentan. Ya sea que estés lidiando con una actualización simple, un proceso de compilación más complejo o incluso una estrategia de integración continua, la automatización es tu aliada.

Estas son las tareas que podrías querer automatizar:

Herramientas de Despliegue

Las herramientas de despliegue se pueden describir como una colección de scripts que llevan a cabo tareas comunes de despliegue de software. La herramienta de despliegue no es parte de tu software, actúa sobre tu software desde ‘afuera’.

Existen muchas herramientas de código abierto disponibles para ayudarte con la automatización de compilación y despliegue, algunas están escritas en PHP y otras no. Esto no debería impedirte usarlas si son más adecuadas para ese trabajo específico. Aquí tienes algunos ejemplos:

Phing puede controlar tu proceso de empaquetado, despliegue o pruebas desde un archivo XML. Phing (basado en Apache Ant) proporciona un conjunto completo de tareas que usualmente se necesitan para instalar o actualizar una aplicación web y se puede extender con tareas personalizadas adicionales, escritas en PHP. Es una herramienta sólida y robusta que ha estado presente durante mucho tiempo, sin embargo, podría percibirse como una herramienta un poco anticuada debido a la forma en que maneja la configuración (archivos XML).

Capistrano es un sistema para programadores intermedios a avanzados que permite ejecutar comandos de manera estructurada y repetible en una o más máquinas remotas. Está preconfigurado para desplegar aplicaciones Ruby on Rails, sin embargo, también puedes desplegar sistemas PHP sin problemas. El uso exitoso de Capistrano depende de un conocimiento práctico de Ruby y Rake.

Ansistrano es un grupo de roles de Ansible para gestionar fácilmente el proceso de despliegue (despliegue y reversión) de aplicaciones de scripting como PHP, Python y Ruby. Es un port de Ansible para Capistrano. Ya ha sido utilizado por muchas empresas de PHP.

Deployer es una herramienta de despliegue escrita en PHP. Es simple y funcional. Sus características incluyen ejecución de tareas en paralelo, despliegue atómico y mantener de la consistencia entre servidores. Dispone recetas de tareas comunes para Symfony, Laravel, Zend Framework y Yii. El artículo de Younes Rafie, Despliegue Fácil de Aplicaciones PHP con Deployer, es un gran tutorial para desplegar tu aplicación con esta herramienta.

Magallanes es otra herramienta escrita en PHP, con una configuración sencilla con archivos YAML. Soporta múltiples servidores y entornos, despliegue atómico, y tiene algunas tareas integradas que puedes utilizar para herramientas y frameworks comunes.

Lectura adicional:

Aprovisionamiento de Servidor

Gestionar y configurar servidores puede ser una tarea abrumadora cuando se esta trabajando con muchos servidores. Existen herramientas para lidiar con esto, que te permiten automatizar tu infraestructura y asegurarte de que tienes los servidores correctos y que están configurados apropiadamente. A menudo, se integran con los principales proveedores de alojamiento en la nube (Amazon Web Services, Heroku, DigitalOcean, etc.) para gestionar instancias, lo que facilita mucho la escalabilidad de una aplicación.

Ansible es una herramienta que gestiona tu infraestructura a través de archivos YAML. Es fácil para comenzar a usar y puede gestionar aplicaciones complejas y a gran escala. Hay una API para gestionar instancias en la nube y puede administrarlas a través de un inventario dinámico utilizando ciertas herramientas.

Puppet es una herramienta que tiene su propio lenguaje y tipos de archivos para gestionar servidores y configuraciones. Se puede usar en una configuración maestro/cliente o en un modo “sin maestro”. En el modo maestro/cliente, los clientes consultan al maestro(s) central(es) para obtener nuevas configuraciones a intervalos establecidos y se actualizan si es necesario. En el modo sin maestro, puedes enviar cambios a tus nodos.

Chef es un potente marco de integración de sistemas basado en Ruby que puedes utilizar para construir todo tu entorno de servidor o máquinas virtuales. Se integra bien con Amazon Web Services a través de su servicio llamado OpsWorks.

Lectura adicional:

Integración Continua

La Integración Continua es una práctica de desarrollo de software en la que los miembros de un equipo integran su trabajo con frecuencia, generalmente cada persona integra al menos una vez al día, lo que conduce a múltiples integraciones por día. Muchos equipos descubren que este enfoque reduce significativamente los problemas de integración y permite desarrollar software cohesivo de manera más rápida.

– Martin Fowler

Existen diferentes maneras de implementar la integración continua para PHP. Travis CI ha hecho un gran trabajo al hacer que la integración continua sea una realidad incluso para proyectos pequeños. Travis CI es un servicio de integración continua hospedado. Se puede integrar con GitHub y ofrece soporte para muchos lenguajes, incluido PHP. GitHub tiene flujos de trabajo de integración continua con GitHub Actions.

Lectura adicional:

Volver al inicio

Virtualización

Ejecutar tu aplicación en diferentes entornos durante el desarrollo y la producción puede dar lugar a errores extraños cuando la pongas en marcha. También es complicado mantener diferentes entornos de desarrollo actualizados con la misma versión de todas las bibliotecas utilizadas al trabajar con un equipo de desarrolladores.

Si estás desarrollando en Windows y desplegando en Linux (o cualquier sistema que no sea Windows) o si estás desarrollando en equipo, deberías considerar usar una máquina virtual. Esto puede sonar complicado, pero además de los entornos de virtualización ampliamente conocidos como VMware o VirtualBox, hay herramientas adicionales que pueden ayudarte a configurar un entorno virtual en unos pocos pasos sencillos.

Vagrant

Vagrant te ayuda a construir tus máquinas virtuales sobre los entornos virtuales conocidos y configurará estos entornos en base a un único archivo de configuración. Estas máquinas pueden configurarse manualmente, o puedes usar software de “provisión” como Puppet o Chef para hacerlo por ti. Proveer la caja base es una excelente manera de asegurarte de que múltiples máquinas se configuren de manera idéntica y elimina la necesidad de mantener listas de comandos de “configuración” complicadas. También puedes “destruir” tu caja base y recrearla sin muchos pasos manuales, lo que facilita crear una instalación “nueva”.

Vagrant crea carpetas para compartir tu código entre tu máquina host y tu máquina virtual, lo que significa que puedes crear y editar tus archivos en tu máquina host y luego ejecutar el código dentro de tu máquina virtual.

Docker

Docker — una alternativa ligera a una máquina virtual completa — se llama así porque se centra en “contenedores”. Un contenedor es un bloque de construcción que, en el caso más simple, realiza una tarea específica, por ejemplo, ejecutar un servidor web. Una “imagen” es el paquete que utilizas para construir el contenedor; Docker tiene un repositorio lleno de ellas.

Una aplicación típica de LAMP podría tener tres contenedores: un servidor web, un proceso de PHP-FPM y MySQL. Al igual que con las carpetas compartidas en Vagrant, puedes dejar tus archivos de aplicación donde están y decirle a Docker dónde encontrarlos.

Puedes generar contenedores desde la línea de comandos (ver ejemplo a continuación) o, para facilitar el mantenimiento, construir un archivo docker-compose.yml para tu proyecto especificando cuáles crear y cómo se comunican entre sí.

Docker puede ser útil si estás desarrollando múltiples sitios web y deseas la separación que viene de instalar cada uno en su propia máquina virtual, pero no tienes el espacio en disco necesario o el tiempo para mantener todo actualizado. Es eficiente: la instalación y las descargas son más rápidas, solo necesitas almacenar una copia de cada imagen, sin importar cuántas veces se use, los contenedores requieren menos RAM y comparten el mismo núcleo del sistema operativo, por lo que puedes tener más servidores ejecutándose simultáneamente, y lleva solo unos segundos detenerlos y iniciarlos, sin necesidad de esperar a que arranque un servidor completo.

Ejemplo: Ejecutando tus Aplicaciones PHP en Docker

Después de instalar Docker en tu máquina, puedes iniciar un servidor web con un solo comando. Lo siguiente descargará una instalación completamente funcional de Apache con la última versión de PHP, mapear el directorio /path/to/your/php/files a la raíz del documento, que podrás ver en http://localhost:8080

docker run -d --name my-php-webserver -p 8080:80 -v /path/to/your/php/files:/var/www/html/ php:apache

Esto inicializará y lanzará tu contenedor. -d hace que se ejecute en segundo plano. Para detenerlo y reiniciarlo, simplemente ejecuta docker stop my-php-webserver y docker start my-php-webserver (los otros parámetros no son necesarios nuevamente).

Aprende más sobre Docker

El comando anterior muestra una forma rápida de ejecutar un servidor básico. Hay mucho más que puedes hacer (y miles de imágenes preconstruidas en Docker Hub). Tómate el tiempo para aprender la terminología y leer la Guía del Usuario de Docker para sacarle el máximo provecho, y no ejecutes código aleatorio que hayas descargado sin verificar que sea seguro; las imágenes no oficiales pueden no tener los últimos parches de seguridad. Si tienes dudas, mantente en los repositorios oficiales.

El sitio PHPDocker.io generará automáticamente todos los archivos que necesitas para un stack LAMP/LEMP totalmente funcional, incluyendo tu elección de versión de PHP y extensiones.

Volver al inicio

Almacenamiento en Caché

PHP es bastante rápido por sí mismo, pero pueden surgir cuellos de botella al hacer conexiones remotas, cargar archivos, etc. Afortunadamente, hay varias herramientas disponibles para acelerar ciertas partes de tu aplicación o reducir la cantidad de veces que estas tareas que consumen tiempo necesitan ejecutarse.

Caché Opcode

Cuando se ejecuta un archivo PHP, primero debe ser compilado en opcodes (instrucciones en lenguaje de máquina para la CPU). Si el código fuente no ha cambiado, los opcodes serán los mismos, por lo que este paso de compilación se convierte en un desperdicio de recursos de la CPU.

Un caché de opcodes evita la compilación redundante al almacenar los opcodes en la memoria y reutilizarlos en llamadas sucesivas. Normalmente verifica la firma o el tiempo de modificación del archivo primero, en caso de que haya habido algún cambio.

Es probable que un caché de opcodes mejore significativamente la velocidad de tu aplicación. Desde PHP 5.5 hay uno incorporado: Zend OPcache. Dependiendo de tu paquete o distribución de PHP, normalmente está activado por defecto; revisa opcache.enable y la salida de phpinfo() para asegurarte. Para versiones anteriores, hay una extensión de PECL.

Lee más sobre cachés de opcodes:

Caché de Objetos

A veces puede ser beneficioso almacenar en caché objetos individuales en tu código, como en el caso de datos que son costosos de obtener o llamadas a bases de datos donde el resultado es poco probable que cambie. Puedes usar software de almacenamiento en caché de objetos para mantener estos fragmentos de datos en memoria para un acceso extremadamente rápido más adelante. Si guardas estos elementos en un almacén de datos después de recuperarlos, y luego los obtienes directamente desde la caché para las solicitudes siguientes, puedes obtener una mejora significativa en el rendimiento, así como reducir la carga en tus servidores de bases de datos.

Muchas de las soluciones populares de almacenamiento en caché de bytecode también te permiten almacenar datos personalizados, por lo que hay aún más razones para aprovecharlas. APCu y WinCache proporcionan APIs para guardar datos de tu código PHP en su caché de memoria.

Los sistemas de almacenamiento en caché de objetos en memoria más comúnmente utilizados son APCu y memcached. APCu es una excelente opción para el almacenamiento en caché de objetos, incluye una API simple para agregar tus propios datos a su caché de memoria y es muy fácil de configurar y usar. La única limitación real de APCu es que está vinculado al servidor donde está instalado. Memcached, por otro lado, se instala como un servicio separado y se puede acceder a él a través de la red, lo que significa que puedes almacenar objetos en un almacén de datos de alta velocidad en una ubicación central y muchos sistemas diferentes pueden obtenerlos desde allí.

Ten en cuenta que si la caché se comparte o no entre procesos PHP depende de cómo se utilice PHP. Al ejecutar PHP a través de PHP-FPM, la caché se comparte entre todos los procesos de todos los pools. Al ejecutar PHP como una aplicación (Fast-)CGI dentro de tu servidor web, la caché no se comparte, es decir, cada proceso PHP tendrá sus propios datos APCu. Al ejecutar PHP desde la línea de comandos, la caché no se comparte y solo existirá durante la duración del comando. Por lo tanto, debes ser consciente de tu situación y objetivos. Además, podrías considerar usar memcached en su lugar, ya que no está vinculado a los procesos PHP.

En una configuración en red, APCu generalmente superará a memcached en términos de velocidad de acceso, pero memcached podrá escalar más rápido y en mayor medida. Si no esperas tener varios servidores ejecutando tu aplicación, o no necesitas las características adicionales que ofrece memcached, entonces probablemente APCu sea tu mejor opción para el almacenamiento en caché de objetos.

Ejemplo de lógica usando APCu:

<?php
// check if there is data saved as 'expensive_data' in cache
$data = apcu_fetch('expensive_data');
if ($data === false) {
    // data is not in cache; save result of expensive call for later use
    apcu_add('expensive_data', $data = get_expensive_data());
}

print_r($data);

Ten en cuenta que antes de PHP 5.5, existía la extensión APC, que proporcionaba tanto una caché de objetos como una caché de bytecode. El nuevo APCu es un proyecto para llevar la caché de objetos de APC a PHP 5.5+, ya que PHP ahora tiene una caché de bytecode incorporada (OPcache).

Aprende más sobre los sistemas de almacenamiento en caché de objetos más populares:

Volver al inicio

Documentar tu Código

PHPDoc

PHPDoc es un estándar informal para comentar el código PHP. Hay muchas [etiquetas] diferentes disponibles. La lista completa de etiquetas y ejemplos se puede encontrar en el [manual de PHPDoc].

A continuación se muestra un ejemplo de cómo podrías documentar una clase con algunos métodos:

<?php
/**
 * @author A Name <a.name@example.com>
 * @link https://www.phpdoc.org/docs/latest/index.html
 */
class DateTimeHelper
{
    /**
     * @param mixed $anything Anything that we can convert to a \DateTime object
     *
     * @throws \InvalidArgumentException
     *
     * @return \DateTime
     */
    public function dateTimeFromAnything($anything)
    {
        $type = gettype($anything);

        switch ($type) {
            // Some code that tries to return a \DateTime object
        }

        throw new \InvalidArgumentException(
            "Failed Converting param of type '{$type}' to DateTime object"
        );
    }

    /**
     * @param mixed $date Anything that we can convert to a \DateTime object
     *
     * @return void
     */
    public function printISO8601Date($date)
    {
        echo $this->dateTimeFromAnything($date)->format('c');
    }

    /**
     * @param mixed $date Anything that we can convert to a \DateTime object
     */
    public function printRFC2822Date($date)
    {
        echo $this->dateTimeFromAnything($date)->format('r');
    }
}

La documentación de la clase en su conjunto tiene la etiqueta @author y una etiqueta @link. La etiqueta @author se usa para documentar al autor del código y puede repetirse para documentar a varios autores. La etiqueta @link se usa para enlazar a un sitio web que indique una relación entre el sitio web y el código.

Dentro de la clase, el primer método tiene una etiqueta @param que documenta el tipo, nombre y descripción del parámetro que se pasa al método. Además, tiene las etiquetas @return y @throws para documentar el tipo de retorno y cualquier excepción que pueda lanzarse, respectivamente.

Los métodos segundo y tercero son muy similares y tienen una única etiqueta @param, al igual que el primer método. La diferencia importante entre los bloques de documentación de los métodos segundo y tercero es la inclusión/exclusión de la etiqueta @return. @return void nos informa explícitamente que no hay retorno; históricamente, omitir la declaración @return void también resulta en la misma acción (sin retorno).

Volver al inicio

Recursos

Desde la Fuente

Personas a Seguir

Es difícil encontrar miembros de la comunidad PHP interesantes y bien informados cuando se está empezando. Puedes encontrar una lista abreviada de miembros de la comunidad PHP para empezar en:

Proveedores PaaS PHP

Frameworks

En lugar de reinventar la rueda, muchos desarrolladores de PHP utilizan frameworks para crear aplicaciones web. Los frameworks eliminan muchas de las preocupaciones de bajo nivel y proporcionan interfaces útiles y fáciles de usar para completar tareas comunes.

No es necesario utilizar un framework para todos los proyectos. A veces lo mejor es utilizar PHP sin más, pero si necesitas un framework, existen tres tipos principales:

Los micro-frameworks son esencialmente una envoltura para enrutar una petición HTTP a un callback, controlador, método, etc. tan rápido como sea posible, y a veces vienen con algunas bibliotecas adicionales para ayudar al desarrollo, tales como envolturas básicas de bases de datos y similares. Se utilizan principalmente para crear servicios HTTP remotos.

Muchos frameworks añaden un número considerable de funciones además de las disponibles en un microframework; son los llamados Full-Stack Frameworks. Suelen incluir ORM, paquetes de autenticación, etc.

Los frameworks basados en componentes son colecciones de bibliotecas especializadas y de propósito único. Los distintos frameworks basados en componentes pueden utilizarse juntos para crear un microframe o un framework completo.

Componentes

Como ya se ha mencionado, los “componentes” son otro enfoque del objetivo común de crear, distribuir e implementar código compartido. Existen varios repositorios de componentes, los dos principales de los cuales son:

Ambos repositorios tienen herramientas de línea de comandos asociadas para ayudar en los procesos de instalación y actualización, y se han explicado con más detalle en la sección Gestión de dependencias.

También existen frameworks basados en componentes y proveedores de componentes que no ofrecen ningún tipo de framework. Estos proyectos proporcionan otra fuente de paquetes que idealmente tienen poca o ninguna dependencia de otros paquetes, o frameworks específicos.

Por ejemplo, puede utilizar el Paquete de Validación FuelPHP, sin necesidad de utilizar el propio framework FuelPHP.

Los componentes Illuminate de Laravel se desacoplarán mejor del framework Laravel. Por ahora, sólo los componentes mejor desacoplados del framework Laravel se enumeran más arriba..

Otros Recursos Útiles

Hojas de trucos

Más buenas prácticas

Noticias sobre PHP y las comunidades de desarrollo web

Puede suscribirse a boletines semanales para mantenerse informado sobre nuevas bibliotecas, últimas noticias, eventos y anuncios generales, así como sobre los recursos adicionales que se publican de vez en cuando:

También hay semanarios en otras plataformas que podrían interesarte; aquí tienes una lista de algunos.

PHP universe

Tutoriales en Vídeo

YouTube Channels

Vídeos Pagos

Libros

Existen muchos libros sobre PHP; lamentablemente, algunos son ya bastante antiguos y han dejado de ser precisos. En particular, evite libros sobre “PHP 6”, una versión que ahora nunca existirá. La siguiente versión mayor de PHP después de la 5.6 fue “PHP 7”, en parte debido a esto.

Esta sección pretende ser un documento vivo de libros recomendados sobre desarrollo PHP en general. Si desea que su libro sea añadido, envíe un PR y será revisado para comprobar su relevancia.

Libros Gratuitos

Libros de Pago

Volver al inicio

Comunidad

The PHP community is as diverse as it is large, and its members are ready and willing to support new PHP programmers. Consider joining your local PHP user group (PUG) or attending larger PHP conferences to learn more about the best practices shown here. You can hang out on IRC in the #phpc channel on irc.freenode.com and follow the @phpc on Twitter or Mastodon. Get out there, meet new developers, learn new topics, and above all, make new friends! Other community resources include StackOverflow.

Read the Official PHP Events Calendar

Grupos de Usuarios PHP

If you live in a larger city, odds are there’s a PHP user group nearby. You can easily find your local PUG at PHP.ug. Alternate sources might be Meetup.com or a search for php user group near me using your favorite search engine (i.e. Google). If you live in a smaller town, there may not be a local PUG; if that’s the case, start one!

Special mention should be made of two global user groups: NomadPHP and PHPWomen. NomadPHP offers twice monthly online user group meetings with presentations by some of the top speakers in the PHP community. PHPWomen is a non-exclusive user group originally targeted towards the women in the PHP world. Membership is open to everyone who supports a more diverse community. PHPWomen provide a network for support, mentorship and education, and generally promote the creating of a “female friendly” and professional atmosphere.

Read about User Groups on the PHP Wiki

Conferencias sobre PHP

The PHP community also hosts larger regional and national conferences in many countries around the world. Well-known members of the PHP community usually speak at these larger events, so it’s a great opportunity to learn directly from industry leaders.

Find a PHP Conference

ElePHPants

ElePHPant is that beautiful mascot of the PHP project with an elephant in its design. It was originally designed for the PHP project in 1998 by Vincent Pontier - spiritual father of thousands of elePHPants around the world - and ten years later adorable plush elephant toys came to birth as well. Now elePHPants are present at many PHP conferences and with many PHP developers at their computers for fun and inspiration.

Interview with Vincent Pontier