Blog

  • Cuál es el mejor Framework PHP

    Cuál es el mejor Framework PHP

    Ya escuchaste muchas veces cosas como «¿Cómo que no usás un framework de php?», ¿verdad?

    Y seguramente, de tanto escucharlo te dió curiosidad pero… al buscar te encontrás con que hay tantas opciones para elegir que parece que nunca vas a lograrlo.

    La pregunta es inevitable: ¿cuál es el mejor framework?

    Esto es casi como preguntar ¿a quién querés más? ¿a tu mamá o a tu papá?

    Como en muchas otras áreas de la tecnología la gente tiende a fanatizarse en favor de las herramientas que le resultan más familiares, con lo cual, encontrar opiniones objetivas es ciertamente difícil.

    No voy a dar muchas vueltas, el framework que a mí más me gusta es Symfony, pero de ninguna forma diría que es «el mejor».

    Conozco unos cuantos buenos:

    También hay algunos que se conocen como micro-frameworks:

    Si algo no te convence (el código generado, la facilidad o no para hacer las tareas simples, etc…), es un buen momento para pasar al siguiente candidato por el pre-filtro y así sucesivamente hasta que encuentres la horma de tu zapato 🙂

    ¿Cuál es tu framework favorito?

    En definitiva, la elección siempre será tuya.

    Cómo elegir un framework PHP

    Si bien la última palabra siempre es tuya, te puedo dar algunas ideas que, espero, te ayudarán a tomar la mejor decisión:

    1. ¿Cuándo se hizo el último commit? (Asumo que se trata de un framework de código abierto).
      1. Si ves que hace más de 1 año que no hay un commit lo más probable es que este framework esté discontinuado… mala señal
    2. ¿Qué tan grande es la comunidad de usuarios/desarrolladores?
      1. Si te cuesta mucho encontrar foros de discusión, blogs y demás indicios de que hay mucha gente usando/trabajando en el framework es otro punto en contra
    3. ¿Hay buena documentación?
    4. ¿Tiene alguna finalidad específica? Y en tal caso, ¿coincide con tus necesidades?

    Si las respuestas a estas preguntas te satisfacen el próximo paso es descargar el framework, intentar armar una app sencilla (tipo «Hola Mundo!») y ver cómo te resulta.

    Si algo no te convence (el código generado, la facilidad o no para hacer las tareas simples, etc…), es un buen momento para pasar al siguiente candidato por el pre-filtro y así sucesivamente hasta que encuentres la horma de tu zapato 🙂

  • En qué casos conviene usar un framework PHP

    En qué casos conviene usar un framework PHP

    O en otras palabras: ¿Framework sí o framework no? Qué dilema…

    La respuesta corta es en todos.

    Vamos con la respuesta larga:

    Qué es un Framework

    Existen muchas definiciones diferentes de framework.

    En el consenso general, se trata de un conjunto de librerías que sirven como base de una aplicación.

    Por qué usar un Framework PHP

    Tuve esta discusión bastantes veces con compañeros de trabajo y demás colegas y debo decir que fueron largas horas difícilmente bien invertidas.

    Admito sin emabrgo (nobleza obliga) que fue hace mucho tiempo… es raro escuchar hoy a alguien sostener la postura opuesta (La discusión hoy pasa por cuál framework elegir).

    La respuesta más elaborada (por si tenés tiempo y querés leer un poco más) es esta:

    Salvo que pienses desarrollar un negocio alrededor de un framework de tu propia autoría o tengas mucho tiempo y ganas de aprender mucho PHP, usar un framework estándar es una decisión que no debería tomarte más de 10 segundos.

    ¿Por qué? Simple: ¡tenés muchísimos problemas más importantes por resolver!

    El desarrollo de una aplicación web implica la combinación de una cantidad enorme de elementos (PHP, MySQL, HTML, CSS, JavaScript, etc…) sólo para que «pase algo» y a eso tenés que sumarle lo más importante: la lógica propia de tu aplicación (Para eso te contrataron después de todo ¿no?).

    ¿Para qué te vas a cargar encima con la responsabilidad de resolver miles de problemas que ya están resueltos?

    Por qué NO usar frameworks PHP

    Los frameworks agregan un montón de complejidad que mi aplicación no necesita

    Es probable que esto haya sido cierto hace tiempo. Ls frameworks modernos son sumamente modulares, lo que te permite usar sólo lo que necesitás (Por ejemplo, usando Composer).

    Un framework estándar agrega mucho overhead… mi aplicación necesita ser rápida

    Toda aplicación web necesita ser rápida.

    Cualquier framework decente tiene excelentes herramientas de caché (seguramente mejores que las que podrías implementar vos mismo).

    No confío en el código que no fue producido in-house

    El propio intérprete de PHP no fue producido por tu empresa y, seguramente no tenés mucho problema para justificar su uso, ¿cierto?.

    Diría acá que, salvo que trabajes para Google o Amazon, seguramente el código de un framework bien establecido va a ser mucho mejor que el producido in-house.

    La curva de aprendizaje es muy empinada

    Es posible (siempre dependerá de tu elección de framework), pero se transita una sola vez.

    Cuando hayas dominado el framework, ese conocimiento te va a acompañar en todos los proyectos que encares (incluso si te vas de la empresa donde trabajás actualmente).

    En fin… podría seguir dando razones para usar frameworks, pero me parece que quedó claro el punto.

    Sobre la segunda pregunta (¿Qué framework usar?) escribí esto.

  • Por qué usar una máquina virtual para proyectos PHP

    Por qué usar una máquina virtual para proyectos PHP

    Respuesta rápida: para evitar sorpresas desagradables.

    Paso a detallar un poco de qué estoy hablando con un par de historias que me pasaron hace unos cuantos años durante el desarrollo de un proyecto personal.

    Como la mayoría de los desarrolladores que están empezando, tenía instalado en mi computadora (Windows en aquel entonces) el paquete XAMPP y mi forma de trabajo era programar y probar en mi propia computadora y luego, mediante FTP, subir mis cambios al hosting que había contratado con la empresa de un amigo.

    Un día una de las últimas actualizaciones que había realizado a mi código funcionaba perfectamente en mi casa, pero al subirla a mi hosting las cosas eran un poco diferentes (léase: no andaba nada :p).

    Hago la historia corta: yo había escrito los nombres de las tablas de mis consultas en mayúsculas (algo como SELECT * FROM USERS), cuando los nombres en la base estaban en minúsculas. Claro, mientras estaba en un entorno incapaz de distinguir mayúsculas de minúsculas (Windows) no había problema, pero al deployar sobre un BSD que sí lo era… aparecieron los problemas.

    Tardé unos cuantos días en darme cuenta del problema (Y unas cuantas veces pensé en cambiar de profesión).

    Otro problema similar que tuve fue todavía más difícil de encontrar: mi versión de MySQL era un poquitín superior a la que estaba usando el hosting (Con lo cual, una función de la que mi código dependía simplemente no estaba disponible en Producción).

    Conclusión:

    El modo más seguro de evitar que aparezcan sorpresas al llevar tu código a Producción (O a cualquier otro ambiente para el caso), es programar en un entorno lo más parecido posible al productivo.

    Hay dos opciones básicas para lograr esto:

    1. Tener tu propio servidor que puedas configurar de acuerdo a las necesidades de tu aplicación (Probablemente un VPS)
    2. Configurar tu entorno de desarrollo de acuerdo a las capacidades que te brindará el servidor donde vayas a montar tu aplicación

    Independientemente de cuál sea tu elección, tener una máquina virtual específica para tu proyecto es la forma más práctica de generar código que corra sin problemas en el ambiente productivo.

    Así que en definitiva, la pregunta sería «¿Por qué NO usar una máquina virtual para proyectos PHP?»

  • Una máquina virtual lista para PHP+Symfony2+XDebug

    Charlando con algunos amigos desarrolladores php surgió un tema que les estaba resultando complicado, así que decidí poner mi pequeño granito de arena (para ellos y para otros que tal vez estén pasando por lo mismo).

    Ya habíamos hablado del por qué usar una máquina virtual para proyectos PHP. Ya estaba claro que usar un framework es más conveniente que no usarlo (independientemente de cuál fuera) y les había comentado sobre mis herramientas favoritas de automatización (Ansible y Vagrant).

    Todos estábamos de acuerdo «en la teoría», pero a la hora de pasar a la práctica se veían algo frustrados por no poder lograr tener una máquina virtual que fuese fácil de usar y que soportara, entre otras cosas, el uso de XDebug.

    Lo que te voy a mostrar a continuación son los archivos de configuración que yo usé en el último proyecto que hice en php:

    El archivo playbook.yml:

    ---
    - hosts: all
     sudo: true
     tasks:
    
    - name: create /var/www
     file: path=/var/www state=directory
    
    - name: create site symlink
     file: src=/vagrant dest=/var/www/site state=link
     notify: restart apache
    
    - name: install misc packages
     apt: name={{ item }} state=latest update_cache=true
     with_items:
     - ruby2.0
     - ruby2.0-dev
     - git
     - curl
     - unzip
     - vim
    
    - name: Symlink exists for Ruby 2.0
     file: src=/usr/bin/ruby2.0 dest=/usr/local/bin/ruby state=link
    
    - name: Symlink exists for Ruby Gems 2.0
     file: src=/usr/bin/gem2.0 dest=/usr/local/bin/gem state=link
    
    - name: install language packs for locale support
     apt: name={{ item }} state=latest
     with_items:
     - language-pack-de-base
     - language-pack-es-base
    
    # Apache2
    
    - name: ensure apache is installed
     apt: name=apache2 state=present
    
    - name: make sure apache is running
     action: service name=apache2 state=started enabled=true
    
    - file: src=/etc/apache2/mods-available/rewrite.load dest=/etc/apache2/mods-enabled/rewrite.load state=link
     notify: restart apache
    
    - file: src=/etc/apache2/mods-available/headers.load dest=/etc/apache2/mods-enabled/headers.load state=link
     notify: restart apache
    
    - copy: src=/vagrant/ansible/templates/site.conf dest=/etc/apache2/sites-available/site.conf remote_src=true
     notify: restart apache
    
    - file: src=/etc/apache2/sites-available/site.conf dest=/etc/apache2/sites-enabled/site.conf state=link
     notify: restart apache
    
    - file: path=/etc/apache2/sites-enabled/000-default.conf state=absent
     notify: restart apache
    
    - file: path=/etc/apache2/conf.d state=directory
    
    - copy: src=/vagrant/ansible/templates/fqdn.conf dest=/etc/apache2/conf.d/fqdn.conf remote_src=true
     notify: restart apache
    
    - copy: src=/vagrant/ansible/templates/nosendfile.conf dest=/etc/apache2/conf.d/nosendfile.conf remote_src=true
     notify: restart apache
    
    # MySQL
    
    - name: install MySQL
     apt: name={{ item }} state=latest
     with_items:
     - mysql-server
     - mysql-client
     - python-mysqldb
    
    - name: add mysql user
     mysql_user: name=vagrant
     host={{ item }}
     password=vagrant priv=*.*:ALL,GRANT
     login_user=root
     login_password=
     with_items:
     - '%'
     - localhost
    
    - name: create mysql databases
     mysql_db: name={{ item }}
     state=present
     with_items:
     - site_development
     - site_development_stats
     - site_testing
     - site_testing_stats
    
    - file: path=/etc/mysql/conf.d state=directory
     - name: Set MySQL number of connections
     copy: src=/vagrant/ansible/templates/max_connections.cnf dest=/etc/mysql/conf.d/max_connections.cnf remote_src=true
     notify: restart mysql
    
    - name: Install mysql command line client configuration file
     copy: src=/vagrant/ansible/templates/my.cnf dest=/home/vagrant/.my.cnf owner=vagrant group=vagrant remote_src=true
    
    # PHP
    
    - name: add php5.6 ppa
     apt_repository: repo='ppa:ondrej/php'
    
    - name: install PHP5.6 packages
     apt: name={{ item }} state=latest
     with_items:
     - php5.6
     - libapache2-mod-php5.6
     - php5.6-cli
     - php5.6-dev
     - php5.6-mysql
     - php-pear
     - php5.6-mcrypt
     - php5.6-gd
     - php5.6-curl
     - php5.6-xdebug
     - php5.6-memcache
     - php5.6-memcached
     - php5.6-readline
     - php5.6-xml
     - php5.6-mbstring
     - php5.6-zip
    
    - file: path=/etc/php5.6/conf.d state=directory
     - file: path=/etc/php5.6/cli/conf.d state=directory
     - file: path=/etc/php5.6/apache2/conf.d state=directory
    
    - copy: src=/vagrant/ansible/templates/php-site.ini dest=/etc/php5.6/conf.d/php-site.ini remote_src=true
     notify: restart apache
    
    - name: configure xdebug
     copy: src=templates/xdebug.ini dest=/etc/php/5.6/mods-available/xdebug.ini
     notify: restart apache
    
    - name: symlink common php configuration for cli handler
     file: src=/etc/php5.6/conf.d/php-site.ini dest=/etc/php5.6/cli/conf.d/php-site.ini state=link
     notify: restart apache
    
    - name: symlink common php configuration for apache2 handler
     file: src=/etc/php5.6/conf.d/php-site.ini dest=/etc/php5.6/apache2/conf.d/php-site.ini state=link
     notify: restart apache
    
    # phpmyadmin
    
    - name: install phpmyadmin
     apt: name=phpmyadmin state=latest
    
    # Assets compilation
    
    - name: add nodejs ppa
     apt_repository: repo='ppa:chris-lea/node.js'
    
    - name: install nodejs
     apt: name=nodejs state=latest
    
    # Set up site
    
    - file: src=/vagrant dest=/var/www/site state=link
     - file: path={{ item }} owner=vagrant group=vagrant mode=0777 state=directory
     with_items:
     - /var/cache/site
     - /var/cache/site/cache
     - /var/cache/site/clockwork
     - /var/cache/site/logs
     - /var/cache/site/meta
     - /var/cache/site/sessions
     - /var/cache/site/views
    
    - name: ensure once more that 000-default.conf is deleted
     file: path=/etc/apache2/sites-enabled/000-default.conf state=absent
     notify: restart apache
    
    - name: ensure that phpmyadmin's stock config is deleted
     file: path=/etc/apache2/conf.d/phpmyadmin.conf state=absent
    
    - name: set proper permissions for app/reports directory
     file: path=/vagrant/app/reports group=www-data owner=vagrant mode=0775 state=directory
    
    # Common stuff
    
    - name: Install Composer
     shell: curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer creates=/usr/local/bin/composer
    
    handlers:
     - name: restart apache
     action: service name=apache2 state=restarted
     - name: restart mysql
     action: service name=mysql state=restarted
     - name: restart beanstalkd
     action: service name=beanstalkd state=restarted

    Los archivos templates (deben estar en el directorio ansible/templates dentro de tu proyecto):

    site.conf:

    <VirtualHost *:80>
     ServerName myApp
     DocumentRoot /var/www/site/web
    
    <Directory />
     Options FollowSymLinks
     AllowOverride None
     </Directory>
    
    <Directory /var/www/site/web>
     DirectoryIndex app_dev.php
     Options Indexes FollowSymLinks MultiViews
     AllowOverride All
     Order allow,deny
     allow from all
     </Directory>
    
    ErrorLog /var/log/apache2/error.log
     LogLevel warn
     CustomLog /var/log/apache2/access.log combined
    
    ## enable phpmyadmin
    
    Alias /phpmyadmin /usr/share/phpmyadmin
    
    <Directory /usr/share/phpmyadmin>
     Options FollowSymLinks
     DirectoryIndex index.php
    
    <IfModule mod_php5.c>
     AddType application/x-httpd-php .php
    
    php_flag magic_quotes_gpc Off
     php_flag track_vars On
     php_flag register_globals Off
     php_admin_flag allow_url_fopen Off
     php_value include_path .
     php_admin_value upload_tmp_dir /var/lib/phpmyadmin/tmp
     php_admin_value open_basedir /usr/share/phpmyadmin/:/etc/phpmyadmin/:/var/lib/phpmyadmin/
     </IfModule>
    
    </Directory>
    </VirtualHost>

    fqdn.conf

    ServerName Localhost

    nosendfile.conf

    EnableSendfile off

    max_connections.cnf

    [mysqld]
    max_connections = 400

    my.cnf

    [client]
    user=vagrant
    password=vagrant

    php-site.ini

    max_execution_time = 30000
    memory_limit = 512M
    display_errors = On
    disable_functions =

    xdebug.ini

    zend_extension=xdebug.so
    xdebug.remote_enable=On
    xdebug.remote_connect_back=On

    Para iniciar el proyecto, basta con copiar y pegar esto en un archivo llamado Vagrantfile (en la raíz de tu proyecto):

    # -*- mode: ruby -*-
     # vi: set ft=ruby :
    
    # All Vagrant configuration is done below. The "2" in Vagrant.configure
     # configures the configuration version (we support older styles for
     # backwards compatibility). Please don't change it unless you know what
     # you're doing.
     Vagrant.configure("2") do |config|
     # The most common configuration options are documented and commented below.
     # For a complete reference, please see the online documentation at
     # https://docs.vagrantup.com.
    
    # Every Vagrant development environment requires a box. You can search for
     # boxes at https://atlas.hashicorp.com/search.
     config.vm.box = "ubuntu/trusty64"
    
    # Disable automatic box update checking. If you disable this, then
     # boxes will only be checked for updates when the user runs
     # `vagrant box outdated`. This is not recommended.
     # config.vm.box_check_update = false
    
    # Create a forwarded port mapping which allows access to a specific port
     # within the machine from a port on the host machine. In the example below,
     # accessing "localhost:8080" will access port 80 on the guest machine.
     config.vm.network "forwarded_port", guest: 80, host: 8080
    
    # Create a private network, which allows host-only access to the machine
     # using a specific IP.
     config.vm.network "private_network", ip: "192.168.33.10"
    
    # Create a public network, which generally matched to bridged network.
     # Bridged networks make the machine appear as another physical device on
     # your network.
     # config.vm.network "public_network"
    
    # Share an additional folder to the guest VM. The first argument is
     # the path on the host to the actual folder. The second argument is
     # the path on the guest to mount the folder. And the optional third
     # argument is a set of non-required options.
     # config.vm.synced_folder "../data", "/vagrant_data"
    
    # Provider-specific configuration so you can fine-tune various
    
    # backing providers for Vagrant. These expose provider-specific options.
     # Example for VirtualBox:
     #
     config.vm.provider "virtualbox" do |vb|
     # # Display the VirtualBox GUI when booting the machine
     # vb.gui = true
     #
     # # Customize the amount of memory on the VM:
     vb.memory = "2048"
     end
     #
     # View the documentation for the provider you are using for more
     # information on available options.
    
    # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
     # such as FTP and Heroku are also available. See the documentation at
     # https://docs.vagrantup.com/v2/push/atlas.html for more information.
     # config.push.define "atlas" do |push|
     # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
     # end
    
    # Enable provisioning with a shell script. Additional provisioners such as
     # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
     # documentation for more information about their specific syntax and use.
     # config.vm.provision "shell", inline: <<-SHELL
     # apt-get update
     # apt-get install -y apache2
     # SHELL
    
    #
     # Run Ansible from the Vagrant Host
     #
     config.vm.provision "ansible" do |ansible|
       ansible.playbook = "ansible/playbook.yml"
       ansible.verbose = "vv"
     end
    
    # Symfony needs to be able to write to it's cache, logs and sessions directory in var/
     config.vm.synced_folder "./var", "/vagrant/var",
       :owner => 'vagrant',
       :group => 'www-data',
       :mount_options => ["dmode=777","fmode=777"]
     end

    y ejecutar el comando (Obviamente, habiendo instalado Vagrant previamente…):

     vagrant up

    Y comienza la magia 🙂

    Un ratito después (Dependiendo de la conexión que tengas) vas a tener una máquina lista para desarrollar tu aplicación basada en Symfony2.

    Y ahora… ¡a codear!

    ¿Me olvidé de algo? ¡Avisame en los comentarios!

  • Cómo obtener la cotización del día de una acción con PHP

    Todo lo que voy a mostrarte acá se basa en la API de Yahoo Finance.

    Lo primero que tenés que hacer es instalar composer.

    Segundo, inicializar el proyecto:

    php composer.phar init

    Tercero: agregar la dependencia del paquete https://github.com/scheb/yahoo-finance-api:

    php composer.phar require scheb/yahoo-finance-api

    Y después podés usar un código como este:

    #!/usr/bin/php
    <?php
    
    require __DIR__ . '/vendor/autoload.php';
    
    $client = new \Scheb\YahooFinanceApi\ApiClient();
    
    $d = new DateTime($argv[2]);
    
    echo "Buscando ".$argv[1]." en fecha: ".$d->format('d/m/y').PHP_EOL;
    try {
      $data = $client->getHistoricalData($argv[1], $d, $d);
    
      echo $data['query']['results']['quote']['Close'].PHP_EOL;
    } catch ( Exception $e ) {
      echo $e->getMessage().PHP_EOL;
    }
    

    En este ejemplo, lo que tenés es una utilidad de línea de comandos que recibe dos parámetros:

    1. El ticker (Símbolo del papel en cuestión, por ejemplo TS para la acción de Tenaris)
    2. La fecha.

    La llamada sería así:

    php get_stock_price.php TS Yesterday

    y el resultado será algo como:

    Buscando TS en fecha: 15/12/16
    34.220001
    
  • Webscrapping con PHP

    Webscrapping con PHP

    Me encontré recientemente con este problema desarrollando un sistema para un cliente y creo (¡y espero!) que mi experiencia pueda ayudar a otros.

    El desafío era el siguiente: nuestro cliente es una empresa que se dedica a la administración de activos financieros. Como parte de su operatoria, requieren la consolidación de información que actualmente está dispersa en una serie de planillas Excel.

    Parte de esa información se refiere a movimientos de acciones y bonos. Una de las tareas que se realizaba manualmente era el cálculo de cuánto dinero se había movido al realizar una compra o venta de alguno de estos instrumentos (Simple: cantidad de títulos por precio del título al día de la transacción).

    La complejidad de este cálculo reside en cómo conseguir el precio que tenía el activo al día en que se realizó la transacción. Antes de la intervención de Leeway esta tarea estaba a cargo de una empleada de la compañía (Entre muchas otras, tenía que buscar en el sitio de Yahoo Finance u otro similar y completar ese dato).

    Desarrollamos una aplicación que fuera capaz de consultar esa información y realizar ese cálculo en forma automatizada.

    No es realmente complicado hacerlo cuando se cuenta con una API bien hecha y documentada (Incluso mejor si tenemos a mano un SDK para PHP).

    Lo complicado del tema fue sacar la información de bonos, para la cual no encontramos ninguna fuente pública que tuviera buena información (¿Conocés alguna?, te invito a que dejes un comentario 🙂 ), con lo cual… no quedó opción más que arremangarse y hacer algo de web scrapping (Todo sea por ahorrarle unas horas de rastreo todos los meses a un cliente).

    Así que ahí nos metimos, cURL y SimpleHTMLDom en mano, a remover la maleza y a ver qué encontrábamos.

    Y la verdad… lo que encontramos no fue nada bonito. La primera misión fue entender todo el camino que un usuario humano tenía que recorrer para llegar a la información que nosotros queríamos obtener.

    Una vez que tuvimos esa información, nuestro primer intento fue apuntarle a la última URL con un simple GET y escarbar el resultado… no señor, ¿tan fácil iba a ser el tema?

    Lo primero que notamos era que la URL final tenía poco en común con la inicial (y sin acceso a la base de datos que andaba por detrás… difícil hacer la conversión…). Bien, retrocedamos dos casilleros.

    Nuevamente, bajemos el contenido de la primera URL y busquemos el link que necesitamos (¡Gracias S.C. Chen y compañía por ponerle sintaxis tipo jQuery al SimpleHTMLDom!).

    ¡Primera prueba superada! Tenemos una nueva página que está un pasito más cerca. Veámosla un poco más de cerca… ¡ahá! ¡Se trata de un formulario que va por POST! Ningún problema, nuestro amigo FireBug nos mostrará el camino. Ah, pero esto es muy simple… una pequeña llamada Ajax y voilà.

    Otra vez… ¡no tan rápido! Faltan los parámetros invisibles que se generan del lado del servidor… jejeje (Léase con risa de programador malicioso). Ok, volvamos a revisar ese HTML. Bien, acá están esos input hidden, no hay problema, los agregamos y listo.

    ¿Cómo que 404? ¡Si estoy viendo la información! ¿qué te pasa cURL?

    ¿Cómo? ¿Que el valor de un campo no es un literal si no una expresión?… qué ganas de complicarle la vida al prójimo… bueno… usemos el eval de php. Esta parte sí te la puedo mostrar:

    foreach ( $dom->find('.ajax-token') as $token ) {
      if ( $token->attr['name'] == '__atcrv' ) {
        $atcrv = eval('return '.$token->attr['value'].';');
      }
    }<div class="open_grepper_editor" title="Edit &amp; Save To Grepper">

    Y ahora, ¿qué otra sorpresita hay por ahí? ¿La respuesta (si da 200) viene gzipeada? 0 problema: gzdecode se encarga y por fín, tenemos a mano la tablita con los precios del bono buscado para el día buscado. Muchas gracias, buenas noches.


    La historia fue para poner en contexto, pasando en limpio (para generalizar un poco también):

    1. Todo lo que un navegador hace, cURL puede hacerlo también (Iba a decir, todo lo que una persona hace, pero después me vino a la mente el reCaptcha).
    2. Este mecanismo no es ni de lejos ideal. Basta con un pequeño cambio en el maquetado del sitio para que todo se rompa (Pero bueno… si no disponemos de una buena API, no creo que haya otro mucho mejor)
    3. No hay que temerle a un poco de ingeniería inversa 🙂
    4. La solapa Net de FireBug da un montón de información súper útil.

    ¿Me olvidé de algo importante?