Categoría: Cómo hacer para…

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

  • Cómo instalar librerías de composer en un hosting compartido

    Cómo instalar librerías de composer en un hosting compartido

    Una pregunta que me han hecho en repetidas oportunidades es cómo usar composer en un ambiente de hosting compartido.

    Personalmente, siempre prefiero usar mis propios servidores tipo VPS, precisamente para evitar este tipo de problemas, pero… si no queda otra, veamos qué se puede hacer.

    Cómo instalar dependencias si tenemos acceso ssh

    Existen algunos hostings compartidos que permiten algún tipo de acceso vía ssh (o similar).

    Si este es el caso, puede que hasta tengamos composer ya instalado… claro que la versión que podremos encontrar puede ser algo añeja y, como el servidor lo administra otro, tendremos que poner un ticket de soporte y rogar para que se actualice.

    Si tenemos suerte, podremos ejecutar composer y actualizar nuestras dependencias directamente en el servidor como si estuviéramos en un ambiente controlado por nosotros mismos… si esto no funciona podemos intentar la sigueinte opción.

    Cómo instalar dependencias en un hosting compartido si no tenemos acceso ssh

    Este es ciertamente el caso más común: no hay posibilidad de hacer ssh al servidor.

    Claro que, de alguna forma, nuestro código debe llegar al hosting… para eso lo contratamos a fin de cuentas, ¿cierto?

    Pues en este escenario lo que podemos hacer es preparar lo que vamos a subir al servidor en un directorio especial y luego subirlo.

    La idea de hacer esto en lugar de subir directamente lo que tenemos en nuestro directorio de trabajo es:

    1. Minimizar la transferencia (para disminuir el tiempo que insumirá la subida)
    2. Minimizar la cantidad de espacio que ocuparemos en nuestro espacio de hosting
    3. Evitar subir información sensible que sólo debería estar disponible en nuestro entorno de desarrolllo

    Cómo preparar la subida

    Idealmente nuestro código estará versionado (Usando git, svn o similar).

    Si este es el caso, lo que podemos hacer es generar un nuevo directorio local mediante un clone o checkout (depende de cuál sea el controlador de versiones que usemos).

    Una vez que tengamos el código descargado será tiempo de instalar las dependencias.

    Lo ideal sería subir siempre una copia limpia de nuestro sistema. Si esto no es posible, al menos deberíamos eliminar los contenidos del directorio vendor de nuestra aplicación para no acumular dependencias que ya no sean necesarias.

    Cómo actualizar las dependencias usando composer

    Independientemente de cuál sea el lugar donde ejecutaremos composer, el comando que debemos ejecutar es:

    composer install --no-dev -o

    Con este comando sólo se instalar las dependencias necesarias en producción y se optimizará el autoloading (dando mejor performance a nuestra aplicación).

  • Qué se necesita para desarrollar usando Symfony en Windows

    Qué se necesita para desarrollar usando Symfony en Windows

    Personalmente, es algo que preferiría evitar pero… si no queda opción, hay que hacerlo funcionar 🙂

    Algunos problemas que vamos a tener que resolver para tener un entorno de desarrollo medianamente cómodo son:

    1. Contar con PHP
    2. Contar con composer
    3. Contar con git
    4. Contar con algún IDE

    Todas estas cosas en Linux son prácticamente triviales (Especialmente si usás Ubuntu o algún derivado) pero en Windows son un poquito más difíciles de conseguir.

    Alternativamente podés usar un entorno virtualizado, pero si la única opción es un entorno local en Windows, acá van las instrucciones:

    Instalando PHP en Windows

    Instalar PHP en Windows no es una tarea extremadamente sencilla… para empezar debes descargar el ejecutable adecuado para tu S.O. (Según sea 32 o 64 bits). Eso lo podés hacer desde acá.

    El detalle es que, para que php funcione necesitas tener el redistribuible de Visual C++ correspondiente (!).

    Lo podés descargar de acá (Tené cuidado de descargar la versión correcta para tu Windows).

    Una vez esté todo instalado, para asegurarte de que funciona abrí una terminal en C:\php (O donde hayas descargado y descomprimido el php) y ejecutá el comando php -v.

    Deberías ver algo como:

    Pantalla de línea de comandos de Windows mostrando PHP funcionando

    Una opción alternativa (y que puede resultar más simple) es usar alguno que ya tenga todo incorporado (PHP, MySQL, etc…) como XAMPP o Laragon.

    A los efectos de este artículo asumiré que usamos el camino de sólo instalar PHP (Que, en definitiva es lo único que necesitamos por el momento).

    Instalando composer en Windows

    Conseguir en composer es bastante simple. Te lo podés bajar de acá y se instala como cualquier otro programa de Windows.

    Lo único importante aquí es ingresar la ruta del php.exe que hayas instalado en el paso anterior (Si hay uno solo el mismo instalador lo reconocerá):

    Selección del ejecutable de PHP

    Para ver que todo esté en su lugar, abrí una consola de Windows (Comando cmd) y tipeá composer. Deberías ver algo como:

    Composer desde la terminal de Windows

    Esto sucede porque el instalador de Composer ha modificado la variable de entorno PATH, de modo de que, tanto el comando composer como el propio php sean accesibles desde cualquier ubicación.

    Instalando git en Windows

    Algo bastante similar es la instalación de git para Windows. Lo bajás de acá y, al instalarlo no olvides instalar git-bash (Una consola posix para usar desde tu Windows casi como si estuvieras en Linux).

    No estoy 100% seguro de esto, pero me juego a que está basado en cyg-win, así que si conocés cyg-win no hay mucha novedad en git-bash.

    Habilitando extensiones de php necesarias para Symfony

    Symfony requiere para funcionar que la extensión php-curl esté disponible.

    Con la instalación que bajaste seguramente esto se cumple, sólo que puede estar deshabilitada en el archivo php.ini.

    Lo que tenés que hace es abrir el archivo c:\php\php.ini con algún editor de texto y sacar los «;» del comienzo de las líneas.

    Contenido original del archivo php.ini

    Debe quedar así:

    Creando tu primer proyecto Symfony en Windows

    Con todo esto en su lugar podés crear tu primer proyecto Symfony:

    1. Abrí una consola git-bash
    2. Creá tu proyecto usando composer
      1. composer create-project symfony/website-skeleton my-project
      2. Esperá a que termine la descarga
    3. Probálo!
      1. cd my-project
      2. php bin/console server:run
      3. Abrí un navegador en http://localhost:8000
      4. Si ves una pantalla como esta

    ¡Está todo listo!

    Ahora sólo te queda empezar a codear 🙂

    Adelante!

    P.D.: Si preferís el video podés verlo acá

  • Cómo trabajar con grandes archivos JSON usando PHP

    Cómo trabajar con grandes archivos JSON usando PHP

    Recorrer un archivo json es algo sencillo, ¿cierto? Pues parece que cuando el tamaño del archivo es grande las cosas ya no son tan simples…

    Lo que se haría típicamente sería algo similar a:

    <?php
    
    $json = json_decode( file_get_contents( $argv[1] ), true );
    
    print_r( $json );

    El problema aquí es que se requiere primero leer todo el archivo para, recién entonces empezar a procesar…

    Tomemos un ejemplo como el árbol de categorías de MercadoLibre Brasil. Este archivo pesa 229 MB (Una vez descomprimido).

    229 MB de puro json… es bastante, sí, pero… ¿es mucho?

    La respuesta en definitiva depende la configuración que tengas en tu php.ini.

    A efectos de este análisis hice un cambio al mío de modo que quede así:

    ; Maximum amount of memory a script may consume (128MB)
    ; http://php.net/memory-limit
    memory_limit = 128

    128 MB no es algo inusual para una instalación de PHP (Al día de hoy, es el valor por defecto), con lo cual, estaremos en un problema al tratar de leer un archivo de más de 200…

    Aumentar la memoria disponible

    Una solución posible es darle más holgura a nuestro intérprete (En este caso, dejarle ocupar más memoria).

    Podemos hacerlo cambiando la configuración a algo como:

    ; Maximum amount of memory a script may consume (128MB)
    ; http://php.net/memory-limit
    memory_limit = 256

    Pero eso es, como decimos en mi país, «Pan para hoy, hambre para mañana».

    Si elegimos esta ruta (y queremos asegurarnos de estar bien cubiertos), lo ideal sería hacer esto:

    ; Maximum amount of memory a script may consume (128MB)
    ; http://php.net/memory-limit
    memory_limit = -1

    Y ahí el limitante que tendremos será la memoria física de la computadora… algo drástico para mi gusto y, más aún, poco practicable.

    El problema es que no siempre tenemos acceso a la configuración de php (como por ejemplo, en un hosting compartido).

    Aún si lo tuviéramos, tengo dudas sobre la posibilidad de alterar un setting tan sensible como este, ya que al aprovecharnos de los recursos de una forma tan poco medida estaríamos muy probablemente afectando a nuestros vecinos.

    Mejorar el modo de procesar JSON

    Otra forma de resolver el problema es cambiar la librería que usamos para procesar JSON.

    Existen varias alternativas pero en este artículo quiero mostrarte JsonReader (Una extensión de PHP).

    Una vez instalada, es bastante fácil de usar. Veamos un ejemplo de cómo obtener todos los nombres de los artículos del archivo que te mostraba antes:

    <?php
    
    require 'vendor/autoload.php';
    
    use pcrov\JsonReader\JsonReader;
    
    $reader = new JsonReader();
    $reader->open( $argv[1] );
    
    while ($reader->read("name")) {
        echo $reader->value(), "\n";
    }
    $reader->close();

    Al usar el comando

    php process.php categoriesMLB 

    Obtenemos una salida como:

    Acessórios de Carros
    Acessórios para Veículos
    Acessórios de Carros
    Exterior
    Interior
    Comércio
    Agro, Indústria e Comércio
    Comércio
    Contêineres Marítimos
    Insumos
    Máquinas
    Móveis
    Outros
    Acessórios para Veículos
    Acessórios para Veículos
    Acessórios de Carros
    Acessórios de Motos
    Acessórios Náutica
    Ferramentas
    GPS
    Limpeza Automotiva

    Que continúa por unas 1218885 líneas

    Reducir el problema

    Por último, no podemos dejar de lado la solución de «pensamiento lateral» (Que suele ser la mejor :)): re-definir el problema en términos más simples.

    Se trata de hacernos la pregunta: ¿Realmente necesitamos ese archivo? ¿No podríamos trabajar con uno más pequeño?

    Claramente, la respuesta dependerá del contexto más que de cuestiones de tecnología, pero no estaría completo el artículo si no lo mencionara.

    (Si preferís resolver el problema intentando reducir el tamaño del archivo, este artículo puede ser de tu interés).

    Algunas notas finales

    Es importante distinguir entre la necesidad de procesar un archivo tan grande en forma online (Es decir, mientras estamos generando una respuesta para un visitante al sitio) y offline (En un cronjob en la mitad de la noche por ejemplo).

    Claramente, esta herramienta va bien para el segundo caso (Para el primero te diría que si estás con esa necesidad tendrías que re-pensar algo…).

    Por último, no puedo dejar de agradecer a mis amigos y colegas Alejandro Chapiro y Eric Danan por la inspiración para escribir este texto (Y la discusión sobre soluciones que te mostré acá).

  • Cómo prevenir la subida duplicada

    Cómo prevenir la subida duplicada

    Ultimamente vengo trabajando bastante con procesamiento de planillas Excel usando PHP.

    Por lo general, el workflow del usuario es algo así como:

    • Trabajar con algún otro sistema (HomeBanking, Plataforma de trading, etc…)
    • Descargar información en formato Excel
    • Importar planilla descargada al sistema que yo desarrollé
    • Trabajar la información dentro del sistema

    Uno de los errores comunes cuando una persona carga información a un sistema es el de la carga duplicada.

    Este problema se agrava cuando los duplicados no siempre son errores .

    Esta condición hace que no sea simple detectar y prevenir la importación duplicada.

    El escenario sería algo como tomar el workflow original y modificarlo ligeramente:

    • Trabajar con algún otro sistema (HomeBanking, Plataforma de trading, etc…)
    • Descargar información en formato Excel
    • Importar planilla descargada al sistema que yo desarrollé
    • Salir por un café
    • Olvidar qué fue lo último que se hizo
    • Volver a importar planilla descargada al sistema que yo desarrollé
    • Trabajar la información dentro del sistema

    Siendo que el sistema no puede automáticamente eliminar un registro sólo porque su contenido ya exista parece poco lo que puede hacerse…

    Mi idea de solución se basa en intentar detectar archivos que ya han sido subidos al sistema de un modo eficiente.

    Disclaimer: la solución que voy a descibir aquí es, hasta el momento, también teórica (más allá de alguna prueba de concepto).

    Cómo detectar archivos ya subidos

    Una primera idea es:

    1. Almacenar todos los archivos subidos
    2. Realizar una comparación binaria de los contenidos del nuevo archivo subido contra todos los anteriores

    No me suena muy eficiente que digamos :p

    Una idea algo más elaborada es la de usar un hash como base de comparación.

    La idea es por cada archivo subido calcularlo y comparar contra los ya existentes.

    Lo interesante de este método es que el cálculo es rápido y la comparación es casi trivial.

    Un punto a tener en cuenta sin embargo es que, aunque muy poco probable, existe el riesgo de «falsos positivos».

    Dos strings diferentes pueden tener el mismo hash, con lo cual el sistema podría dar por duplicado dos archivos que no tengan nada que ver.

    Para evitar este problema dejaré la solución 1 como fallback para los casos en que encuentre hashes duplicados.

    Implementación en PHP de detección de subidas duplicados

    No te voy a dejar sólo con palabras, ¿cuál sería la gracia, cierto?

    Vamos a ver algo de código:

    Voy a almacenar todo el contenido de los archivos en una base de datos.

    A efectos de esta prueba me basta con SQLite, pero podés usar la que te resulte más cómoda (o guardar los archivos en algún directorio si preferís).

    Entonces, lo primero es crear la base usando este SQL:

    CREATE TABLE "files" (
    	`id`	INTEGER PRIMARY KEY AUTOINCREMENT,
    	`contents`	BLOB,
    	`hash`	TEXT
    );
    CREATE TABLE sqlite_sequence(name,seq);

    Luego tendremos dos archivos, el que permite subir el archivo (HTML) y el que recibe el archivo (PHP):

    upload.html

    <form action="get_file.php" method="post" enctype="multipart/form-data">
        <p>Importar: <input type="file" name="fileToUpload"/></p>
        <p><input type="submit" value="Subir"/></p>
    </form>

    get_file.php

    <?php
    /**
    * Created by PhpStorm.
    * User: mauro
    * Date: 1/9/19
    * Time: 12:46 PM
    */

    $tmp_name = $_FILES["fileToUpload"]['tmp_name'];
    $hash = sha1_file($tmp_name);

    echo 'Hash for uploaded file: "' . $hash . '"<br/>';

    $new_file_contents = file_get_contents($tmp_name);

    $db = new PDO('sqlite:uploded.sq3');

    if ( $suspects = find_file_by_hash($hash) ) {
    foreach ( $suspects as $suspect ) {
    if ( $suspect == $new_file_contents) {
    die ('Duplicate file');
    }
    }
    }

    if ( save_file_record( $new_file_contents, $hash ) ) {
    echo 'File saved ok!';
    } else {
    echo 'Saving error :(';
    }

    /**
    * @param $hash
    * @return array
    */
    function find_file_by_hash( string $hash ) : array
    {
    GLOBAL $db;

    $sql = "SELECT * FROM files WHERE hash = '$hash'";

    if ( $st = $db->query( $sql ) ) {

    return array_map( function( $r ) {

    return $r['contents'];
    }, $st->fetchAll( PDO::FETCH_ASSOC ) );
    } else {

    return [];
    }
    }

    /**
    * @param string $contents
    * @param string $hash
    * @return int
    */
    function save_file_record( string $contents, string $hash ) : bool
    {
    GLOBAL $db;

    $sql = "INSERT INTO files ( contents, hash ) VALUES ( :contents, :hash )";

    $st = $db->prepare( $sql );
    $st->bindParam('contents', $contents, PDO::PARAM_LOB );
    $st->bindParam('hash', $hash );

    if ( !$st->execute() ) {
    print_r( $st->errorInfo() );

    return false;
    }

    return true;
    }

    Para hacer correr este código sólo hay que grabar los archivos en un mismo directorio e iniciar el servidor web que viene con PHP:

    php -S localhost:8080

    Y subir un archivo varias veces. La primera deberías ver el mensaje «File saved ok!» y la segunda «Duplicate file».

    Listo! No te recomiendo implementar el código así como está, pero espero que haya servido para explicar la idea :).

    Si preferís una versión en video, acá hay un link a YouTube.

  • Cómo filtrar colecciones usando Doctrine ORM

    Estaba pensando cómo escribir este artículo en forma clara y didáctica y, después de darle algunas vueltas me pareció que lo más fácil era armar un video:

    Para más información sobre Doctrine podés consultar acá

  • Cómo llevar los cambios de una base de datos de desarrollo a producción

    Cómo llevar los cambios de una base de datos de desarrollo a producción

    Cuando tenemos una aplicación en producción (Es decir, siendo utilizada por usuarios reales) es muy común que nos encontremos con necesidades que no han sido cubiertas por el desarrollo original.

    Esto puede deberse a diversos factores como la falta de análisis, una pobre comprensión de la problemática a encarar o simplemente al hecho de que la realidad va cambiando conforme pasa el tiempo.

    Independientemente de cuál de éstas haya sido, cuando se detecta una necesidad que el software no cubre se requiere realizarle modificaciones para adaptarlo al nuevo escenario.

    Algunas veces los cambios en el código vienen de la mano de cambios en la estructura de la base de datos utilizada para dar soporte a la aplicación.

    Y ahí comienzan los problemas 🙂

    En un ambiente de trabajo profesional es muy común contar con diferentes entornos de ejecución.

    Al menos deberíamos tener dos:

    • Desarrollo (Donde trabajamos y los datos no son precisamente valiosos)
    • Producción (Donde trabajan los usuarios y los datos son sagrados).

    Parte de nuestro trabajo será realizar las modificaciones al código de la aplicación, probarlo y eventualmente desplegarlo en el servidor de producción.

    Para llevar el control de los cambios realizados sobre el código existen muchos sistemas de control de versiones, como por ejemplo Git o Subversion.

    Utilizar uno de estos sistemas simplifica mucho conseguir la perfecta sincronización entre el código que ejecutamos en nuestro entorno de desarollo y el de producción, pero… ¿cómo hacemos para que la estructura de la base de datos también esté sincronizada?

    Actualizar la base de datos manualmente

    El enfoque más simple consiste en aplicar los cambios a la base en forma manual.

    Esto significa entrar al servidor de bases de datos (Usando phpMyAdmin por ejemplo) y ejecutar los comandos que dejen la base en el estado deseado.

    Para usar esta estrategia no se necesita mucho más que buena memoria o buenas notas.

    Claro que la desventaja de este mecanismo es que es sumamente riesgoso y propenso a errores.

    Utilizar scripts SQL

    Otra opción más interesante es generar los scripts SQL que se utilizan durante el desarrollo y almacenarlos como parte del código, de modo de aprovechar el versionado de la propia aplicación.

    Este enfoque es mejor que el anterior ya que disminuye el riesgo de ejecutar comandos diferentes en cada ambiente y, de ese modo, minimiza las chances de terminar con estructuras de bases de datos distintas.

    Uno de los problemas que aún no resuelve este enfoque es cómo determinar de un modo simple cuáles cambios ya han sido aplicados y cuáles no.

    Versionar la base de datos

    Un enfoque alternativo es el uso de alguna herramienta para el versionado de la base de datos.

    En este esquema los cambios en el ambiente de desarrollo no se realizan escribiendo SQL, si no mediante código.

    Liquibase

    Liquibase es una herramienta basada en conjuntos de cambios (changeSets) descriptos usando diferentes lenguajes posibles (SQL, XML, Json o YAML).

    Una vez definidos los cambios que deben realizarse se utiliza el ejecutor de los cambios que es el encargado de verificar el estado actual, traducir los nuevos cambios a SQL e impactarlos.

    Migraciones de Doctrine

    Otra herramienta similar (y mi favorita :)) es el mecanismo de migraciones de Doctrine.

    Las migraciones son una parte de este gran ORM que describen los cambios que deben realizarse a la base de datos.

    Una gran ventaja de este mecanismo sobre Liquibase o similares es que todo el código se escribe utilizando puro PHP y al momento de ejecutar la migración queda a cargo de Doctrine realizar la traducción a SQL.

    Notas finales

    Como siempre, cuanto más automatizada sea nuestra operación más confiable será ya que podremos realizar tantos ensayos como necesitemos hasta contar con un mecanismo correcto.

    La contracara de utilizar una herramienta como esta es que es un camino de una sola vía: si no mantenemos la disciplina de escribir todos nuestros cambios del mismo modo nos vamos a encontrar con más problemas de soluciones… aunque, en la realidad, una vez que domines una de estas técnicas dificulto que quieras volver atrás 🙂

  • Cómo autenticar usuarios en WebServices SOAP usando PHP

    Cómo autenticar usuarios en WebServices SOAP usando PHP

    Muchas organizaciones (Especialmente gubernamentales) optan por exponer sus servicios web mediante el protocolo SOAP.

    Para hacer uso de dichos servicios es necesario consumirlos.

    Existen algunas ocasiones en las que no basta con conocer la URL del servicio, también es necesario realizar algún tipo de autenticación para obtener el resultado buscado.

    Autenticación HTTP en WebServices SOAP

    El método más simple de autenticación es el propio de HTTP.

    Si este es el caso, basta con generar una URL del estilo http://usuario@password:dominio/web_service para poder acceder.

    El principal problema de este método es su poca seguridad, ya que las credenciales viajan en cada pedido, por lo tanto, es bastante poco frecuente su uso en servicios web.

    En aplicaciones web normales podrías llegar a encontrártelo (o incluso podrías querer implementarlo).

    API-Key en WebServices SOAP

    Una segunda forma de realizar autenticación es aquella basada en las API-Keys.

    En este escenario, el proveedor del servicio debe decirte cuál es tu clave, la cual deberás enviar mediante algún encabezado como parte de tu petición.

    Para ello, asumiendo que utilices la clase SoapClient, deberás crear un nuevo contexto en el que basar tus peticiones, algo como:

    $soapclient = new SoapClient($wsdl, [ 
        'stream_context' => stream_context_create([ 
         'http'=> [ 
          'header' => "X-Api-Key: 1234abcd"    
         ] 
        ]) 
    ]); 

    Este esquema es algo más seguro, siempre y cuando se realice la comunicación a través de SSL.

    Autenticación via encabezados SOAP

    Otro esquema que suele utilizarse es el de la autenticación mediante encabezados SOAP.

    Este método permite que las credenciales viajen como parte del mensaje enviado lo cual puede ser deseable para evitar depender del protocolo subyacente (HTTP en la mayoría de los casos).

    Para lograrlo debes usar el método __setSoapHeaders y la clase SoapHeader.

    Ejemplo:

    $header = new SoapHeader(
                       $namespace,
                       'UserCredentials'
                       [
                             $UserID,
                             $Pwd
                       ]
    );
    
    $client->__setSoapHeaders($header);

    Claro que tanto el namespace como el nombre y estructura exacta del encabezado deberás validarlo contra el archivo WSDL del servicio al que te quieras conectar, pero la adaptación es simple.

    Autenticación vía WSSE

    Por último hay que mencionar un protocolo especial de seguridad para servicios web: WSSE (o WS-Security).

    Este protocolo es bastante complejo ya que incluye, entre otros, firmas digitales.

    Desafortunadamente, a la fecha no existe una implementación nativa de PHP para este tipo de autenticación, con lo cual no queda mucha opción que crear la nuestra o usar alguna desarrollada por un tercero.

    El punto clave aquí es hacer algunos toques a los encabezados que enviaremos.

    Para ello una buena opción es extender la clase SoapHeader de esta forma:

    class WsseAuthHeader extends SoapHeader 
    {
        private $wss_ns = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
    
        function __construct($user, $pass, $ns = null) 
        {
            if ($ns) {
                $this->wss_ns = $ns;
            }
    
           $auth = new stdClass();
           $auth->Username = new SoapVar($user, XSD_STRING, NULL, $this->wss_ns, NULL, $this->wss_ns); 
           $auth->Password = new SoapVar($pass, XSD_STRING, NULL, $this->wss_ns, NULL, $this->wss_ns);
    
           $username_token = new stdClass();
           $username_token->UsernameToken = new SoapVar($auth, SOAP_ENC_OBJECT, NULL, $this->wss_ns, 'UsernameToken', $this->wss_ns); 
    
           $security_sv = new SoapVar(
               new SoapVar($username_token, SOAP_ENC_OBJECT, NULL, $this->wss_ns, 'UsernameToken', $this->wss_ns),
               SOAP_ENC_OBJECT, NULL, $this->wss_ns, 'Security', $this->wss_ns);
           parent::__construct($this->wss_ns, 'Security', $security_sv, true);
        }
    }

    Y luego simplemente se trata de agregar este nuevo encabezado a nuestro cliente:

    $client->__setSoapHeaders(new WsseAuthHeader( $UserID, $PWD));

    Y a partir de aquí ya es posible consumir los servicios con normalidad.

  • Cómo hacer un CRUD con Symfony e EasyAdmin

    Cómo hacer un CRUD con Symfony e EasyAdmin

    Me proponía escribir un artículo sobre lo bueno que es EasyAdmin, pero se me ocurró que una imagen vale más que 1000 palabras… Y un video más aún :). Así que armé este:

    Si lo disfrutaste y te quedaste con ganas de aprender más sobre Symfony el curso Introducción a Symfony Framework te puede ayudar.

  • Cómo generar facturas electrónicas usando PHP

    Cómo generar facturas electrónicas usando PHP

    En los países de Latinoamérica se está produciendo una migración hacia la facturación electrónica (dejando de lado las viejas facturas de papel).

    Más allá de aspectos políticos, es una muy buena noticia por, al menos, los siguientes motivos:

    1. Las operaciones comerciales se realizarán mucho más eficientemente
    2. Se limitará muchísimo la economía informal y todos los problemas que de ella se derivan
    3. Dará mucho trabajo a muchos programadores 🙂

    Sobre este último punto, veo que a muchos colegas se les complica un poco el tema de integrar sus sistemas con aquellos de la autoridad central de su país, así que decidí hacer este pequeño compendio de las librerías que hay disponibles como para acortar un poco el camino.

    Cómo generar facturas electrónicas para Argentina

    En el caso de Argentina, la autoridad central es la AFIP.

    La generación de facturas electrónicas se realiza mediante un WebService de tipo SOAP (La documentación oficial se puede consultar acá).

    Una clase que ya está implementada para hacer esto es la que desarrolló Ivan Muñoz.

    El código completo se encuentra en GitHub y mirando un poco, también hay un SDK para PHP que contiene esta librería y otras.

    Disclaimer: no probé su funcionamiento con lo cual no puedo dar garantías (¡Si tenés alguna experiencia y querés compartirla en los comments te lo agradezco!).

    Algunas observaciones de mirar el código nomás:

    • Está preparada para operar con composer
    • Podría tener mejores ejemplos
    • El código podría emprolijarse un poco (Usa notación de PHP 4, accede al php.ini desde la clase, etc…)

    La gente de BitIngenieria ha desarrollado también librerías para conectarse a diferentes organismos públicos (Entre ellos AFIP). Ahí podés encontrar mucho código listo para usar… sólo que es pago.

    Cómo generar facturas electrónicas para México

    En el caso de México, se puede usar el código publicado en http://www.lacorona.com.mx/fortiz/sat/codigo.php#intro por Fernando Ortiz.

    Es interesante cómo está presentado, no sólo el código fuente si no también el esquema de requests que deben hacerse para lograr el objetivo.

    Mirando el código simplemente veo:

    • Está escrito en modo procedural (En lugar de Orientado a Objetos)
    • El código podría estar mejor estructurado (Usa variables globales, incluye archivos dentro de funciones, etc…)
    • Usa SimpleXMLElement para procesar el XML
    • Tiene muy buena documentación
    • Es muy completo (Cubre una gran cantidad de casos)

    Tal vez no sería el que yo elegiría para implementar en forma directa, pero ciertamente es un excelente punto de partida.

    Cómo generar facturas electrónicas para Perú

    Aquí hay un tutorial interesante que explica paso a paso cómo hacer facturas electrónicas para Perú utilizando el webservice del SUNAT.

    No es exactamente lo que buscaba (Un SDK escrito en PHP), pero es un buen comienzo.

    La gente de NubeFact tiene una oferta para desarrolladores PHP (O para cualquiera bah) basada en un WebService REST propio (Que internamente se comunica con el WebService SOAP de SUNAT).

    Queda simplemente consumir el WebService REST de ellos (Y pagar por el servicio, claro… ojo que hasta 50 facturas diarias es gratis… para pensarlo).

    Cómo generar facturas electrónicas para Costa Rica

    Para el caso de Costa Rica tenemos esta API desarrollada por la comunidad CRLibre que permite comunicarse de forma simple con el Ministerio de Hacienda de Costa Rica.

    Su objetivo es ser un middleware que haga menos engorroso el desarrollo de este tipo de sistemas (Mostrando al desarrollador una cara más amigable).

    Por lo que parece no tiene aún un cliente de PHP (¡Vaya ironía!) pero parece un proyecto muy interesante y bien armado.

    Mirando un poco el código veo que también podría emprolijarse un poco (Hacerlo más acorde al paradigma de objetos, sacando referencias a variabels globales, etc…).

    Otro proyecto interesante que está disponible es Faktur-PHP-SDK desarrollado por una persona (o personas) llamada OpenCode506.

    No lo he probado funcionando, pero ya de ver el código me gusta bastante más que los otros que he comentado.

    Los puntos interesantes que le veo:

    1. Pide PHP 5.6 o superior (Ya implementa Namespaces por ejemplo)
    2. Viene con tests unitarios incorporados
    3. Está preparado para usarse vía composer

    Lo que podría ser un poco mejor es la documentación.

    Cómo generar facturas electrónicas para Uruguay

    Para el caso de Uruguay no encontré una implementación libre de facturas electrónicas… lo que vi es la documentación oficial y a través de una sesión de consulta conocí de la existencia de la empresa Sisnet que ofrece un middleware, sólo que hay que pagar por el servicio (y luego usar su webservice).

    Cómo generar facturas electrónicas para Chile

    Si tenés que generar facturas electrónicas para Chile podés usar la librería LibreDTE que desarrolló la gente de Sasco.

    A simple vista el proyecto parece sólido.

    Cumple con las caraterísticas básicas que tanto me gustan:

    1. Se puede instalar usando Composer
    2. Está muy bien documentada
    3. El código es moderno (Usa namespaces al menos)
    4. Tiene sus tests unitarios incorporados

    Cómo generar facturas electrónicas para España

    Por último, aunque estaba buscando sólo para LatinoAmérica, me crucé con una implementación de FacturaE para PHP desarrollada por José Miguel Moreno (Acá se puede ver el código).

    En resumen

    Habrás notado que, si bien cada país implementa su propia forma de Factura electrónica y por lo tanto se requiere una librería en particular, todas se basan en el mismo principio:

    1. Una entidad gubernamental expone un servicio web (Usualmente SOAP)
    2. La librería consume dicho servicio mostrando una API mucho más sencilla y amigable a los desarrolladores de a pié

    Con lo cual: si te encuentras en la situación de tener a mano una librería desarrollada para la facturación electrónica de tu país ¡úsala! En caso contrario (O que no te satisfaga completamente) para implementarla necesitará conocer cómo interactuar con WebServices en PHP.

    Si buscas ayuda sobre esto este curso puede serte útil.

  • Cómo recorrer un archivo XML usando PHP

    Cómo recorrer un archivo XML usando PHP

    Como de costumbre, comencemos por ponernos de acuerdo en las definiciones.

    Qué es XML

    Las siglas XML remiten a eXtensible Markup Language (Lenguaje de etiquetas extendible).

    Se trata de texto estructurado mediante etiquetas (Palabras encerradas entre < y >):

    <utensilios>
      <tenedor/>
      <cuchillo/>
    </utensilios>
    

    Para qué sirve XML

    XML se inventó como un medio de intercambio de información entre sistemas a través de Internet. Al ser un formato basado en texto, era fácil aprovechar la infraestructura existente para comunicarse a través de HTTP.

    Hoy en día, XML es utilizado en muchas implementaciones de WebServices (Por ejemplo, las facturas electrónicas) y también para almacenar configuraciones (En el caso del lenguaje Java es muy común encontrar este tipo de archivos, en PHP no tanto).

    Cómo se procesa XML usando PHP

    Al tratarse de texto, un modo de procesar XML es, como cualquier otro texto… pueden usarse expresiones regulares u otro medio de análisis de texto para interpretar y/o generar las etiquetas.

    Claro que no es muy divertido que digamos…


    Para comprender completamente lo que viene a continuación se requieren conocimientos de Programación Orientada a Objetos con PHP. Si aún no lo tenés muy claro este curso te puede ayudar.


    Otro modo bastante más práctico es usar la biblioteca SimpleXML que viene con PHP.

    La clase principal de la biblioteca es SimpleXMLElement. Con esta clase se puede crear una estructura en memoria a partir de un texto XML y luego recorrerlo en forma sencilla.

    Ejemplo de lectura de XML con SimpleXMLElement

    El constructor de SimpleXMLElement recibe un texto XML:

    <?php
    $xml = new SimpleXMLElement( '<utensilios><tenedor/><cuchillo/></utensilios>' );

    Y a partir de ahí pueden realizarse diversas operaciones para recorrer los elementos.

    Una de ellas es buscar elementos explícitamente a través de su XPATH:

    $elementos = $xml->xpath('/utensilios/tenedor');

    Esta llamada dejará en el array $elementos todos los nodos tenedor que se encuentren bajo la clave utensilios (En este caso es sólo uno, pero eso SimpleXMLElement no lo sabe a priori).

    Si agregamos un print_r( $elementos ) veremos :

    Array
    (
        [0] => SimpleXMLElement Object
            (
            )
    
    )

    Es decir, un array con un único elemento… de tipo SimpleXMLElement, al que podemos nuevamente aplicarle todas las funciones que provee la clase.

    Ejemplo de recorrida de XML con SimpleXML

    Otra forma de recorrer el texto XML es a través del método children:

    <?php
    $xml = new SimpleXMLElement( '<utensilios><tenedor/><cuchillo/></utensilios>' );
    foreach ( $xml->children() as $child ) {
            print_r( $child );
    }

    Esta es la que deberías usar si no conoces exactamente la estructura del XML o si efectivamente tenés que recorrer el archivo completo.

    Cómo leer un archivo XML usando PHP

    Bueno, con lo que viste hasta ahora el paso que queda es realmente corto… se podría leer el contenido del archivo a una variable y pasarla al constructor de SimpleXMLElement:

    <?php
    $texto = file( 'archivo.xml' );
    $xml = new SimpleXMLElement( $texto );
    $elementos = $xml->xpath('/utensilios/tenedor');
    print_r( $elementos );

    O bien pasar directamente la ruta al archivo al constructor:

    <?php
    $xml = new SimpleXMLElement( 'archivo.xml' );
    $elementos = $xml->xpath('/utensilios/tenedor');
    print_r( $elementos );

    Y dejar que SimpleXML se encargue de interpretar si se trata de uno u otro.

    Conclusión

    Si todavía no te convencí de usar SimpleXML en lugar de hacer las cosas a mano, te invito a leer un poco más sobre el formato XML (Especialmente el manejo de namespaces).

    Ahora, si tenés que interactuar con un sistema que está implementado usando XML, bueno… no hay muchas opciones.

    Si, por el contrario, estás pensando en usar algún formato de texto para guardar configuración o incluso para intercambiar con otros sistemas, te sugiero darle una mirada a JSON o a YAML