margenn

On a recent project I needed a way to know if a user is using a mobile device to visit the site I was building and, if so, I wanted a little more info about the device, OS and browser used.

Before baking my own solution, I checked existing solutions such as the great WURFL, well known framework’s own solutions like CodeIgniter’s and other quite good scripts like php-mobile-detect, detectmobilebrowsers.mobi or mobiforge.com.

None did what I needed in a simple and lightweight way:

WURFL

  • Not straightforward to implement. 
  • Device database updates have to be done manually and periodically (at least twice a year) to stay up to date (Ughh). 
  • Heavy on the server (poor performance). 

CodeIgniter’s user agent class

Works well for mobile detection but it does not tell you either the OS or browser used.

php-mobile-detect

I like this one a lot but, unfortunately it doesn’t detect every browser/os/device I wanted, it has some bugs and I haven’t tested its regular expressions.

detectmobilebrowsers.mobi

Very good script but, unfortunately it doesn’t detect every browser/os/device I wanted. However, I used it as a reference.

mobiforge.com

This script is more suited towards detecting old mobile devices (feature phones).

So in the end I made my own script.

All I wanted was a helper function that would execute once per page load and set some global variables.

Those of you who prefer or need an OOP approach should modify the php-mobile-detect class to your needs.

Here’s my function:

<?php
/*
 * Lightweight detector of mobile devices, OSs & browsers
 * Copyright 2012  Túbal Martín  (email: tubalmartin@gmail.com)
 * License: GPL2
 */

if ( ! function_exists('mobile_detector') )
{
    // Global vars
    $is_mobile = false;
    $is_iphone = $is_ipad = $is_kindle = false;
    $is_ios = $is_android = $is_webos = $is_palmos = $is_windows = $is_symbian = $is_bbos = $is_bada = false;
    $is_opera_mobile = $is_webkit_mobile = $is_firefox_mobile = $is_ie_mobile = $is_netfront = $is_uc_browser = false;


    function mobile_detector($debug = false)
    {
        global $is_mobile;

        // Check user agent string
        $agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';

        if (empty($agent)) {
            return;
        }

        $mobile_devices = array(
            'is_iphone' => 'iphone',
            'is_ipad' => 'ipad',
            'is_kindle' => 'kindle'
        );
        
        $mobile_oss = array(
            'is_ios' => 'ip(hone|ad|od)',
            'is_android' => 'android',
            'is_webos' => '(web|hpw)os',
            'is_palmos' => 'palm(\s?os|source)',
            'is_windows' => 'windows (phone|ce)',
            'is_symbian' => 'symbian(\s?os|)|symbos',
            'is_bbos' => 'blackberry(.*?version\/\d+|\d+\/\d+)',
            'is_bada' => 'bada'
        );
        
        $mobile_browsers = array(
            'is_opera_mobile' => 'opera (mobi|mini)', // Opera Mobile or Mini
            'is_webkit_mobile' => '(android|nokia|webos|hpwos|blackberry).*?webkit|webkit.*?(mobile|kindle|bolt|skyfire|dolfin|iris)', // Webkit mobile
            'is_firefox_mobile' => 'fennec', // Firefox mobile
            'is_ie_mobile' => 'iemobile|windows ce', // IE mobile
            'is_netfront' => 'netfront|kindle|psp|blazer|jasmine', // Netfront
            'is_uc_browser' => 'ucweb' // UC browser
        );
        
        $groups = array($mobile_devices, $mobile_oss, $mobile_browsers);
        
        foreach ($groups as $group) {
            foreach ($group as $name => $regex) {
                if (preg_match('/'.$regex.'/i', $agent)) {
                    global $$name;
                    $is_mobile = $$name = true;
                    break;
                }
            }
        }
        
        // Fallbacks
        if ($is_mobile === false) {
            $regex = 'nokia|motorola|sony|ericsson|lge?(-|;|\/|\s)|htc|samsung|asus|mobile|phone|tablet|pocket|wap|wireless|up\.browser|up\.link|j2me|midp|cldc|kddi|mmp|obigo|novarra|teleca|openwave|uzardweb|pre\/|hiptop|avantgo|plucker|xiino|elaine|vodafone|sprint|o2';
            $accept = isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : '';

            if (false !== strpos($accept,'text/vnd.wap.wml')
                || false !== strpos($accept,'application/vnd.wap.xhtml+xml')
                || isset($_SERVER['HTTP_X_WAP_PROFILE'])
                || isset($_SERVER['HTTP_PROFILE'])
                || preg_match('/'.$regex.'/i', $agent)
            ) {
                $is_mobile = true;
            }
        }

        // DEBUGGER OUTPUT
        if ($debug === true) {
            echo '<strong>User Agent: '.$agent.'</strong><br>';
            foreach ($GLOBALS as $k => $v) {
                if (strpos($k, 'is_') !== false) {
                    echo '<span style="color:'.($v ? 'green':'red').';">$'.$k.'</span><br>';
                }
            }
        }

    }

    // execute inmmediatly
    mobile_detector();

}

I have created a Gist so you can download or fork the code.

This function should only be called once per page load and preferably as soon as possible.

This function adds the following variables to the global scope:

Is it a mobile?

  • $is_mobile

Any famous device?

  • $is_iphone (Apple iPhone)
  • $is_ipad (Apple iPad)
  • $is_kindle (Amazon Kindle)

What mobile OS?

  • $is_android (Android OS)
  • $is_bada (Bada OS)
  • $is_bbos (Blackberry OS)
  • $is_ios (Apple iOS)
  • $is_palmos (Palm OS)
  • $is_symbian (Symbian OS)
  • $is_webos (Hp WebOS)
  • $is_windows (Windows Phone OS and older)

What mobile browser?

  • $is_firefox_mobile (Mozilla Fennec)
  • $is_ie_mobile (IE)
  • $is_netfront (NetFront)
  • $is_opera_mobile (Opera Mobile or Mini)
  • $is_uc_browser (UC Browser)
  • $is_webkit_mobile (Webkit)

The initial value of these variables is false.

You can test/debug the plugin results (debug mode) here.

If you need to debug the function, call it passing true as a parameter and the function will output the results.

If you use Wordpress to build websites and you’re reading this post let me tell you I already developed a plugin for Wordpress based on this function.

Share your thoughts!!

Tags: mobile detector device php browser

PHPStorm

Todos los que utiliceis a diario IDEs basados en Java para programar sabreis que no son especialmente rápidos, especialmente con archivos grandes o cuando tenemos muchos archivos abiertos.

Sin embargo hay algunas recomendaciones y trucos para que nuestro IDE basado en Java funcione más rápida y agilmente.

En margenn, después de haber probado opciones como Netbeans, Eclipse PDT o Aptana, elegimos utilizar PHPStorm, de JetBrains, y la verdad es que para nosotros no hay vuelta atrás, sin duda es el mejor. 

A continuación expongo mi guía de optimización de rendimiento de PHPStorm:

1. Deshabilita todos los plugins que no utilices en tu día a día

 En mi caso, programo con PHP, JS, HTML y CSS por lo que todo lo demás sobra para mi. Estos son los plugins que he deshabilitado (y no son pocos):

  • ASP
  • CVS Integration (Usamos Subversion, Mercurial o Git)
  • Database Support (Desarrollo en remoto - FTP)
  • Java Server Pages Integration
  • LESS support
  • Perforce Integration
  • Phing Support
  • QuirksMode
  • Refactor-X
  • RELAX-NG Support
  • SASS support
  • SpellChecker
  • SQL support
  • W3C Validators
  • XPathView + XSLT Support
  • YAML

2. Desactiva las “inspections” que no consideres necesarias

Especialmente en el caso de Javascript desactivar las inspecciones oportunas agiliza mucho el editor ya que cada vez que editas tu archivo, tiene que escanear todo de nuevo para entender cómo se relaciona el nuevo código que escribes con el existente.

Estas son las que he deshabilitado para Javascript por diferentes motivos:

  • Unnecessary Semicolon
  • Unresolved Function
  • Unresolved Variable
  • Mismatched Collection Query Update Inspection
  • Potentially Invalid Constructor Usages
  • Suspicious Name Combination Inspection
  • Implicitly Internal Declaration
  • Untyped Declaration

Ahora cuando edito un archivo el editor no se ralentiza y la CPU no se pone al 100%.

Deshabilitadas para HTML:

  • Form input without an associated label
  • Image size mismatch
  • Malformed content of <script> tag

Como nota adicional también he deshabilitado todas las inspecciones de PHPDoc, Probably bug y XML.

3. Desactiva las “intentions” que no consideres necesarias

Las intenciones también tienen su impacto en el rendimiento aunque en un grado menor. Desactiva todas las que no consideres útiles teniendo en cuenta tu estilo de código (formato).

Si observais, de nuevo, el lenguaje que más intentions tiene es Javascript.

4. Amplia la memoria disponible para la máquina virtual

En margenn utilizamos exclusivamente MAC OS X (Windows en virtual para testear unicamente):

  • Buscad la aplicación PHPStorm, click secundario y “Mostrar contenidos del paquete”.
  • En el directorio “Contents” encontrareis el archivo “info.plist”, abridlo con TextEdit o XCode.
  • Cambiad la configuración del parámetro VMOptions.i386 a la siguiente:
    -Xms512m -Xmx1024m -XX:MaxPermSize=512m -XX:ReservedCodeCacheSize=128m
  • Cambiad la configuración del parámetro VMOptions.x86_64 a la siguiente:
    -Xms512m -Xmx1024m -XX:MaxPermSize=512m -XX:ReservedCodeCacheSize=128m -XX:+UseCompressedOops

En Windows y Linux la ruta y el nombre del archivo de configuración es diferente pero los parámetros son los mismos.

A continuación explico qué significa cada parámetro:

  • -Xms512m: Tamaño de inicio de la memoria de tipo heap de la máquina virtual de Java a 512Mb. El valor por defecto que establece PHPStorm es de 128Mb. Si se aumenta este valor, se elimina el tiempo que se tardaría en aumentar el tamaño en memoria de la máquina virtual si se llegara el caso de que se necesitara más memoria, por lo que aumentaría el rendimiento en los casos que la aplicación haga uso intensivo de la memoria.
  • -Xmx1024m: Tamaño máximo de la memoria de tipo heap de la máquina virtual de Java a 1024Mb (1 Giga).  El valor por defecto que establece PHPStorm son 768Mb. Si la aplicación supera el tamaño máximo de memoria que marca este parámetro, se lanza la excepción java.lang.OutOfMemoryError.  No conviene asignar a este parámetro el máximo de la memoria de nuestro ordenador porque si ya no queda memoria RAM disponible (por la que usa el sistema operativo u otras aplicaciones) se pueden producir escrituras en memoria asignada a otros programas y provocar un auténtico lío.
  • -XX:MaxPermSize=512m: Tamaño máximo de la memoria de tipo PermGen (non-heap) a 512Mb. El valor por defecto de PHPStorm es 250Mb (i386) o 350Mb (x86_64). Si la aplicación supera el tamaño máximo de memoria para este tipo que marca este parámetro, se lanza la excepción java.lang.OutOfMemoryError: PermGen space. El valor necesario para este parámetro siempre suele ser menor que el de la memoria de tipo heap. Si se quiere especificar un valor distinto de 128Mb bastaría con sustituir el valor 128 del parámetro con el que se desee, siempre que sean múltiplos de 2 (64, 128, 256, 512, 768, 1024, 2048…)
  • -XX:ReservedCodeCacheSize=128m: Tamaño de la memoria reservada para caché de código. El tamaño asignado por defecto de PHPStorm es 64m.

Podeis echar un vistazo a la referencia completa de las opciones de máquina virtual de Java Hotspot.

Después de seguir estos pasos, os aseguro que notareis que PHPStorm funciona más ligero y no se ralentizará apenas al editar archivos grandes.

Hasta el próximo artículo!!

Tags: phpstorm webstorm jetbrains performance jidea java ide php

Función “slice” de Javascript para PHP

Posted on December 15, 2010 by

Recientemente me embarqué en un proyecto personal (que daré a conocer dentro de poco) que implicaba reescribir en PHP código escrito previamente en Javascript.

Al poco de empezar la tarea pude observar que el script dependía de la función slice de Javascript. Miré en la documentación de PHP y ninguna función hacía lo mismo. Consulté las funciones para los arrays…y tampoco ninguna me servía. Finalmente busqué en Google y sólo encontré gente preguntando en foros si existía dicha función en PHP o algún port pero ninguna solución.

Así que me puse manos a la obra, al cabo de un rato escribí los tests y ¡Tachán! os presento la implementación en PHP de la función slice de Javascript para strings exclusivamente ya que el proyecto no requería tratar con arrays.

El comportamiento es idéntico a su “counterpart” en Javascript, de modo que si alguien necesita cambiar qué devuelve la función en caso de una excepción (“” string vacío) tendrá que modificar el código y crear sus tests.

/** 
* PHP port of Javascript's "slice" function
* Author: Tubal Martin http://margenn.com
* Tests: http://margenn.com/tubal/str_slice/
*
* @param string $str
* @param int    $start index
* @param int    $end index (optional)
*/
function str_slice($str, $start, $end = FALSE)
{
    if ($start < 0 || $end <= 0) {
        
        if ($end === FALSE) {
            $slice = substr($str, $start);
            return ($slice === FALSE) ? '' : $slice;
        }
        
        $max = strlen($str);
        
        if ($start < 0) {
            if (($start = $max + $start) < 0) {
                return '';
            }
        }
    
        if ($end < 0) {
            if (($end = $max + $end) < 0) {
                return '';
            }
        }
        
        if ($end <= $start) {
            return '';
        }
    }   
    
    $slice = substr($str, $start, $end - $start);
    return ($slice === FALSE) ? '' : $slice;
}

Podéis ejecutar los tests visitando la URL indicada en el comentario situado en la cabecera de la función.

La función está optimizada para un rendimiento óptimo.

Espero que os sirva de utilidad ;)

Tags: slice javascript php port function