Categoría: Cómo hacer para…

Estos artículos te explicarán cómo resolver problemas específicos usando PHP

  • Cómo compartir la conexión a MySQL entre scripts PHP

    Cómo compartir la conexión a MySQL entre scripts PHP

    Leo frecuentemente comentarios como:

    Al guardar la conexión a MySQL usando serialize y unserialize me da errores

    O

    Guardo la conexión a la BD en una variable de sesión pero después cuando consulto ese valor desde otra página no existe

    Nadie quiere tener que abrir una nueva conexión a la base de datos cada vez que se ejecuta una página PHP. Compartir en X

    En un contexto de alta concurrencia esto puede suponer un gran desperdicio de recursos e incluso convertirse en un cuello de botella.

    Sin embargo, la solución no es guardar la conexión dentro de la sesión… de hecho, eso no va a funcionar.

    Se trata de un caso muy particular, pero existe una alternativa.

    Qué son las conexiones permanentes a MySQL

    En el uso normal de las conexiones a MySQL (usando, por ejemplo mysqli_connect), al finalizar la ejecución del script se cierran automáticamente las conexiones que hayan sido abiertas.

    Si tenemos en cuenta el modelo de ejecución de php notaremos que habrá un gran overhead con toda esa apertura/cierre de conexiones.

    Para evitar este problema PHP dispone de conexiones permanentes.

    Se trata de conexiones que, a diferencia de las comunes, continúan existiendo luego de que el script ha finalizado su ejecución.

    Cómo se crean conexiones permanentes de PHP a MySQL

    En general, existen dos modos de vincular PHP y MySQL:

    1. MySQLi
    2. PDO

    La principal diferencia entre ellas es que MySQLi es una librería específica (es decir, sólo puede trabajar con MySQL) mientras que PDO es genérica (Puede trabajar con diferentes motores de bases de datos).

    Ya que estamos hablando de MySQL en este post voy a explorar el uso de MySQLi.

    La forma de crear una conexión persistente es simple, sólo se necesita agregar antes del nombre del host el string p: ejemplo:

    <?php
    
    $conn = mysqli_connect("p:localhost", "root", "rootpwd");

    ¡Y listo! Con este sencillo código puedes olvidarte del problema de abrir y cerrar conexiones a MySQL en cada script de tu sitio.

  • Cómo pasar datos de tipo DateTime a través de $_GET

    Cómo pasar datos de tipo DateTime a través de $_GET

    Tenés una fecha guardada en un objeto DateTime y necesitás enviarla a otra página… incluso puede que sea una página de otro sitio (un servicio web por ejemplo).

    Suponiendo que la URL del destino es algo como http://otrositio.com?fecha=dd-mm-YYYY, sólo se trata de tomar el string http://otrositio.com?fecha= y concatenar el objeto.

    No parece muy complicado, ¿no?

    No es que sea una ciencia oculta tampoco, pero no es tan simple.

    ¿Cuál es el problema?

    La clase DateTime no cuenta con un método __toString, con lo cual, si intentás algo como:

    <?php
    
    $d = new DateTime();
    $url = 'http://otrositio.com?fecha='.$d;

    Te vas a encontrar con un error:

    PHP Warning:  Uncaught Error: Object of class DateTime could not be converted to string

    El punto es que no existe una única forma de expresar una fecha como una cadena.

    Por ejemplo:

    • 22-12-1977
    • 22 de Diciembre de 1977
    • Jueves 22 Dic. 1977

    Y unas cuantas más son, en rigor, la misma fecha.

    De modo que PHP no es capaz de adivinar cuál de todos estos formatos querés usar.

    Cómo formatear una fecha usando PHP

    Claro que, siendo un problema tan común, no es extraño asumir que existe una solución estándar.

    Esta solución es precisamente el método format.

    Por ejemplo, para darle formato dd-mm-YYYY a una fecha se puede usar:

    <?php
    
    $d = new DateTime();
    $s = $d->format('d-m-Y');

    Y luego usar el resultado para generar la URL:

    <?php
    
    $d = new DateTime();
    $s = $d->format('d-m-Y');
    $url = 'http://otrositio.com?fecha='.$s;

    Cómo transformar un string recibido por $_GET en un DateTime

    Y ahora… qué pasaría si tu situación es la inversa, es decir, ¿qué deberías hacer si necesitás tomar una fecha como parámetro desde la URL?

    Pues bien, aquí tu aliado será el constructor de la clase DateTime:

    <?php
    
    $d = new DateTime($_GET['fecha']);

    O bien, para evitar problemas, el método estático createFromFormat:

    <?php
    
    $d = DateTime::createFromFormat('d-m-Y', $_GET['fecha']);

    Y una vez convertido en objeto ya puedes usar todo el resto de los métodos que seguro te harán mucho más cómodo el trabajo con datos de tipo fecha.

  • PHP me da error… ¿y ahora?

    PHP me da error… ¿y ahora?

    Cuando estás comenzando con php es muy común encontrarte ante alguna situación similar a esta:

    «por algún motivo no puedo insertar datos ni actualizar ni borrar.
    Me da un error en las lineas 51 y 52″

    «estoy queriendo insertar un string en una base de datos pero me da error, no me reconoce el formato de la hora»

    «estoy haciendo un select php con un where, pero no me muestra los resultados, me da error

    Seguramente te estarás preguntando qué puede estar pasando y más importante: ¿¿cómo resolverlo??

    Hay pocas cosas más desesperantes que probar y probar y que nada funcione…

    Te tengo buenas noticias: la solución está más cerca de lo que parece.

    Lo primero que hay que hacer en una situación como esta es respirar hondo, tal vez dar una pequeña vuelta alrededor del escritorio y volver con la cabeza más fresca.

    Una vez pasada la desesperación tenés que comprender algo fundamental:

    No todos los errores son iguales

    Con esto quiero decir que «me da error» ayuda poco cuando se trata de encontrar una solución.

    Hay que comprender la causa del problema para avanzar.

    Es como cuando vas al médico: si todo lo que el profesional sabe es que te sentís mal es difícil que te pueda curar.

    Cómo encontrar la causa del error

    Lo primero que necesitás es comprender exactamente cuál es el error.

    Usualmente, cuando un programa no completa su ejecución, en algún lado se guarda información de qué fue lo que sucedió.

    Dependiendo de la configuración es posible que esa información esté directo frente a tus ojos, a través de un mensaje de error en la pantalla:

    O que se esconda detrás de un mensaje algo genérico como:

    En este caso no significa que esta es toda la información que vas a obtener, sólo que vas a tener que buscar un poco más, probablemente en los archivos de log del servidor web.

    Los mensajes pueden parecer un poco difíciles de leer e interpretar, especialmente si no sabés inglés, pero si les prestás atención notarás que son muy informativos.

    Cerca del 80% de los errores de php se pueden comprender simplemente leyendo el mensaje. Compartir en X

    Veamos algunos ejemplos.

    Call to undefined function

    Fatal error: Uncaught Error: Call to undefined function sqlsrv_query() in /home/mauro/Code/sunat/cambio.php on line 4

    Desmenucemos un poco el mensaje.

    La primera parte dice Fatal error.

    Suena feo ¿no? Error fatal… tranquilo, no se murió nadie, simplemente significa que se ha producido un error que impide que el script siga ejecutándose.

    Luego vemos que dice Uncaught error.

    Uncaught significa «no atrapado», lo que da una idea de que los errores pueden atraparse… tema un poco avanzado para este momento, por lo pronto dejémoslo de lado.

    Y ahora sí llegamos a la parte jugosa: Call to undefined function sqlsrv_query().

    Este es realmente el problema: se está intentando llamar a una función que no ha sido definida aún (o al menos, el intérprete no la conoce).

    ¿Cuál es la función desconocida? sqlsrv_query.

    En php existen dos tipos de funciones:

    1. Propias del lenguaje (o de librerías escritas en C)
    2. Definidas por el programador (vos)

    El caso de sqlsrv_query es el primero, pero si intentaras llamar a una función tuya que el intérprete desconoce el problema sería el mismo.

    Así que aquí tenemos la primera pista.

    Luego tenemos otros dos datos muy importantes:

    in /home/mauro/Code/sunat/cambio.php

    Está indicando cuál es exactamente el archivo que se estaba ejecutando cuando el problema se presentó.

    Por último on line 4 te está indicando cuál fue exactamente la línea que disparó el error.

    Con todo esto tenés bastante de dónde agarrarte: sabés qué línea de qué archivo tenés que ir a mirar… no está mal, ¿cierto?

    Ahora vamos al caso específico.

    Si no sabés mucho sobre la función sqlsrv_query siempre tenés un aliado poderoso: el viejo y querido Google.

    Una búsqueda simple te va a dar estos resultados:

    Y ya que apareció un link a la página oficial de php.net, yo te diría que lo sigas a ver qué aparece :).

    Leyendo un poco vas a encontrar que la función sqlsrv_query pertenece a la extensión SQLSRV.

    Como el resto de las extensiones, puede ser habilitada o deshabilitada en la configuración de PHP (el famoso archivo php.ini).

    Así que una posibilidad es que la librería no esté instalada y/o habilitada.

    Sólo lo vas a saber mirando cómo está tu instalación de php.

    Undefined variable

    Notice: Undefined variable: ejecutar in C:\wamp\www\demo\php\insertar.php on line 19

    Acá lo que está diciendo el intérprete está diciendo:

    1. Que hay una variable no definida llamada ejecutar (Undefined variable: ejecutar)
    2. Que el problema aparece en la línea 19 del archivo C:\wamp\www\demo\php\insertar.php

    Momento.

    ¿No era que en PHP no hacía falta definir las variables?

    ¿En qué quedamos?

    Sin ver el código completo puedo inferir que en la línea 19 se está intentando usar esa variable antes de haberla asignado en algo como:

    <?php
    
    if ( $ejecutar == 'Otro ejemplo:algo' ) ...

    En php la definición y la primera asignación de una variable se producen en el mismo momento.

    Otro detalle interesante para notar es que el mensaje comienza con Notice (A diferencia del anterior que empezaba con Fatal Error).

    PHP tiene diferentes niveles de errores, algunos más graves que otros.

    Según el tipo específico del error se disparará (o no) alguna acción, como por ejemplo finalizar la ejecución del script ante la detección del error.

    Object of class … could not be converted to String

    Catchable fatal error: Object of class DateTime could not be converted to string

    Aquí lo que está sucediendo es que se está tratando de usar un objeto de tipo DateTime en el contexto de un string… y la clase DateTime no tiene un método __toString.

    Un código que podría dar este problema es:

    <?php
    
    echo 'Hoy es '.(new DateTime()).'!';

    Qué hacer cuando el mensaje de error no alcanza

    Claro que no siempre la vida es tan generosa… ¿Qué pasa cuando el mensaje de error no es suficiente?

    ¿Qué podés hacer?

    Bueno… sí, es una opción, pero existen algunas mejores opciones 🙂

    A veces sucede que encontrar la causa de un error requiere algo más que simplemente leer el código.

    Esto sucede, por ejemplo, cuando el programa falla a veces

    Tomemos un código como este:

    <?php
    
    $a = rand(1,3);
    
    switch ($a) {
    case 1:
            echo 'Hoy es: '.(new DateTime());
            break;
    case 2:
            echo $a;
    case 3:
            echo $a * 2;
    }
    
    echo PHP_EOL;

    Estadísticamente este programa debería fallar una de cada 3 veces que es ejecutado.

    ¿Cuándo? Cada vez que $a == 1.

    Y ¿cómo podés saber cuánto vale $a en cada ejecución?

    Necesitás alguna herramienta que te permita ver su contenido.

    Existen diferentes opciones:

    Pero la realidad es que la mejor alternativa es no usar ninguna de ellas, si no más bien un debugger.

    Qué es un debugger

    Un debugger es un programa que te permite ejecutar tu código paso-a-paso.

    Es decir, en lugar de correr el programa entero y, recién entonces, ver la salida y tratar de entender qué valor tenía cada variable al momento de la ejecución, con el debugger podés ver el contenido de tus variables en tiempo real.

    Los más populares para PHP son

    Si querés conocer más sobre cómo usar un debugger en tus aplicaciones podés seguir leyendo acá.

    Si PHP te da errores no corras

    La próxima vez que te encuentres ante un error de PHP (Y creeme, te va a pasar), no te desesperes.

    Tomá un poco de aire y hacé un pequeño esfuerzo por comprender el mensaje de error que estás viendo.

    Te sorprenderá la cantidad de veces que no necesitarás más para subsanar el problema 🙂

  • ¿cURL o file_get_contents? 6 tips para decidir

    ¿cURL o file_get_contents? 6 tips para decidir

    Estás haciedo un script para obtener contenido desde una url y te encuentras que se puede hacer con curl y con file_get_contents… ¿cuál es la más apropiada para este caso?

    Antes de decidir, hazte estas preguntas

    ¿Qué diferencias hay entre cURL y file_get_contents?

    file_get_contents es una función de bastante alto nivel.

    Puede usarse tanto para leer archivos locales como remotos.

    cURL es una librería que permite la comunicación con diferentes tipos de servidores y protocolos.

    Esto significa que, si bien con cURL es posible lograr el objetivo, esta herramienta tiene muchas más aplicaciones que la primera.

    ¿Cuál es la más segura?

    Un punto muy importante es el de la seguridad, dado que se trata de intercambiar información con servidores remotos y, ahí donde dejamos una puerta abierta… el diablo puede colarse 🙂

    En general se considera a cURL como la opción más segura.

    Si vas a usar file_get_contents para obtener contenido de archivos remotos debes validar que la URL no esté creada en forma maliciosa.

    ¿Cuál es la que menos recursos consume?

    En cuanto al consumo de recursos, ninguna de las dos presenta una ventaja significativa sobre la otra, en definitiva, lo más probable es que no sea este el culpable de que agotes las capacidades instaladas.

    ¿Cuál es la más rápida?

    En general las pruebas de velocidad dan a cURL como el ganador.

    ¿Cuál es la más simple?

    Definitivamente, la más simple de utilizar es file_get_contents, en principio porque forma parte el núcleo de PHP, a diferencia de cURL que es un módulo que debe ser activado y configurado.

    ¿Cuál funcionará en mi hosting?

    Esta es probablemente la pregunta que definirá cuál es la solución adecuada.

    Si estás en un entorno compartido es posible que no puedas usar ninguna, lamentablemente, este tipo de comunicaciones suele estar deshabilitada en estos proveedores… claro que también puedes subir de plan.

    Si utilizas tu propio servidor el límite es tuyo.

    En todo caso, para utilizar file_get_contents necesitarás tener habilitada la opción allow_url_fopen y, para utilizar cURL php deberá estar compilado con la opción --with-curl

  • Cómo validar que el usuario tiene permisos para ver la pagina web

    Cómo validar que el usuario tiene permisos para ver la pagina web

    Me encuentro a menudo con problemas como estos:

    Estoy haciendo un sistema para validar las personas que ingresan a un modulo especifico.
    El sistema solicita una clave de acceso que yo les genero, por ejemplo la página del formulario es el index.html y cuando completan e introducen la clave correcta los lleva a view.html, el problema está en que si una persona pone en la url directamente view.html, pueden ingresar sin pasar por el formulario.
    Como puede hacer para si alguien conoce la URL view.html, al ingresarla en el navegador lo redirija al index.html


    Diseñando mi sitio web con PHP, he notado algo que no es correcto. En la primera pagina, el usuario ingresas sus datos (usuario y contraseña). Luego de que es validado en la base de datos identificando que existe, se le da acceso a la pagina del menú que le permite hacer consultas a la base de datos. Al copiar la URL de la pagina donde esta el menú, note que se puede ingresar sin hacer la validación de usuario y contraseña. Entonces pensé, que pasaría si el usuaria le comparte la URL de la pagina donde esta el menú a otra persona que no tiene permisos? pues ingresará a los datos.

    Está claro que si un usuario accede a un módulo para el que no tiene permisos estamos ante un problema de seguridad… al fin y al cabo, ¡es lo mismo que no haber puesto un formulario de login! (Y en tal caso al menos nos habríamos ahorrado algo de trabajo, ¿no? 🙂 )

    La solución a este problema puede pensarse como la solución a dos sub-problemas:

    Cómo saber si un visitante pasó antes por una página de tu sitio

    Una forma de saber si un usuario ha visitado o no una página de tu sitio antes de llegar a la actual (En este caso view.html) es utilizar algún tipo de marca.

    La implementación más sencilla es utilizar el mecanismo de sesiones que provee PHP.

    Al hacerlo de esta forma te garantizas que la información acompañe al visitante a lo largo de todo su recorrido.

    El uso es muy simple, sólo tienes que escribir algo como:

    <?php
    
    session_start();
    $_SESSION['paso_por_index'] = true;

    Al comienzo del archivo que responde a la URI /index.html

    Cómo redireccionar a un visitante no autorizado

    Luego, el archivo que quieras ocultar debe comenzar con algo como:

    <?php
    
    session_start();
    
    if (!array_key_exists('paso_por_index', $_SESSION)) {
       header('Location: index.html');
       die;
    }

    De esta forma, si la marca no está colocada el script entenderá que el usuario no ha pasado por index.html al llegar a view.html y, en consecuencia, lo redireccionará a la página inicial.

  • Cómo mejorar la calidad de tus aplicaciones PHP

    Cómo mejorar la calidad de tus aplicaciones PHP

    A nadie le gusta encontrar bugs en sus aplicaciones.

    Mucho menos que sea el cliente quien los encuentre.

    ¿Qué podemos hacer para evitar estas desagradables situaciones? Testear.

    Qué significa testear una aplicación

    Testear una aplicación puede significar varias cosas pero básicamente, se trata de probar el funcionamiento tratando de confirmar que todo ocurre según lo planeado.

    La parte desagradable del testing es que sólo puede darnos seguridad de que la aplicación falla.

    En otras palabras el hecho de que todas las pruebas resulten exitosas puede significar dos cosas:

    1. Que efectivamente la aplicación esté libre de errores
    2. Que no hayamos realizado suficientes pruebas

    Desafortunadamente no es posible determinar si estamos en la situación uno o la dos.

    ¡Claro que esto no significa que testear no valga la pena!

    El punto es que nunca podremos tener un 100% de confianza en que la aplicación es correcta… aunque tener algo de confianza es mejor que no tener nada.

    Qué tipos de testing existen

    Existen diversos tipos de pruebas que pueden realizarse en aplicaciones.

    La más básica es el testing manual.

    Este tipo de prueba se trata de uno mismo usando la aplicación como espera que lo haría un usuario y evaluando los resultados a ojo.

    Claramente se trata del testing más simple que puede realizarse pero, a su vez, el menos ventajoso:

    • Es lento
    • Es tedioso
    • Es poco confiable

    Una segunda categoría de tests son los automatizados y, dentro de ellos, existen subdivisiones:

    • Los tests unitarios
    • Los tests de integración
    • Los tests de aceptación

    Qué son los tests unitarios

    Los tests unitarios son aquellos en los que se prueba una parte muy específica del código (Un método por ejemplo).

    Se sabe de antemano qué resultado debe retornar una función dado un input y se verifica que así suceda.

    Qué son los tests de integración

    Los tests de integración son similares a los unitarios, salvo que lo que se prueba no es una unidad en aislamiento si no cómo dos o más unidades interactúan entre sí.

    Qué son los tests de aceptación

    Los tests de aceptación apuntan a verificar el correcto funcionamiento del sistema como tal, sin tomar en cuenta el código que está detrás.

    La idea de estas pruebas es automatizar las que se realizarían en forma manual.

    Qué herramientas pueden usarse para testear PHP

    Existen varias herramientas que pueden utilizarse para verificar el código escrito con PHP.

    La más conocida y utilizada es phpUnit.

    Si bien esta herramienta está diseñada para la realización de pruebas unitarias, es posible utilizarla en otros tipos de pruebas también.

    Las pruebas de aceptación requieren algún mecanismo de automatización de la interface de usuario (En el caso de aplicaciones web se trata de automatizar el navegador).

    Una herramienta muy interesante para realizar pruebas de aceptación es CodeCeption para escribir las pruebas y Selenium para automatizar el uso del navegador.

    ¿Qué partes del código deben ser testeadas?

    Como te podrás imaginar, no es posible tener el 100% del código cubierto por tests… una aplicación de mediana complejidad plantea un número realmente elevado de escenarios.

    Esto quiere decir que debemos elegir qué partes deben ser verificadas y cuáles no.

    La decisión no es sencilla, pero hay que tratar de fallar en favor de las partes críticas del sistema (Aquellas que, de no funcionar correctamente nos pondrían en serios problemas).

    La buena noticia es que los tests, al ser automatizados, pueden ejecutarse tantas veces como sea necesario y, a medida que se descubren nuevos inconvenientes, nuevos tests pueden ser agregados al conjunto, haciendo que la aplicación se robustezca más y más.

    ¿Lo único que puede hacerse es testear?

    No.

    Existe otra serie de herramientas que apuntan a detectar problemas antes de ejecutar el código.

    En lenguajes compilados por ejemplo, la verificación de compatibilidad de tipos de datos se produce antes de la ejecución de los programas… no es el caso de PHP.

    Herramientas como phpStan pueden ayudar a mejorar la calidad del código mientras se está desarrollando.

    Conclusión

    El testing es una práctica sumamente importante a la hora de realizar desarrollos profesionales.

    ¿De qué forma verificas la calidad de tus aplicaciones?

  • Cómo extraer las imágenes de un PDF usando PHP

    Trabajar con pdfs no es lo más cómodo del mundo.. especialmente cuando se trata de extraer información de ellos.

    Cuando se requiere realizar una misma tarea muchas veces es sumamente útil crear scripts para automatizar estas tareas.

    En este ejemplo te mostraré cómo extraer una imagen guardada en un documento como este:

    Por supuesto que no partiremos desde 0 habiendo tantas librerías disponibles.

    Qué librería PHP permite leer archivos PDF

    Existen diversas librerías para leer archivos PDF en PHP. Para este caso, una buena es https://github.com/smalot/pdfparser.

    Utilizarla es bastante simple.

    Comenzamos por instalarla usando Composer:

    composer require smalot/pdfparser

    Con esto tendremos acceso a objetos de tipo Smalot\PdfParser\Parser con los que podremos interpretar el contenido del archivo sin problemas.

    Lo próximo entonces es abrir el archivo utilizando las capacidades de la librería:

    <?php
    
    require_once 'vendor/autoload.php';
    use Smalot\PdfParser\XObject\Image;
    
    $parser = new \Smalot\PdfParser\Parser();
    $pdf    = $parser->parseFile($argv[1]);
    

    En este caso estoy usando un script que recibirá el nombre del archivo .pdf por la línea de comandos

    Cómo identificar una imagen dentro de un PDF

    Las imágenes dentro de los archivos pdf constituyen un tipo de objeto especial llamado XObject.

    Para identificarlos tenemos un método especial del parser: getObjectsByType que podemos utilizar de esta forma:

    foreach ($pdf->getObjectsByType('XObject') as $xobject) {
        ...
    }
    

    Ahora entonces tenemos en la variable $xobject la referencia a la imagen.

    Lo que nos falta es guardar ese contenido en algún archivo, enviarlo por email o cualquier otra cosa que queramos hacer.

    Simplemente se trata de utilizar el método getContent del objeto, por ejemplo:

    $images = array_filter($pdf->getObjectsByType('XObject'), fn($o) => $o InstanceOf Image );
    foreach ( $images as $image ) {
        file_put_contents( 'image_'.$i, $image->getContent());
    }
    

    Y luego, si quisiéramos conocer el número de imágenes que había podríamos hacer un echo count($images);

  • Cómo testear una aplicación PHP que no usa objetos

    Cómo testear una aplicación PHP que no usa objetos

    PHPUnit, al igual que la mayoría de los frameworks de testing, se basa fuertemente en el supuesto de que la aplicación a verificar está desarrollada bajo el paradigma de Orientación a Objetos.

    Sin embargo, es muy común en nuestros días encontrarnos con aplicaciones tipo spaghetti… ¿es posible hacer testing automatizado sobre ellas?

    La respuesta es sí.

    Claro que las respuestas a qué testear y cómo testear son un poco diferentes.

    Qué puede testearse en una aplicación que no usa objetos

    Obviamente, no será posible verificar una clase porque… la aplicación no tiene clases.

    De modo que podemos testear:

    • La página que se presentará al usuario (Lo que podríamos asemejar a un test funcional)
    • El resultado de ejecutar alguna función en particular
    • El resultado de correr algún script

    Cómo testear el resultado de una página php

    Para este escenario nos tendremos que valer de un pequeño truco: las funciones ob_start y ob_get_clean (Además de tener instalado phpUnit, claro).

    La idea es muy simple en realidad.

    Se crea un caso de test, se abre un buffer y se incluye el archivo que queremos validar.

    A continuación se levantan los contenidos del buffer y se examinan usando assertions.

    Veamos un ejemplo simple:

    El archivo que queremos validar se llama wrong.php

    <html>
    <body>
    <p><?php echo 'Bye bye world'; ?></p>
    </body>
    </html>

    Y este sería el caso de test:

    <?php
    
    use PHPUnit\Framework\TestCase;
    
    class PageTest extends TestCase
    {
            public function testGreeting()
            {
                    ob_start();
                    require_once 'wrong.php';
                    $this->assertRegExp('/<p>Hello World!<\/p>/', ob_get_clean());
            }
    }

    Para correr el test usamos el comando vendor/bin/phpunit PageTest.php y la salida será:

    PHPUnit 9.3.7 by Sebastian Bergmann and contributors.
    
    F                                                                   1 / 1 (100%)
    
    Time: 00:00.004, Memory: 4.00 MB
    
    There was 1 failure:
    
    1) PageTest::testGreeting
    Failed asserting that '<html>\n
    <body>\n
    	<p>Bye bye world</p>\n
    </body>\n
    </html>\n
    ' matches PCRE pattern "/<p>Hello World!<\/p>/".
    
    /home/mauro/Code/testing/PageTest.php:11
    
    FAILURES!
    Tests: 1, Assertions: 1, Failures: 1.

    Cómo testear el resultado de una función php

    Este caso es bastante similar al anterior, aunque un poco más simple.

    Aquí lo que haremos será, en lugar de validar la salida completa, verificaremos qué sucedió como resultado de ejecutar la función.

    Empecemos por modificar el archivo a testear:

    <?php
    
    function duplicate(int $p) : int
    {
            return $p * 3;
    }

    Y ahora hagamos un nuevo test:

    <?php
    
    use PHPUnit\Framework\TestCase;
    
    class FunctionTest extends TestCase
    {
            public function testFunction()
            {
                    ob_start();
                    require_once 'wrong.php';
    
                    $this->assertEquals(4, duplicate(2));
            }
    }

    El resto sigue igual al caso anterior

    Cómo testear la ejecución de un script php

    Por último, podríamos requerir testear el funcionamiento de un script de línea de comandos (Un cronjob por ejemplo).

    Imaginemos un script como este:

    <?php
    
    echo 'Bye bye '.$argv[1].'!';

    Y este test:

    <?php
    
    use PHPUnit\Framework\TestCase;
    
    class ScriptTest extends TestCase
    {
            public function testGreeting()
            {
                    $this->assertEquals('Hello World!', shell_exec('php script.php World'));
            }
    }

    Nos dará este resultado:

    PHPUnit 9.3.7 by Sebastian Bergmann and contributors.
    
    F                                                                   1 / 1 (100%)
    
    Time: 00:00.026, Memory: 4.00 MB
    
    There was 1 failure:
    
    1) ScriptTest::testGreeting
    Failed asserting that two strings are equal.
    --- Expected
    +++ Actual
    @@ @@
    -'Hello World!'
    +'Bye bye World!'
    
    /home/mauro/Code/testing/ScriptTest.php:9
    
    FAILURES!
    Tests: 1, Assertions: 1, Failures: 1.

    Conclusión

    En defintiva, no es cierto que es imposible testear aplicaciones php que no se basen en POO… lo que sí es cierto es que los tests van a ser más engorrosos y menos informativos.

    Claro que eso es consecuencia de un diseño poco modular de la aplicación que estamos testeando… ¡pero esa fue precisamente la premisa del ejercicio!

    Espero te haya dado alguna idea nueva, ¡espero tus comentarios!

  • Cómo testear los emails que envía tu aplicación

    Cómo testear los emails que envía tu aplicación

    ¿Alguna vez te llegó un correo similar a este?

    Es un hecho: los mails que genera y envía tu aplicación son una parte de ella y, como tal, deben ser testeados.

    En este artículo te mostraré algunas técnicas que te ayudarán a encarar estas pruebas

    Testear emails enviando a un único destinatario

    Una técnica simple es hacer que todos los correos lleguen siempre a un mismo destinatario (a vos).

    La ventaja de esto es que prácticamente no necesitás nada extra (Nada que no tengas digamos).

    El problema es que implementar esto puede implicar ensuciar el código con condicionales aquí y allí.

    Si usas un componente para el envío (Como el Mailer de Symfony), las cosas pueden ser más simples.

    Basta con especificar el destinatario único en el archivo de configuración de esta forma:

    # config/packages/dev/mailer.yaml
    framework:
        mailer:
            envelope:
                recipients: ['youremail@example.com']

    A partir de aquí, todos los correos salientes serán recibidos por un único destinatario independientemente de quién sea el «verdadero».

    Testear emails usando un servidor SMTP local

    Otra forma de realizar estas pruebas es instalar un servidor SMTP local y configurarlo de modo de no realizar ningún envío, si no encolarlos todos y luego consultar los envíos pendientes.

    Realizar esta configuración desde cero puede resultar bastante engorroso.

    Afortunadamente existen algunas herramientas que simplifican mucho todo este proceso.

    Una que me parece particularmente interesante es MailHog.

    Es bastante simple de instalar y cuenta con una interface web para consultar los correos que se han «enviado» a través de ella:

    Otra ventaja no menor de usar MailHog es que viene pre-instalado con el paquete Homestead de Laravel (Una excelente herramienta para usar una máquina virtual para tus desarrollos)

    Pero, por si no te convencí de usar MailHog aún, acá te dejo algunas alternativas:

  • Cómo saber el controlador que maneja una ruta en Symfony

    Cómo saber el controlador que maneja una ruta en Symfony

    El framework Symfony es de lo mejorcito que tenemos en el mundillo de PHP (Personalmente es mi favorito por lejos).

    Para procesar un request se requiere un método de alguna clase (Un controlador).

    Para decidir cuál es el controlador al que se debe invocar al momento de responder al pedido de un usuario se utiliza un componente llamado Router.

    Este componente conoce el mapeo entre una URI y dicho controlador.

    Existen diversos modos de definir este mapeo (por ejemplo mediante annotations).

    Esto hace que, si una aplicación es grande, puede ser algo complejo encontrar cuál es exactamente el controlador que se esconde detrás de una URL.

    En este artículo te mostraré un pequeño truco para obtener esa información.

    Voy a asumir que estás en algún tipo de consola POSIX (Linux, BSD, Mac o algo como el WSL de Windows).

    Cómo debugear el mapa de ruteo de Symfony

    Algo que seguramente conoces si usas Symfony es el poderoso comando (¿o debería llamarle meta-comando?) console.

    A través de esta interface es posible realizar una gran cantidad de tareas (e incluso implementar las tuyas).

    En esta ocasión quiero mostrarte un uso algo poco frecuente: el comando debug:router.

    Usando este comando puedes ver un listado completo de las rutas definidas en tu proyecto, sin importar si están definidas usando YAML, XML, PHP o annotations.

    Particularmente, si necesitas conocer la ruta que corresponde con una URL puedes usar la herramienta grep y un pipe, de esta forma:

    php bin/console debug:router -e prod | grep PARTE_URL

    Por ejemplo, digaos que necesitas conocer la ruta correspondiente a /admin/export/instant/.

    El comando que puedes utilizar es:

    php bin/console debug:router -e prod | grep export

    Y obtuve este resultado:

    Mapa de rutas definidas en la aplicación

    Una vez allí es fácil obtener el nombre de la ruta que buscas: oro_importexport_export_instant

    Cómo obtener el detalle de una ruta de Symfony

    Claro que con ese dato tienes apenas la punta del iceberg.

    Para obtener el detalle debes pasar como parámetro al comando el nombre de la ruta, por ejemplo:

    php bin/console debug:router -e prod oro_importexport_export_instant

    Y obtendrás un resultado similar a:

    Detalle de una ruta de Symfony

    Cómo encontrar el archivo donde está definido un controlador Symfony

    Y ahora que sabes cuál es el controlador es fácil llegar al archivo que lo define… ¿cierto? Pues… depende.

    Como decía, en el supuesto de una aplicación de mediana o gran envergadura, es probable que estés utilizando muchos bundles, con lo cual el controlador puede estar definido en varios lugares.

    Una forma simple de llegar a este dato es usar el comando find, por ejemplo:

    find vendor -type f  -name ImportExportController.php

    Y ahora sí, combinando esta información con la obtenida en el comando anterior es posible ver el código exacto del método que responde a esta petición.

    Nota al pié: este último paso puede simplificarse mucho si utilizas algún buen IDE para PHP 😉