Esta semana me he encontrado con un problema que me dió muchos quebraderos de cabeza pero que por suerte llegué a una solución :)
El problema surgía al manipular cadenas de caracteres en php codificadas en UTF-8 con funciones del tipo ucwords, strtoupper, etc...
Al pasarlas por éstas funciones, algunos caracteres se corrompían, y se guardaban erroneamente.

Indagando un poco encontré la explicación a todos éstos problemas.
Las funciones de manipulación de strings en PHP están programadas para trabajar con caracteres de 1 byte de longitud. De ésta manera, funciones como contar el número de caracteres que incluye una cadena (strlen) simplemente calcula el tamaño ocupado en memoria y divide por 1 byte. Todo esto se viene abajo al utilizar cadenas de caracteres codificadas con juegos de caracteres como UTF-8, en los que algunos caracteres pueden ocupar 2 o 3 bytes. Por lo tanto, muchas de las funciones típicas de trabajo con cadenas son peligrosas de usar si nuestros textos están codificados en UTF-8 y son susceptibles de contener éste tipo de caracteres de mayor tamaño, que al leer byte a byte, encontrará erroneamente los datos binarios correspondiente a dos (o tres) caracteres, en vez de uno.

Existe una lista de funciones "inseguras" al trabajar con cadenas con encodings que guarden caracteres de más de 1 byte. Son las siguientes :
· ord
· str_ireplace
· str_pad
· str_split
· strcasecmp
· strcspn
· stristr
· strlen
· strpos
· strrpos
· strrev
· strspn
· strtolower
· strtoupper
· substr_replace
· [l|r]trim (unicamente cuando se usa el segundo parametro)
· ucfirst
· ucwords

Si lo que necesitas es convertir toda la cadena a minúsculas (strtolower), a mayúsculas (strtoupper) o la primera letra de cada palabra a mayúsculas y el resto en minúsculas (ucwords), existe una alternativa que nos evitará tener que cambiar el encoding de nuestra cadena:

PHP:
  1. $string = mb_convert_case($string, TIPO_ACCION, "UTF-8");

Donde TIPO_ACCION  puede tomar los  siguientes valores para obtener el resultado equivalente:  "MB_CASE_LOWER" (strtolower), "MB_CASE_UPPER" (strtoupper)  , "MB_CASE_TITLE" (ucwords)

mb_convert_case es una función que permite la manipulación de los caracteres de una cadena de tipo multibyte.

Tienes muchas más funciones para cadenas multibyte.

Un saludo !

Hace tiempo que llevo pensando en escribir un pequeño artículo sobre los Aceleradores de php. Y ésta semana, aprovechando que he instalado uno en una nueva máquina para hostear éste y algunos otros blogs más, me he animado a ello.

Lo primero de todo, es entender qué es un Acelerador de php.

Como su nombre bien indica, un acelerador de php es "algo" (ahora veremos qué..), que acelera nuestro código escrito en php. Si reflexionamos un poco llegaremos rápidamente a unas sencillas preguntas ¿Cómo puede hacer "algo" que nuestro código se ejecute más rápidamente? ¿Qué hace ésto que parece tan mágico? ¿Retoca nuestro codigo en tiempo real para optimizarlo? ¿Mágicamente hace que nuestro código consuma menos recursos?

Por supuesto que no...

La explicación es mucho más sencilla de lo que parece.
Como bien sabemos, php no es un lenguaje compilado, sino un lenguaje interpretado (script). Cuando nuestro servidor Apache, o un comando CLI llama al intérprete de php para que ejecute un determinado archivo, nuestro intérprete genera en tiempo real los opcodes necesarios para su ejecución. Cada vez que se llama a ejecutar uno de nuestros archivos, nuestro intérprete vuelve a compilar en tiempo real nuestros scripts, incluso cuando llamamos a un script que se haya ejecutado anteriormente. Es aquí donde entran en juego los Aceleradores de php. Su función es simplemente la de recubrir todo el proceso de compilación, guardar los estados compilados de nuestros scripts en memoria, y facilitar una interfaz automatizada que evalúa si es necesario compilar un script o si disponemos de una copia ya compilada en memoria. Osea, que un Acelerador de php, no es más que un "cacheador" de opcodes.
Imaginaos cuánto tiempo y recursos ahorramos al evitar el compilado de toda nuestra home, o de nuestras páginas más visitadas.

La mejora de rendimiento es asombrosa, incluso podremos llegar a ver mejorado el rendimiento de carga de algunas páginas por 3.

Ésta es una de la mejoras de optimización más sencillas que podemos llevar a cabo en nuestro servidor web, y sin duda una de las mejores en relación dificultad de aplicación / mejoras claramente visibles obtenidas.

Existen muchos Aceleradores de php , como APC, eAccelerator, xCache , PHPA, ...
Para gustos, colores. Seguro que encontrarás rápidamente un montón de comparativas, articulos a favor, en contra, y demás ...

Yo voy a explicar cómo instalar xCache (en una máquina Ubuntu). Por su extrema sencillez de instalación y excelente rendimiento.

Continue Reading →

Si alguna vez has tenido que lidiar con código ajeno, o has integrado algún paquete de clases de terceras partes, o trabajado con algún API que te devuelva los datos en forma de objeto; probablemente hayas tenido la necesidad de conocer los métodos disponibles para interactuar con un objeto. Si a eso le sumamos una documentación pobre de las clases que vamos a usar, estamos ante un buen dolor de cabeza y unas cuantas horas de tiempo perdido estudiando el código para poder trabajar.

Éste fin de semana tuve ese problema, y buscando un poco, encontré dos métodos de php que se han convertido en imprescindibles para mí.

La primera función nos permite conocer el nombre de la clase de un objeto

PHP:
  1. string get_class(OBJECT);

Que pasándole como parámetro el objeto, nos devolverá un string con el nombre de la clase.

La segunda es si cabe aún más útil, ya que para una clase, nos devuelve un array con la lista de metodos públicos disponibles.

PHP:
  1. array get_class_methods(string class_name);

Imaginad ahora qué bonito lo que obtenemos, si combinamos éstos dos métodos:

PHP:
  1. $objetoMisterioso;
  2. $metodosDisponibles=get_class_methods(get_class($objetoMisterioso));
  3. print_r($metodosDisponibles);

Que nos devolverá algo parecido a:

CODE:
  1. Array(
  2. [0]=>getMethod
  3. [1]=>setMethod
  4. [2]=>delElement
  5. );

Realmente útil y nos ahorrará mucho tiempo cuando trabajemos con objetos que no conocemos del todo bien.

Si hemos elegido Amazon S3 como sistema de almacenamiento virtual , quizás nos interese tener almacenados archivos que se puedan descargar libremente por cualquier usuario, y que además puedan ser linkados desde cualquier sitio. Pero en la mayoría de los casos tendremos almacenados nuestros archivos y no nos gustará que nadie nos enlace directamente y tener que soportar los gastos de transferencia y almacenamiento por él.

Voy a explicar cómo, con un sencillo script en php conseguiremos que nuestros archivos de S3 estén totalmente protegidos y que sólo puedan ser accesibles desde nuestro dominio.

Lo primero de todo, es conocer dos características que Amazon nos prové con sus APIs:
- Los ACL (Access Control List) : que nos permite definir los permisos de lectura-escritura-ejecución de nuestros bucket y objetos.
- Las URLs Firmadas : que nos permite acceder temporalmente a nuestros archivos protegidos, a través de una url que incluye una firma y un tiempo de expiración de los permisos. Éstas URLs las podremos generar con nuestro script, y llevarán como parámetro nuestra firma generada en tiempo de ejecución, que lleva implicita el tiempo de caducidad de la url.

La primera medida es aplicar permisos exclusivo de lectura para nuestro usuario (usando los ACLs) a todos los archivos que queramos proteger, para que sea imprescindible una validación al acceder a ellos.
Para generar nuestro script , necesitaremos echar mano de la clase Crypt/HMAC para construir el hash en sha1 que Amazon S3 requiere.
Lo primero será crear nuestra función para  generar nuestra firma:

PHP:
  1. require_once 'Crypt/HMAC.php';
  2. function hex2b64($str) {
  3. $raw = '';
  4. for ($i=0; $i <strlen($str); $i+=2) {
  5. $raw .= chr(hexdec(substr($str, $i, 2)));
  6. }
  7. return base64_encode($raw);
  8. }
  9. function makeSig($str) {
  10. $secretKey="Llave secreta que nos proporciona Amazon para nuestra cuenta";
  11. $hasher =& new Crypt_HMAC($secretKey, "sha1");
  12. $signature = hex2b64($hasher->hash($str));
  13. return($signature);
  14. }

Lo siguiente, será crearnos una función para construir nuestra URL firmada :

PHP:
  1. function getSignedURL($bucket, $key, $expires=120){
  2. $accessKeyId="accessKeyId que nos proporciona Amazon con nuestra cuenta";
  3. $expires = time() + $expires;
  4. $resource = $bucket."/".urlencode($key);
  5. $stringToSign = "GETnnn$expiresn/$resource";
  6. $signature = urlencode(makeSig($stringToSign));
  7. $signedUrl="http://s3.amazonaws.com/$resource?AWSAccessKeyId=$accessKeyId&Expires=$expires&Signature=$signature";
  8. return $signedUrl;
  9. }

Ésta función nos devolverá la url firmada, que estará disponible durante el tiempo (en segundos) que hayamos establecido en el parametro $expires (120seg por defecto)

Ahora ya sólamente nos falta un script que nos lance al contenido, pero que nos proteja de links externos a nuestro dominio.
Para ésto simplemente comprobaremos si existe el campo HTTP_REFERER en nuestra variable $_SERVER , que nos indicará que estamos accediendo a ésta url desde otra url de referencia , y que nuestro dominio (extraido desde $_SERVER['HTTP_HOST']) esté contenido en él.

PHP:
  1. function get_amazon_url($amazon_bucket,$amazon_object){
  2. if(isset($_SERVER['HTTP_REFERER']) && strstr($_SERVER['HTTP_REFERER'],$_SERVER['HTTP_HOST']))   {
  3. $url=getSignedURL($amazon_bucket,$amazon_object,60);
  4. header('Location:'.$url);
  5. }else{
  6. echo 'Éste contenido ha sido enlazado ilegalmente';
  7. }
  8. }

Al llamar a ésta función, seremos automáticamente redirigidos al contenido en amazon siempre que el link provenga desde nuestro mismo dominio. Por supuesto, si quisieramos permitir el acceso desde una determinada lista de dominios, simplemente tendríamos que modificar un poco la condición if , para que compruebe que cada uno de los dominios están incluido en el HTTP_REFERER.

Espero que sea útil.
Un Saludo !!