Delirios de un Informático

Cómo conectar PHP a SQL Server en Mac OS X

Siguiendo con mi serie de anotaciones sobre el desarrollo con PHP en Mac OS X, hoy toca hablar de algo que es más sencillo de lo que parece a primera vista: conectar PHP con SQL Server (virtualizado en una máquina Windows Server 2003 en mi caso) utilizando iODBC en Mac OS X Lion.

Con la versión de PHP que trae Lion de serie viene incluído el soporte para iODBC (una implementación de código abierto de ODBC y alternativa a unixODBC), al que le basta un controlador compatible para acceder a cualquier servidor de bases de datos. En este caso se recurre a FreeTDS, la implementeación de código abierto del controlador para SQL Server.

Como para muchas otras aplicaciones que se necesitan compilar en Mac OS X, he recurrido a Homebrew, que tras instalarlo sólo hay que ejecutar lo siguiente:

brew install freetds

La configuración también resulta sencilla: el archivo /usr/local/etc/freetds.conf debe contener lo siguiente al final:

[sqlserver]
	host = ip_o_host_del_servidor
	port = 1433
	tds version = 7.1

Luego, hay que crear el archivo /etc/odbcinst.ini con el siguiente contenido:

[FreeTDS]
Description = FreeTDS
Driver = /usr/local/lib/libtdsodbc.so
Setup = /usr/local/lib/libtdsodbc.so
UsageCount = 1

Por último, debe definirse el DSN creando el archivo /etc/odbc.ini:

[ejemplo]
Driver = FreeTDS
Database = ejemplo
Description = Base de datos de ejemplo
ServerName = sqlserver

Tras estos pasos, PHP debería poder conectarse al servidor SQL Server sin ningún problema. Pueden usarse las funciones ODBC de PHP para realizar la conexión:

$db = odbc_connect('ejemplo', 'usuario', 'contraseña');
$result = odbc_exec($db, "SELECT campo1, campo2 FROM tabla");
while($row = odbc_fetch_object($result)) var_dump($row);
odbc_close($db);

Si existe cualquier problema con la conexión, puede instalarse unixODBC con Homebrew (brew install unixodbc) y testear la conexión y obtener mensajes de error con el siguiente comando:

tsql -S ip_o_host_del_servidor -U sa

Por último es importante no añadir espacios o tabulaciones en los archivos .ini o iODBC no podrá obtener correctamente los valores causando un error en la conexión.

Code Igniter Reactor 2.0.0 y la línea de comandos

Una de las novedades de Code Igniter 2.0 es la incorporación de compatibilidad para su uso a través de la línea de comandos (antes había que recurrir a chapuzas). Se añade un intérprete que permite llamar a los métodos de los controladores a través de los parámetros de un script. Para eso, hay que definir el siguiente elemento en la configuración:

$config['uri_protocol'] = 'CLI';.

De este modo podremos realizar una llamada del tipo ./cli.php controlador parametro1 parametro2, lo que equivaldría a una dirección web como http://host/controlador/parametro1/parametro2. Mi recomendación es que el script ejecutado sea una copia del index.php en donde se añada la citada configuración (utilizando la nueva variable $assign_to_config), así como un #!/usr/bin/php -q en la primera línea para facilitar la ejecución directa.

Una vez dentro del código puede detectarse si la ejecución se realiza a través de línea de comandos simplemente determinando si se ha definido la constante STDIN, así como con la comprobación del elemento uri_protocol de la configuración de Code Igniter.

Por otro lado, puede ser necesario determinar quién está ejecutando el script para otorgar o no privilegios. Eso puede hacerse gracias a la variable superglobal $_ENV, en donde se define el elemento USERNAME. Por ejemplo, para comprobar si el script se ejecuta con privilegios de administrador (bien siendo root o mediante sudo) basta con el siguiente codigo:

$auth = (getenv('USERNAME') == 'root');

Actualizando a Code Igniter Reactor 2.0.0

Hace poco que se lanzó Code Igniter Reactor 2.0.0 con importantes novedades. Los chicos de EllisLab han publicado un changelog completo pero hay algunos detalles que no han agregado al proceso de migración:

  • Core libraries: si habías extendido las librerías CI_Loader o CI_Output deberás crear una carpeta llamada core y moverlas ahí, ya que en esta nueva versión estas librerías se separan de las librerías estándar
  • Ordenación de resultados en Active Record: se han cargado el método orderby() de Active Record para sustituirlo por order_by(), por lo que habrá que realizar el cambio para que funcionen las consultas (Regexxer es una excelente herramienta para estas tareas)
  • Traducciones: podían haber aprovechado para indicar que se ha creado un repositorio de traducciones de donde se pueden descargar ya

El resto de cambios están documentados y no suponen demasiado problema, por eso me encanta Code Igniter :).

PHP, CLI, STDOUT & STDERR

Suelo utilizar PHP como lenguaje para programar ciertos scripts que me faciliten muchas tareas, porque es el lenguaje que más conozco y por lo tanto el más rápido para desarrollarlos. Estos scripts pueden ejecutarse en línea de comandos como cualquier archivo Bash o Python y muchas veces requieren el uso de comandos del sistema que pueden llamarse a través de las funciones exec() o shell_exec(). El problema es que a veces estos comandos muestran una serie de datos que interesa capturar para procesarlos desde PHP y que no logran capturarse mediante estas funciones. Por eso, he rebuscado y encontrado el modo de solucionarlo:

$cmd = "/usr/bin/comando --parametros";
exec("$cmd 2>&1", $out, $err);

De este modo toda la salida de datos se pasa a PHP sin mostrarlo por pantalla :).

Cómo añadir nuevas reglas de validación en Code Igniter

Code Igniter trae de serie una sencilla pero potente librería para la validación de formularios, con una serie de reglas de validación predefinidas. Es posible añadir nuevas reglas externas a la librería creando nuevos métodos en el controlador, pero si esas reglas han de reutilizarse en más de un controlador el principio DRY no se cumple.

La solución pasa por extender la librería nativa añadiendo nuevos métodos que equilvaldrán a nuevas reglas. Por ejemplo, para poder realizar una comprobación con la nueva regla valid_url se extendería la clase nativa del siguiente modo:

<?php

class MY_Form_validation extends CI_Form_validation
    {
    function valid_url($url)
        {
        return (bool) preg_match('/^http:\/\/', $url);
        }
    }

Después, la comprobación se realizaría así:

$this->form_validation->set_rules('url', 'lang:url_incorrecta', 'valid_url');

En caso de ser necesario, puede utilizarse $ci =& get_instance(); para obtener una instancia del controlador y poder utilizar así otras librerías o modelos que puedan ser necesarios. Por supuesto, este truco también sirve para sustituir las reglas de validación nativas 🙂

Actualización: esto puede resultar particularmente útil para crear una regla que permita comprobar la existencia de un elemento en la base de datos cuando un campo que el usuario debe introducir es UNIQUE.