Archivo de la categoría: PHP

PHP

Detectar el navegador y toda su información desde PHP o Javascript

Hola, les hago una intro bien corta y pasamos al código.

Browscap es un proyecto que recoge la información de todos los navegadores web en una base de datos que distribuye de forma gratuita y en varios formatos.

PHP tiene soporte nativo para hacer uso de esta DB, pero debido a un par de limitaciones que PHP aun no ha solventado en su API, en GitHub un usuario creó una soluciona basada también en la DB de Browscap pero mucho mas robusta y fácil de usar.

Bien, llegados a este punto sabemos que lo que necesitamos usar es Browscap junto a la librería que se creó para explotarla.

Para mas información, toda la documentación completa de php-browscap la pueden encontrar aquí. Yo me centro en una implementación mucho mas simple porque realmente creo que de los 40 datos que entrega sobre el navegados, con apenas 7 tenemos el 99% de los casos cubiertos.

Acá tienen un ejemplo funcionando para que vean lo rápido y certero que es a la hora de conseguir la info del browser (pruébenlo con mas de uno)

Ok, pongamos algo de código que sino todo esto es muy aburrido:
Vamos a aprovechar esta librería bajo dos situaciones. en PHP y en JS

En PHP

Descarguen la librería php-browscap via Composer o a mano. Pueden saber mas sobre como descargarla aquí
Ahora que la tenemos creamos un archivo index.php para usarla.

<?php
require 'vendor/autoload.php';
use phpbrowscap\Browscap;
$bc = new Browscap('cache');
/** @var stdClass $current_browser */
$current_browser = $bc->getBrowser();
?>

Fácil, no?. Ok ya tenemos un stdClass en $current_browser con toda la información disponible del browser que llame a index.php

Aplicaciones?

Puedes por ejemplo tomar decisiones del tipo

<?php
$curr_br = $current_browser->Browser;
$CoolBrowsers = $curr_br == 'Chrome' || $curr_br == 'Firefox';
if ($CoolBrowsers) {
  add_asset('css', 'just-for-cool-browsers.css');
  $output = renderAdvancedLayout();
}
else {
  add_asset('css', 'just-for-bad-bad-browsers.css');
  $output = renderBasicLayout();
}
?>

También puedes detectar si están accediendo desde un móvil o una tablet, incluso saber si es Android, IOS, la version del SO, etc. Con toda esta información a resumidas cuentas puedes entregar un contenido muy preciso.

Beneficios

Estas entregando contenidos a medida, ergo necesitas entregar menos HTML, CSS, Js y las imágenes mas adecuadas al navegador. Esto finalmente se traduce en menos procesamiento del lado del servidor, lo que se entrega pesa menos KB y el navegador tarda menos en leer y renderizar todos los recursos (en especial JS).

En JS

Por ultimo me gustaría compartir un tip para poder aprovechar la detección de browsers en JS:
A ver, con js podemos detectar el browser usando un poco de expresiones regulares, pero llegar al detalle y la precisión a la que llega browscap es virtualmente imposible. Dicho esto, comentar que se puede entregar toda la información del browser detectado desde php simplemente haciendo esto:

<script type="application/javascript">
  var browser = <?php= json_encode($current_browser = $bc->getBrowser()); ?>;
  alert(browser.Browser);
  alert(browser.Version);
</script>

Hasta la próxima!

PHP

Closures: mod_fcgid: stderr: PHP Fatal error: Using $this when not in object context in

Las closures son algo genial, y grandes frameworks como symfony las aprovechan para hacer magia prácticamente, pero son relativamente nuevas en el mundo de PHP, y hay veces que dan problemas.

Por ejemplo, el titulo de este post se genera cuando tenes un closure dentro e una clase (está dentro de un método de la clase). cuando querés acceder a $this desde dentro del closure te va a lanzar el error. Un ejemplo:

<?php
protected function buscarProcesa(&$text) {
  preg_replace_callback("/algo/s", function ($matches) {
    $this->procesarMatches($matches);
  }, $text);
}

En este ejemplo estamos queriendo usar el método $this->procesarMatches() desde dentro del closure, y va a fallar por dos motivos el primero es que los closures no pueden acceder a las variables que se encuentren fuera de si mismas, así que $this al estar fuera, es inaccesible. Pero tiene solución, usando use ($this) se puede pasar a $this dentro del closure:

<?php
protected function buscarProcesa(&$text) {
  preg_replace_callback("/algo/s", function ($matches) use ($this) {
    $this->procesarMatches($matches);
  }, $text);
}

Ahora bien, usando use ($algunavariable, $otravariable) las podemos meter dentro del closure, y mientras que no sea $this, todo va a funcionar bien, pero el caso es que necesitamos $this, y en el momento que hagamos function ($matches) use ($this) vamos a recibir el error que les comento.

¿Por qué?, bueno es una limitación que ha quedado de cuando implementaron closures en PHP. Algún día lo solucionaran, pero mientras tanto la solución es esta:

<?php
protected function buscarProcesa(&$text) {
  $self = $this;
  preg_replace_callback("/algo/s", function ($matches) use ($self){
    $self->procesarMatches($matches);
  }, $text);
}

Si miran bien lo que hicimos es asignar $this a $self y enviamos $self al closure… y así funciona todo sin problemas.

Una nota final: Cualquier método/variable que llames dentro del closure tiene que ser necesariamente publica, sino falla.

Chau!

PHP

Xampp icuuc46.dll intl

Si habilitan el modulo intl en Xampp puede que les de error al no encontrar la librería “icuuc46.dll“.  La solución en este caso nos la da Cyn Wong:

  1. Ir a [xampp_path]/php/ y copiar estos tres archivos:
    1. icudt46.dll
    2. icuin46.dll
    3. icuuc46.dll
  2. Pegalos en [xampp_path]/apache/bin y reinicia, y vas a ver como ya no hay problemas.

Chau!

PHP

Como forzar la descarga de un archivo con PHP

Cada tanto necesitamos cosas así :)

<?php
/**
 * Downloader
 *
 * @param $archivo
 *  path al archivo
 * @param $downloadfilename
 *  (null|string) el nombre que queres usar para el archivo que se va a descargar.
 *  (si no lo especificas usa el nombre actual del archivo)
 *
 * @return file stream
 */
function download_file($archivo, $downloadfilename = null) {
    if (file_exists($archivo)) {
        $downloadfilename = $downloadfilename !== null ? $downloadfilename : basename($archivo);
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename=' . $downloadfilename);
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Pragma: public');
        header('Content-Length: ' . filesize($archivo));
        ob_clean();
        flush();
        readfile($archivo);
        exit;
    }
}

Uso:
Si queres que el archivo se descargue con un nombre distinto al original.

<?php
download_file("archivos/archivoReal.zip", "archivoEnmascarado.zip");

Si queres que el archivo se descargue con el nombre original.

<?php
download_file("archivos/archivoReal.zip");
Druplicon

Drupal 6: db_last_insert_id() no funciona correctamente [Solución]

Es curioso como un error de este tipo se les llega a escapar y entra en producción.

¿Por qué no funciona? porque a algun iluminado se le ocurrio que solo lanzando la query ‘SELECT LAST_INSERT_ID()’ sin especificar la tabla sobre la que se quiere obtener el ultimo registro insertado Drupal iba automagicamente a obtenerlo…. ERROR. Ah y por cierto incluso la query ‘SELECT LAST_INSERT_ID()’ bien armada también suele fallar.

/**
 * Returns the last insert id.
 *
 * @param $table
 *   The name of the table you inserted into.
 * @param $field
 *   The name of the autoincrement field.
 */
function db_last_insert_id($table, $field){
    return db_result(db_query('SELECT LAST_INSERT_ID()'));
}

La función que soluciona este problema es esta:

/**
 * Version modificada del analogo de drupal que soluciona un problema comentado en foros.
 *
 * @param string $table tabla sobre la que se quiere obtener el ultimo id insertado
 * @param string $field cual es la columna que tiene el id
 * @return int
 */
function custom_db_last_insert_id($table, $field) {
    $result = db_query("SELECT $field FROM {$table} ORDER BY $field DESC LIMIT 1");
    $row = db_fetch_object($result);
    return $row->$field;
}

Solo resta agregar que uso esta solución hace mas de un año y funciona bien.

Herramientas

Reemplazar la ñ, acentos, espacios y caracteres especiales con PHP [Actualizada]

<?php
/**
 * Reemplaza todos los acentos por sus equivalentes sin ellos
 *
 * @param $string
 *  string la cadena a sanear
 *
 * @return $string
 *  string saneada
 */
function sanear_string($string)
{
    $string = trim($string);
    $string = str_replace(
        array('á', 'à', 'ä', 'â', 'ª', 'Á', 'À', 'Â', 'Ä'),
        array('a', 'a', 'a', 'a', 'a', 'A', 'A', 'A', 'A'),
        $string
    );
    $string = str_replace(
        array('é', 'è', 'ë', 'ê', 'É', 'È', 'Ê', 'Ë'),
        array('e', 'e', 'e', 'e', 'E', 'E', 'E', 'E'),
        $string
    );
    $string = str_replace(
        array('í', 'ì', 'ï', 'î', 'Í', 'Ì', 'Ï', 'Î'),
        array('i', 'i', 'i', 'i', 'I', 'I', 'I', 'I'),
        $string
    );
    $string = str_replace(
        array('ó', 'ò', 'ö', 'ô', 'Ó', 'Ò', 'Ö', 'Ô'),
        array('o', 'o', 'o', 'o', 'O', 'O', 'O', 'O'),
        $string
    );
    $string = str_replace(
        array('ú', 'ù', 'ü', 'û', 'Ú', 'Ù', 'Û', 'Ü'),
        array('u', 'u', 'u', 'u', 'U', 'U', 'U', 'U'),
        $string
    );
    $string = str_replace(
        array('ñ', 'Ñ', 'ç', 'Ç'),
        array('n', 'N', 'c', 'C',),
        $string
    );
    //Esta parte se encarga de eliminar cualquier caracter extraño
    $string = str_replace(
        array("\\", "¨", "º", "-", "~",
             "#", "@", "|", "!", "\"",
             "·", "$", "%", "&", "/",
             "(", ")", "?", "'", "¡",
             "¿", "[", "^", "`", "]",
             "+", "}", "{", "¨", "´",
             ">", "< ", ";", ",", ":",
             ".", " "),
        '',
        $string
    );
    return $string;
}

Ejemplo de uso:

<?php
echo sanear_string("áàäâªÁÀÂÄdoéèëêÉÈÊËreíìïîÍÌÏÎmióòöôÓÒÖÔfaúùüûÚÙÛÜsolñÑçÇlasi\\¨º-~#@|!\,·$%&/()?¡¿[^`]+}{¨´>< ;,:. ");

Lo anterior imprime:

"aaaaaAAAAdoeeeeEEEEreiiiiIIIImiooooOOOOfauuuuUUUUsolnNcClasi"
debug

Instalar Xdebug en Ubuntu (cualquier versión)

Esto es mas bien un resumen de las instrucciones que nos deja el mismo creador de Xdebug en su pagina (http://xdebug.org/docs/install) mas un par de cosas que da por entendido que debemos saber :)

1) Lo primero es bajarse el código fuente de la ultima versión de Xdebug (la 2.1 es la ultima al día de la fecha)

http://xdebug.org/files/xdebug-2.1.0.tgz

2) Descompriman el archivo en una carpeta cualquiera

De acá en mas solo nos queda compilar configurar y probar:

3) Lancen el siguiente comando para asegurarse que tienen instaladas las librerías de desarrollo de php5 que son necesarias para poder compilar Xdebug:

apt-get install php5-dev

4) Entren en la carpeta por medio de la consola:

cd /ruta/a/la/carpeta/donde/descomprimiste/los/archivos/fuente/de/xdebug/xdebug-2.1.0/

5) Lanzar el comando phpize

phpize

6) Lanzar:

./configure --enable-xdebug
make
sudo make install

Al final del infierno de outputs de salen en la consola van a apreciar que se dibuja un recuadro como el que se ve en la siguiente imagen:

Bueno, justo encima de ese recuadro pueden ver que dice algo como “Installing shared extensions: /usr/lib/php5/20090626+lfs/“. Copien esa ruta.

Ya esta instalado Xdebug. Configuremos lo XD.

7) Editemos php.ini

sudo gedit /etc/php5/apache2/php.ini

Y en la ultima linea del archivo pequen lo siguiente:

zend_extension="/usr/lib/php5/20090626+lfs/xdebug.so"
xdebug.remote_enable=1
xdebug.remote_handler=dbgp
xdebug.remote_mode=req
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9000

NOTA: en la linea que dice “zend_extension” reemplacen la ruta del ejemplo por la que copiaron en el paso 6

8) Listo! todo en teoría funciona. para comprobarlo abran el IDE al que le tengan mas bronca y configurenlo!

Druplicon

Drupal 6: Módulos y templates con acentos, “¿” y “ñ” mal codificados [solución]

Un problema con el que me suelo dar de tanto en tanto son las codificaciones de caracteres. En especial la de acentos, la ñ y el signo de pregunta de apertura (¿).

El problema mas común que me encontré en mis primeros tiempos como desarrollador de módulos de Drupal fue que de tanto en tanto abría un modulo, y este tenia los caracteres especiales hechos pedazos, mal codificados.

Probé con todo tipo de yerbas raras. Desde usar funciones de todo tipo de encoding de PHP hasta cambiar la collation de la DB…

Pero la solución estaba tan al alcance de la mano que me llegue a sentir un autentico boludo cuando me cayeron las fichas: ¡La codificación de los archivos!. Si señor/a, no me había fijado en la codificación de los archivos (*.module, *.php, *.install, *.inc, etc).

Lo único que hay que hacer para que todo funcione como la seda son unas pocas cosas:

Que el archivo esté codificado en UTF-8. Podés saber que codificación tiene abriéndolo con Notepad++ y yendo al menú “Formato” como se ve en la imagen:

Y si ven que no está seteado en “UTF-8 sin BOM” o “UTF-8”, pueden convertirlo a cualquiera de estos formatos en el mismo menú:
(Para nosotros usar “UTF-8 sin BOM” o “UTF-8” nos es indistinto :) )

En el caso de los IDE deben saber que Netbeans y Zend Studio de serie vienen configurados para crear y tratar a los archivos bajo UTF-8, mientras que Eclipse NO.

Entonces, si usas Eclipse solo tenes que tocar un detalle en la configuración general para despreocuparte de este asunto:
Vayan al menú “Window -> preferences” y en el menú de preferencias deben ir a “General -> Workspace”. Cambien la codificación que haya puesta por la de UTF-8 como se ve en la imagen.


Otra cosa mas. Si trabajan con GIT sepan que tampoco codifica los archivos en UTF-8, y se soluciona yendo a sus opciones y cambiándolo.

Con lo anterior podes olvidarte de tener problemas de codificación De hecho podes dejar de usar entidades HTML y demases, y solo con usar t(“áéíóúñ”) que no vas a tener mas problemas.

Lo ultimo que quiero comentar es que si trabajan con mas gente sobre estos módulos, asegurate que todos tengan bien configurado su IDE o editor para evitar que los codifique mal (con solo abrir un archivo UTF-8 con un editor que no esté en ese formato es muy posible que al editarle una letra y guardarlo lo haga pedazos!).

Druplicon

Drupal 6: Mostrar los errores de PHP en lugar de la pantalla blanca (o blank page)

Suele hacer falta poder ver los errores de PHP en Drupal de vez en cuando.

Abrí el archivo index.php de Drupal y poné lo siguiente justo por encima de “require_once ‘./includes/bootstrap.inc';“:

<?php
//Reportar errores PHP
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
//Fin reportar errores PHP

Notas rápidas de strpos() (PHP)

Strpos() devuelve la posición del primer caracter de la palabra que estamos buscando.

Ejemplos:

<?php
//en la posición 0
if ($posicion = strpos("hola mundo", "hola") !== FALSE) {
    echo "Palabra encontrada en la posición $posicion";
}
?>
<?php
//en la posición 9
if ($posicion = strpos("he dicho hola mundo!", "hola") !== FALSE) {
    echo "Palabra encontrada en la posición $posicion";
}
?>

Nunca hay que olvidar de hacer la comparación del resultado con comparaciones estrictas (“===”, “!==”) para evitar falsos positivos o negativos.