Redimensionar imágenes en PHP

Es habitual que cuando en una web trabajamos con imágenes tengas que presentar la misma en varios tamaños. Una opción es redimensionarla en pantalla con CSS… pero es una mala opción. Es una mala opción porque lo que haces es cargar una imagen grande y luego procesarla para hacerla pequeña, por lo que ralentizas la carga de la página. ¿La solución? Guardar una copia reducida de la imagen, lo que se llama un thumbnail.

¿Y cómo reducir la imagen? Bueno, PHP incluye la librería GD que nos permite tratar y creawr imágenes. Y además, en este caso, vamos a redimensionar la imagen manteniendo la proporción.

//Ruta de la original
$rtOriginal="/images/example.jpg";
	
//Crear variable de imagen a partir de la original
$original = imagecreatefromjpeg($rtOriginal);
	
//Definir tamaño máximo y mínimo
$max_ancho = 150;
$max_alto = 150;

//Recoger ancho y alto de la original
list($ancho,$alto)=getimagesize($rtOriginal);

//Calcular proporción ancho y alto
$x_ratio = $max_ancho / $ancho;
$y_ratio = $max_alto / $alto;

Con esto ya tenemos los datos para mantener las proporciones de la imagen. Ahora toca calcular el tamaño:

if( ($ancho <= $max_ancho) && ($alto <= $max_alto) ){
//Si es más pequeña que el máximo no redimensionamos
	$ancho_final = $ancho;
	$alto_final = $alto;
}
//si no calculamos si es más alta o más ancha y redimensionamos
elseif (($x_ratio * $alto) < $max_alto){
	$alto_final = ceil($x_ratio * $alto);
	$ancho_final = $max_ancho;
}
else{
	$ancho_final = ceil($y_ratio * $ancho);
	$alto_final = $max_alto;
}

Con esto ya tenemos el tamaño de la imagen con las proporciones guardadas. Finalmente nos queda crear la imagen y guardarla:

//Crear lienzo en blanco con proporciones
$lienzo=imagecreatetruecolor($ancho_final,$alto_final);	

//Copiar $original sobre la imagen que acabamos de crear en blanco ($tmp)
imagecopyresampled($lienzo,$original,0,0,0,0,$ancho_final, $alto_final,$ancho,$alto);

//Limpiar memoria
imagedestroy($original);

//Definimos la calidad de la imagen final
$cal=90;

//Se crea la imagen final en el directorio indicado
imagejpeg($lienzo,"./images/thumb.jpg",$cal);

Todo el cálculo, por otra parte, es necesario sólo si quieres mantener la proporción, si no la cosa queda más sencilla:

//Ruta de la original
$rtOriginal="/images/example.jpg";
	
//Crear variable de imagen a partir de la original
$original = imagecreatefromjpeg($rtOriginal);
	
//Definir tamaño máximo y mínimo
$ancho_final = 150;
$alto_final = 150;

//Recoger ancho y alto de la original
list($ancho,$alto)=getimagesize($rtOriginal);

$lienzo=imagecreatetruecolor($ancho_final,$alto_final);	

//Copiar $original sobre la imagen que acabamos de crear en blanco ($tmp)
imagecopyresampled($lienzo,$original,0,0,0,0,$ancho_final, $alto_final,$ancho,$alto);

//Limpiar memoria
imagedestroy($original);

//Definimos la calidad de la imagen final
$cal=90;

//Se crea la imagen final en el directorio indicado
imagejpeg($lienzo,"./images/thumb.jpg",$cal);

Esto para imágenes jpg. Para png la diferencia sería usar imagepng() para crear, y para gif imagegif(). En la web de php puedes investigar más sobre la librería GD y sacarle todo el jugo.

Seleccionar filas aleatoriamente en MySQL

Existen varias opciones a la hora de seleccionar filas de forma aleatoria en MySQL, haciendo uso de la función RAND(). Vamos a imaginar que quieres obtener 3 filas aleatorias de una base de datos. Lo más sencillo es usar la siguiente sintaxis:

SELECT * FROM tabla ORDER BY RAND() LIMIT 3;

El problema de este método es que tiene que generar una tabla temporal completa con todos los datos de la tabla original reordenados aleatoriamente… en fin, que es una sangría de recursos. Existe una opción, que es generar sólo una tabla con los registros que queramos, mediante una subconsulta. Tal que así:

SELECT * FROM tabla WHERE RAND()<(SELECT ((3/COUNT(*))*10) FROM tabla) ORDER BY RAND() LIMIT 3;

Un método que sigue gastando muchos recursos, pero menos al crear sólo una tabla temporal con la cantidad de filas que necesitamos (cantidad que necesitamos / total * 10).

Pero si tenemos un índice autonúmerico en nuestra tabla podemos ahorrar más recursos:

SELECT t.* FROM tabla AS t JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) FROM tabla)) AS id) AS x WHERE t.id >= x.id LIMIT 3;

Este método no genera un valor aleatorio para cada fila consultada, por lo que ahorra muchos recursos en el servidor. Eso sí, no te dará los registros ordenados aleatoriamente, sino que será registros secuenciales, pero comenzando en un valor aleatorio. Es decir, los dos primeros podrían devolverte las filas 1, 21 y 42, este en cambio te devolvería 1,2,3 – 41,42,43… si quieres que sea un resultado aleatorio de verdad deberías realizar la consulta varias veces con limit 1… con lo que el ahorro de recursos se va al carajo. Además, puede fallar si hay algún hueco en la secuencia del campo autonumérico (por ejemplo, si has borrado un valor).

Una última opción, en lugar de generar el valor aleatorio a nivel de base de datos generarlo antes, en el código del servidor. Un ejemplo en PHP (que también tiraría de id autonumérica y que también daría problemas si tuviera huecos, ojo):

$result = $mysqli->query("SELECT * FROM tabla WHERE id in ROUND(".lcg_value()."*(SELECT COUNT(*) FROM tabla)) LIMIT 1")

El ejemplo de arriba nos devolvería una fila aleatoriamente, por lo que bastaría con repetirla en un bucle tantas veces como necesitemos (en este caso el problema vendría de la cantidad de conexiones que hay que abrir a la base de datos). Puedes intentar implementar lcg_value() en los otros métodos citados, y comprobar si el rendimiento mejora.

Consumir JSON en Android

Si el otro día hablaba de crear y consumir JSON en PHP, ahora toca su turno a Android.

¿Por qué usar JSON? Bueno, es un formato de intercambio de datos independiente de cualquier lenguaje y autodefinido. Ok, XML también, pero JSON es más ligero y fácil de parsear.

Existen varias librerías para JSON en Android, pero en el ejemplo usaremos los métodos nativos del SDK: las clases JSONArray y JSONObject.

JSONArray necesita recibir una cadena JSON para devolvernos un array de JSONObject. Supongamos que hemos recibido esos datos (sea de un web service o de un archivo) y los tenemos en una variable llamada data:

JSONArray jsArray = new JSONArray(data);

Ahora ya tenemos los datos dentro de un JSONArray. ¿Cómo lo recorremos? Pues con un for:

for (int i = 0; i < jsArray.length(); i++) {
    JSONObject jsObject = jsArray.getJSONObject(i);
    
    int intejemplo = jsObject.getInt(atributo1);
    String stringejemplo = jsObject.getString(atributo2);
}

El problema es que trabajar así puede ser muy plomífero, tratando los datos a mano, uno a uno. Existen librerías que te pueden echar un cable en esto, dándote mayores funcionalidades. En este caso las dos más populares son GSon y Jackson. En los enlaces podrás ver documentación sobre su uso.

El saber manipular JSON se torna capital cuando quieres trabajar con servicios web, o para comunicar apps con widgets.

Trabajando con JSON en PHP: crear y consumir JSON

JSON se ha convertido en una herramienta fundamental en la programación web, ya sea en servicios web, para comunicar con apps de móviles, pasar datos de php a javascript…

Desde PHP podemos tanto crear un JSON como recibirlo y manejarlo. Para la creación debemos utilizar la función json_encode(), que recibe lo que queramos serializar (una cadena, una variable numérica, un array, un objeto) y devuelve una cadena con los datos serializados. Si le pasamos un array normal o una cadena, no veríamos mucho cambio tras la serialización, pero si le pasamos un array asociativo, tendríamos un resultado como el del ejemplo:

$miArray = array("ferrari"=>"rojo", "porsche"=>"plateado", "mercedes"=>"negro");
$string = json_encode($miArray);
//el valor de string sería {"ferrari":"rojo","porsche":"plateado","mercedes":"negro"} 

Pero la verdadera potencia la encontramos cuando la aplicamos a objetos, ya que JSON nos permite serializar cualquier objeto de PHP, con el mismo procedimiento que usamos arriba: la función json_encode.

Una cuestión a la hora de trabajar con JSON que debemos tener en cuenta es la codificación de caracteres. En principio json_encode() ya convierte a Unicode los resultados, ya que en la notación JSON usamos siempre Unicode. El problema es que PHP por defecto tratará la cadena de origen como UTF-8, así que si no estás trabajando con esta codificación, y estás usando, por ejemplo, ISO-8859-1, se tornará necesario que codifiques las cadenas a UTF-8 antes de convertirlas en JSON. Esto se hace con la función utf8_encode().

$miArray = array("palabro1"=>utf8_enconde("ñandú"), "palabro2"=>"maniqueo", "palabro3"=>utf8_encode("bandoneón");
$cadenajson = json_encode($miArray);

Con esto ya recibiríamos las cadenas codificadas en UTF-8 y prevendríamos errores de codificación.

Ok, con esto hemos visto como generar un JSON, pero ¿cómo lo consumimos?.

Bueno, primero habría que destacar que existen varias librerías para JSON en PHP, pero desde PHP 5.2 este incluye librerías nativas desarrolladas en C, que son las más recomendables por cuestiones de rendimiento. Ahora vamos a suponer un objeto tal como el del ejemplo:

$strjson = '{
   "elemento1": "valor1",
   "elemento2": "valor2",
   "elemento3": null,
   "otroobj": {
      "a ver": "oh",
      "venga": "va"
   };

Esa sería la cadena JSON que recibiría nuestro código PHP con un objeto serializado ¿cómo la transformamos en objeto? Pues si serializábamos con json_encode() el paso contrario será con json_decode():

$objetophp = json_decode($strjson);

//si imprimimos objetophp con print_r() nos dará:
stdClass Object
(
   [elemento1] => valor1
   [elemento2] => valor2
   [elemento3] =>
   [otroobj] => stdClass Object
   (
      [a ver] => oh
      [venga] => va
   )
) 

Al tener un objeto puedes acceder a sus propiedades como en cualquier objeto normal de PHP.

Una serie de avisos: Recuerda, como dijimos antes, que las cadenas deben estar en UTF-8, si usas otra codificación tendrás que tirar de utf8_encode. Los nombres tanto de las propiedades como de los valores deben ir entre comillas dobles, no se aceptan comillas simples y sólo los valores numéricos o null pueden ir sin comillas. No te dejes ni llaves ni corchetes abiertos, ni ninguna coma de más. Todo eso provocará fallos.

Crear un web service RESTful con PHP

Ya que ayer hablaba de crear un web service con SOAP hoy toca complementar la información creando un web service REST. Esto nos permitirá crear lo que podríamos llamar una RESTful API.

En este caso nos vamos a servir del framework Slim y de la librería NotORM para el acceso a la base de datos.

Lo primero que haremos será crear una suerte de Hello World con Slim, para probar su funcionamiento

<?php
require "Slim/Slim.php";

// creamos una nueva instancia de Slim
$ejemplo = new Slim();

// agregamos una nueva ruta y un código
$ejemplo->get("/", function () {
    echo "<h1>Ejemplo</h1>";
});

// corremos la aplicación
$ejemplo->run();

Vale, ejemplo simplón de cómo usar Slim, más adelante veremos algo más. Ahora vamos con la conexión a la base de datos, en la que usaremos NotORM y PDO:

<?php
require "NotORM.php";

$pdo = new PDO($dsn, $username, $password);
$db = new NotORM($pdo);

Ok, conexión creada. Vamos a suponer que tenemos una base de datos con nombres de discos. La idea sería crear un webservice que nos devolviera todos los discos de la base de datos, codificados como un objeto JSON:

<?php

$app = new Slim(
    "MODE" => "developement",
    "TEMPLATES.PATH' => "./templates"
);

$app->get("/discs", function () use ($app, $db) {
    $discs = array();
    foreach ($db->discs() as $disc) {
        $discs[]  = array(
            "id" => $disc["id"],
            "title" => $disc["title"],
            "band" => $disc["band"],
            "genre" => $disc["genre"]
        );
    }
    $app->response()->header("Content-Type", "application/json");
    echo json_encode($discs);
});

Bueno, primero creamos una instancia de Slim, definiendo que la utilizaremos en un entorno de desarrollo y apuntando en qué carpeta están las plantillas de la maquetación. Verás que en el ejemplo hemos llamado al método get(), es uno de los métodos de Slim que te enruta a una petición GET en la url que se le envía como primer argumento. Como segundo argumento le enviamos una función callback que se ejecutará como respuesta. La palabra use nos permite acceder a las variables externas desde dentro del ámbito de aplicación de la función anónima.

Ahora un ejemplo donde se haría lo mismo pero pidiendo los datos de un disco concreto:

<?php
$app->get("/disc/:id", function ($id) use ($app, $db) {
    $app->response()->header("Content-Type", "application/json");
    $disc = $db->discs()->where("id", $id);
    if ($data = $disc->fetch()) {
        echo json_encode(array(
            "id" => $data["id"],
            "title" => $data["title"],
            "band" => $data["band"],
            "genre" => $data["genre"]
            ));
    }
    else{
        echo json_encode(array(
            "status" => false,
            "message" => "No existe un disco con el id $id "
            ));
    }
});

A diferencia del anterior ves que en la ruta ahora enviamos la id del disco a buscar como parámetro.

Ok, esto nos permite insertar datos, pero ¿para insertar y modificar? Bueno, vamos a hacer un método para insertar los datos (mediante el método post() de Slim) y otro para actualizarlos (el método put()):

<?php

$app->post("/disc", function () use($app, $db) {
    $app->response()->header("Content-Type", "application/json");
    $disc = $app->request()->post();
    $result = $db->discs->insert($disc);
    echo json_encode(array("id" => $result["id"]));
});

$app->put("/disc/:id", function ($id) use ($app, $db) {
    $app->response()->header("Content-Type", "application/json");
    $book = $db->discs()->where("id", $id);
    if ($disc->fetch()) {
        $put = $app->request()->put();
        $result = $disc->update($put);
        echo json_encode(array(
            "status" => (bool)$result,
            "message" => "El disco $id ha sido actualizado"
            ));
    }
    else{
        echo json_encode(array(
            "status" => false,
            "message" => "El disco $id no existe"
        ));
    }
});

El primer método nos devuelve el id del último disco insertado, el segundo nos devuelve un valor verdadero o falso y un mensaje de error u éxito. Supongamos que quisieras acceder a esto por medio de un formulario (aunque lo normal en una de estas API es hacerlo mediante el envío de datos por la URL). ¿Cómo diferenciarías si es una nueva inserción o una edición? Fácil, crea un campo oculto llamado _METHOD y dale el valor PUT, tal cual como en el ejemplo:

</pre>
<form class="hiddenSpellError">action="#" method="post"></form>
<pre>
   <input class="hiddenSpellError" type="text" />type="hidden" name="_METHOD" value="PUT"></pre>
Title: <input class="hiddenSpellError" type="text" />type="text" name="title">
<pre></pre>
Band: <input class="hiddenSpellError" type="text" />type="text" name="band">
<pre></pre>
Genre: <input class="hiddenSpellError" type="text" />type="text" name="genre">
<pre>

   <input class="hiddenSpellError" type="text" />type="submit" value="Submit">
</form>

Ok. Nos queda el borrar datos para tener todas las operaciones CRUD en nuestra RESTful API. Ahora nos serviremos del método delete() de Slim.

<?php

$app->delete("/disc/:id", function ($id) use($app, $db) {
    $app->response()->header("Content-Type", "application/json");
    $disc = $db->discs()->where("id", $id);
    if ($disc->fetch()) {
        $result = $disc->delete();
        echo json_encode(array(
            "status" => true,
            "message" => "Disco borrado exitosamente"
        ));
    }
    else{
        echo json_encode(array(
            "status" => false,
            "message" => "El disco con id $id no existe"
        ));
    }
});

El framework Slim provee de muchas posiblidades a la hora de crear servicios web. Esto es una explicación básica de cómo se crearía cada método, pero las posibilidades son muy grandes. En su página de documentación puedes estudiar cómo sacar el 100% a este framework.

Crear un webservice básico con PHP y SOAP

SOAP es un protocolo de intercambio para servicios web basado en XML, sobre el que puedes leer en esta entrada de la wikipedia. Para no liarnos con preámbulos, vamos con la chicha ¿cómo creamos un servicio web en PHP?.

Para facilitarnos la vida empezaremos por descargar NuSOAP, un toolkit para el desarrollo de servicios web con SOAP en PHP, que nos proveerá de diversas clases para trabajar con este protocolo. Basta con descargarlo, descomprimirlo, meterlo dentro de nuestro proyecto y, cuando queramos usarlo, incluir nusoap.php como librería.

Ok, con NuSOAP instalado toca crear un servidor SOAP en nuestra aplicación. Para el ejemplo crearemos un servidor, lo llamaremos producto.php, que si recibe una petición donde se le pida una lista de libros devuelva tres títulos (es un ejemplo básico, piensa que en la realiad podrías acceder a una base de datos y dar muchas más funcionalidades).

<?php
	require_once "nusoap.php";
	 
        function getProd($categoria) {
	    if ($categoria == "libros") {
	        return join(",", array(
	            "El señor de los anillos",
	            "Los límites de la Fundación",
	            "The Rails Way"));
	    }
	    else {
	            return "No hay productos de esta categoria";
	    }
	}
	 
	$server = new soap_server();
	$server->register("getProd");
	$server->service($HTTP_RAW_POST_DATA);
?>

Ok, ahora necesitas un cliente, que llamaremos cliente.php. En el constructor, el cliente recibirá el url del servidor y para acceder al método que nos devuelve los libros recurriremos al método call(), al cual le pasaremos el nombre del método del servidor al que queremos acceder y los parámetros en forma de array. Además, también controlaremos que no haya errores en la comunicación.

<?php
	require_once "nusoap.php";
	$cliente = new nusoap_client("http://localhost/producto.php");
	 
	$error = $cliente->getError();
	if ($error) {
	    echo "<h2>Constructor error</h2><pre>" . $error . "</pre>";
	}
	 
	$result = $cliente->call("getProd", array("categoria" => "libros"));
	 
	if ($cliente->fault) {
	    echo "<h2>Fault</h2><pre>";
	    print_r($result);
	    echo "</pre>";
	}
	else {
	    $error = $cliente->getError();
	    if ($error) {
	        echo "<h2>Error</h2><pre>" . $error . "</pre>";
	    }
	    else {
	        echo "<h2>Libros</h2><pre>";
	        echo $result;
	        echo "</pre>";
	    }
	}
?>

Con esto ya tienes una idea múy básica del funcionamiento de un webservice SOAP construído con PHP. Pero claro, nos falta un archivo WSDL para tener un webservice decente. Aunque dicho archivo puede ser escrito a mano, NuSOAP puede generarlo por ti pasándole ciertos parámetros, por lo que lo ideal sería generarlo en el servidor. Así que modifica tu producto.php para que quede tal que así:

<?php
	require_once "nusoap.php";
	 
	function getProd($categoria) {
	    if ($categoria == "libros") {
	        return join(",", array(
	            "El señor de los anillos",
	            "Los límites de la Fundación",
	            "The Rails Way"));
	    }
	    else {
	        return "No hay productos de esta categoria";
	    }
	}
	 
	$server = new soap_server();
	$server->configureWSDL("producto", "urn:producto");
	 
	$server->register("getProd",
	    array("categoria" => "xsd:string"),
	    array("return" => "xsd:string"),
	    "urn:producto",
	    "urn:producto#getProd",
	    "rpc",
	    "encoded",
	    "Nos da una lista de productos de cada categoría");
	 
	$server->service($HTTP_RAW_POST_DATA);
?>

Como ves, el cambio en es cuando llamamos a register, ya que en vez de pasarle, como antes, el método en cuestión, le añadimos también varios argumentos para generar el WSDL:

  • El primer array nos permite definir el argumento de entrada y su tipo de datos
  • El segundo define la función de retorno y su tipo de datos
  • urn:producto es la definición del namespace
  • urn:producto#getProd es donde definimos la acción SOAP
  • Luego viene el tipo de llamada,que puede ser rpc, como en el ejemplo, o document
  • Tras esto definimos el valor del atribute use, que puede ser encoded o literal
  • Finalmente viene una descripción de qué hace el método al que llamamos

Ahora basta con que en el navegador accedas a producto.php?wsdl y verás el WSDL generado. Ya puedes copiarlo y añadirlo a tu directorio web (crea un archivo y llámalo, por ejemplo, libros.wsdl). Para que el cliente lo utilice debes modificar el código, y en el constructor, en vez del url le pasas el nombre del archivo, de forma que quede como en el ejemplo:

         $client = new nusoap_client("libros.wsdl", true);

Ahora sí, ya tienes montado un pequeño servicio web. El ejemplo es simplón, pero piensa en todas las funcionalidades que podrías incorporarle.

Métodos mágicos en PHP

Reconozco que no había oído hablar de esta característica de PHP 5 hasta estas navidades, cuando un experimentado programador php me comentó que era algo que debía aprender (el mismo que me recomendó aprender Symphony, por cierto). La verdad es que el nombre suena rimbobante: «Métodos mágicos»… te quedas como «no se si será una decepción o si será la leche». Luego, curiosamente durante el transcurso de una entrevista laboral me volvieron a preguntar si sabía del tema, así que decidí informarme.

Tras leer sobre el tema he de decir que no me queda muy claro del todo el uso de dichos métodos. Se trata de una serie de métodos comunes a todas las clases que se crearon para PHP5 y que refuerzan la orientación a objetos del lenguaje. Estos métodos comienzan su nombre con __ por lo que se recomienda no crear métodos propios que empiecen así. Los citados métodos mágicos son:

  • __construct()
  • __destruct()
  • __call()
  • __callStatic()
  • __get()
  • __set()
  • __isset()
  • __unset()
  • __sleep()
  • __wakeup()
  • __toString()
  • __invoke()
  • __setState()
  • __clone()

Si has programado en Java unos cuántos ya te sonarán, porque no es sólo que se llamen igual, es que tienen una funcionalidad similar.

Por lo que he entendido leyendo la documentación, el método __sleep() es llamado cuando se utiliza el método serialize(), se ejecuta antes de comenzar la serialiación y nos permite definir un array con los nombres de todas las variables del objeto que se va a serializar. Por su parte __wakeup() nos permitiría reinicilizar el objeto después de la serialización (restablecer conexiones a la base de datos, por ejemplo), y se ejecuta cuando se utliza unserialize().

Por su parte __invoke() es llamado cuando se intenta llamar a un objeto como si fuera una función. Sí, suena raro, así que os pongo un ejemplo:

<?php
class CallableClass
{
    public function __invoke($x)
    {
        var_dump($x);
    }
}
$obj = new CallableClass;
$obj(5);
?>

//esto devolvería int(5)

En cuanto a __toString(), nos permite decidir qué se imprimirá si el objeto es llamado como si fuera una cadena de texto. Por ejemplo, si haces echo $objeto.


<?php
class TesteandoToString
{
    public $imprimir;

    public function __construct($imprimir)
    {
        $this->imprimir = $imprimir;
    }

    public function __toString()
    {
        return $this->imprimir;
    }
}

$class = new TesteandoToString('Hola caracola');
echo $class;

//esto imprimiría Hola caracola
?>

El método __set_state() nos permite recoger un array con las variables exportadas por el método var_export() y asignarlas a un nuevo objeto. No confundir con la funcionalidad del método __clone(), que directamente crea una copia exacta de otro objeto.

Los métodos __call(), __callStatic(), __set(), __get(), __isset() y __unset() se utilizan para la sobrecarga, lo que nos permite «crear» dinámicamente propiedades y métodos. A estos métodos no se les pueden pasar parámetros por referencia. Se invoca a estos métodos cuando queremos trabajar con propiedades o métodos que no se han declarado o que no son visibles. El funcionamiento de __get() y __set() es similar al que pueden tener en Java los métodos get() y set(): el primero nos permite acceder a las propiedades privadas del objeto desde fuera de la clase y el segundo nos permite modificarlos. Puedes imaginar que __unset() lo que hará será «limpiar» de valor la propiedad y que __isset() funciona como isset() pero permitiendo el acceso a los valores privados. En cuanto a __call(), nos permitirá llamar a métodos inaccesibles en un contexto de objeto, y __callStatic() lo mismo pero en un contexto estático.

Finalmente __construct() es un método constructor, y será lo primero que se ejecute al crear un nuevo objeto de la clase, y __destruct() será lo último que se ejecute antes de eliminar el objeto de la memoria.

En fin, con todos estos métodos está claro que PHP gana mucho en cuanto a orientación a objetos.

Evitar SQL-injection en PHP

Los ataques SQL-Injection son unos de los más habituales en el mundo web. Aquí vamos a ver una serie de consejos para evitar estos agujeros en la seguridad:

Lo primero es seguir una serie de consejos a nivel de administración, como limitar los permisos del usuario a nivel de base de datos. Por ejemplo, en la mayoría de aplicaciones web lo habitual es que no tenga que utilizarse DROP, por lo que por seguridad sería mejor no permitir ya al usuario hacerlo. También sería interesante no dar información extra al atacante evitando sacar en pantalla los errores de la base de datos. En ese caso lo ideal es capturar el error haciendo uso de exception. Tampoco hay que confiarlo todo a las validaciones por javascript, porque el atacante puede saltárselas desactivándolo desde el navegador, por lo que mejor hacer las validaciones del lado del servidor. Y finalmente escapar las comillas con msqli_real_scape_string (en caso de que usemos MySQL), pg_scape_string (en caso de PostgreSQL) o addslashes (por si utilizamos otro SGBD). También podemos utilizar htmlentities para convertir los textos en entidades html, como un plus a la seguridad. Abajo un simple ejemplo:


<?php

try{
  $query = sprintf("SELECT * FROM users WHERE id=%d", mysqli_real_escape_string($id));
  $query = htmlentities($query);
  mysqli_query($query);
}catch(Exception $e){
    echo('Lo sentimos, ha habido un error en la conexión');
}
?>

Como medida extra se podrían utilizar expresiones regulares para evitar la inserción de ciertas palabras (SELECT, DROP, UNION…), si bien puede resultar poco práctico, sobre todo si tu software está destinado al mercado británico.

Pero si todo esto te parece lioso, hay una alternativa: la clase PDO. Dicha clase nos facilitará mucho la vida a la hora de trabajar con bases de datos, ya que nos permite abstraernos del SGDB que estemos utilizando. Si por ejemplo, en una página donde no utilices PDO o algo similar sino el mysql_connect simple, decides migrar tu web de MySQL a PostgreSQL tendrías que cambiar todos los métodos del conector de MySQL por los métodos de PostgreSQL. Con PDO bastaría con que cambiaras una sola línea de código, concretamente la de la creación del objeto PDO, y el resto de la aplicación seguiría funcionando. PDO además te permitirá usar consultas parametrizadas (como los Prepared Statements de java) o realizar transacciones. En fin, en el enlace de arriba tenéis todo el manual de PDO para estudiarlo si queréis. Ahora vamos con un simple ejemplo de Prepared Statement, para que veáis lo sencillo que es (dando por sentado para el ejemplo que ya hemos creado el objeto PDO, tal cual está explicado en el manual del enlace).


<?php
  $prepared_statement = $pdo->prepare("SELECT name FROM usuarios WHERE id = :id");/*preparamos la consulta*/
  $prepared_statement->bindParam(':id', $_GET['id'], PDO::PARAM_INT); /*Le pasamos el parámetro, asociado al parámetro de la consulta y definiendo su tipo (si no, por defecto lo trata como string)*/
  $prepared_statement->execute(); /*ejecutamos*/
  $prepared_statement = $statement->fetch(); /*recogemos los resultados, como un array. Se pueden utilizar parámetros para especificar otro tipo de respuesta, como por ejemplo PDO::FETCH_ASSOC para obtener un array asociativo*/
?>

Y con estos breves consejos lograrás que tu página sea más segura. El consejo: utiliza PDO, por ahorrarte comeduras de cabeza, por seguridad y por portabilidad de tu código.

Evitar ataques XSS con PHP Input Filter

Hoy una lección de seguridad en programación web: Evitar que a vuestra página le hagan un ataque por XSS (Cross Site Scripting). Si no sabéis lo que es el Cross Site Scripting… os lo explica mejor la wikipedia y así me centro en el tema (una vez le hice un ataque de estos a la página de un grupúsculo neonazi que redirigía a un vídeo del desfile del orgullo gay en Youtube)

Bueno, hay frameworks como Symphony o CakePHP que ya se encargar de implementar este tipo de seguridad, al igual que también lo hacen ciertos gestores de contenidos. Pero si se da el caso de que no estás usando ninguno, estás haciendo PHP «a pelo», lo mejor será que recurramos a la clase PHP Input Filter. Dicha clase permite filtrar código malicioso ingresado en los formularios para prevenir ataques XSS de manera sencilla, pero tiene la cualidad de no limpiar determinadas etiquetas o atributos si queremos.

Para utilizar esta clase, la descargaremos desde el enlace anterior e incluiremos class.inputfilter.php al inicio de nuestro códgi. Luego crearemos una instancia de la clase InputFilter, entonces podremos filtrar los datos con el método process:

    require_once("class.inputfilter.php");
    $filter = new InputFilter();
    $variable = $filter->process($_POST['variable']);

Esto nos permitiría limpiar de etiquetas la variable enviada por $_POST llamada variable. ¿Y si queremos limpiar todos los campos enviados por el método post? Pues basta con pasarle todo el array:

$_POST = $filter->process($_POST);

Y con eso tendríamos el array $_POST limpio de etiquetas y securizado.

Pero ¿y si queremos permitir ciertas etiquetas? Imaginemos que desde un input text metemos texto formateado, y nos interesa que sí puedan subir saltos de línea, texto en negrita o cursiva. Pues muy sencillo también, ya que basta con pasar en la creación del objeto InputFilter un array con las etiquetas que queremos conservar como parámetro:

    $filter = new InputFilter(array('em', 'i', 'br', 'b', 'strong'));
    $_POST = $filter->process($_POST);

Y esto se puede llevar más allá, porque también podemos decir que se permitan ciertos atributos de ciertas etiquetas. Por ejemplo, imaginemos que queremos permitir que inserten enlaces junto a su href y a su id, pero ningún atributo más:

    $filter = new InputFilter(array('a'), array('href', 'id'));
    $enlace = $filter->process($_POST['enlace']);

Tras el array de etiquetas pasamos otro array de atributos, simplemente.

Con estos sencillos consejos lograrás que tus formularios sean más seguros en PHP.

Instalar PHPUnit en WAMP

Inciso antes de empezar: Estoy buscando información y cursos de Symphony 2, porque me han recomendado aprender sobre este framework (ya que el destino parece empujarme a trabajar en PHP, si la vida te da limones haz mucha limonada para meter en tu currículum), si alguien puede aportar algo sobre el tema, por favor, en comentarios.

Y seguimos con el tema, instalar PHPUnit en un servidor Apache en Windows (WAMP).

Bueno, habría que empezar por decir ¿por qué usar PHPUnit? Pues para poder realizar tests automatizados sobre nuestro código PHP de manera eficiente. ¿Cómo lo instalamos en nuestro servidor? Bueno, todo es ir en cómodos pasos:

NOTA: En el artículo original lo hacíamos con PEAR, pero se le quitó el soporte hace años. Editamos para ver cómo hacerlo con PHAR:

  • Añade el directorio (en mi caso «c:\wamp\bin\php\php5.3.4») php a tu PATH
  • Descargamos el fichero PHAR desde https://phar.phpunit.de/phpunit-7.phar (Ojo, puede cambiar la versión con el paso del tiempo) y lo guardamos como C:\bin\phpunit.phar
  • Después ejecutamos el siguiente comando:
C:\Users\username> cd C:\bin
C:\bin> echo @php "%~dp0phpunit.phar" %* > phpunit.cmd
C:\bin> exit
  • Abre un nuevo prompt en línea de comados y comprueba que se ha instalado correctamente con el siguiente comando:
C:\Users\username> phpunit --version

Ok, ahora debería estar funcionando e instalado. Lo mejor es hacer un test de prueba para ver si funciona todo. En este caso una tontería básica de la que sabemos el resultado: comprobaremos que uno es igual a uno.

  class MyTest extends PHPUnit_Framework_TestCase
  {
    public function testUnoIgualUno()
    {
      $this->assertEquals(1, 1);
    }
  }

Ahora desde otro terminal basta ejecutar phpunit MyTest.php y este debería devolverte como respuesta el número de versión de PHPUnit, el tiempo que necesitó para la operación, la memoria consumida y un mensaje como este: OK (1 test, 1 assertion)

Todavía no he profundizado mucho con este software, pero cuando tenga tiempo en el trabajo espero poder ponerme con ello porque facilita mucho la vida el poder realizar tests de forma organizada.