Mostrando entradas con la etiqueta arquitectura software. Mostrar todas las entradas
Mostrando entradas con la etiqueta arquitectura software. Mostrar todas las entradas

8 de julio de 2011

[PHP] Verificación de entradas en arrays multidimensionales (2/2)

conjuntoEntradas() {
  [PHP] Verificación de entradas en arrays multidimensionales (1/2)
  [PHP] Verificación de entradas en arrays multidimensionales (2/2)
}


Llegado al punto que al no ser las dos opciones comentadas en el primer post de esta serie de mi gusto por el motivo que ya expuse, además estar desfavorecidas frente a otras, por su menor rendimiento en velocidad de ejecución, podría haber optado por tirar por el derecho y decidir utilizar la función isset(), es decir la que en el comentario de la documentación de función is_null(), se dejaba claro que era la más óptima entre estas tres opciones.

No obstante, durante mi búsqueda de como hacer la verificación de la existencia de claves dentro de los arrays multidimensionales, me tope con otra función, que por nombre y definición (Verifica si el índice o clave dada existe en el array) debería ser la más apropiada para realizar dicho menester; estoy hablando de la función array_key_exists().

No obstante, en los comentarios de la documentación de array_key_exists(), alguien y había comentado la diferencia de velocidad de ejecución con isset(), siendo de nuevo, favorable para ésta última.

Aún habiendo este comentario, yo quería hacer mis pruebas, ya que al final del comentario, se menciona que la diferencia de rendimiento no es tan grande en un sistema operativo Mac o Linux, como lo es un MS Windows; además la prueba del comentario se realiza sobre un array unidimensional y no multidimensional, que son con los que yo me iría encontrando en mis implementaciones y por otra parte, me entró la vena friki por realizar la prueba sobre un profiler, y como ya había utilizado en una vez anterior, el elegido fue Xdebug, utilizando, con el mismo motivo, el visor KCachegrind; así que manos, la obra.

Primero la implementación de las funciones que me permitirán discernir entre las dos distintas formas de verificar la presencia de claves en arrays multidimensionales con la función array_key_exists().

function aa_verificacion_a_una_instruccion_fallo_2d($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (@array_key_exists('clave2.5', $var1['clave1.3'])) {
        }
    }
}

function ab_verificacion_por_dimension_fallo_2d($var1) {
    for ($i = 0; $i < 10000; $i++) {
        if (array_key_exists('clave1.3', $var1) && 
                (array_key_exists('clave2.5', $var1['clave1.3']))) {
        }
    }
}

function ba_verificacion_a_una_instruccion_fallo_3d($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (@array_key_exists('clave4.1', $var1['clave1.3']['clave2.1'])) {
        }
    }
}

function bb_verificacion_por_dimension_fallo_3d($var1) {
    for ($i = 0; $i < 10000; $i++) {
        if (array_key_exists('clave1.3', $var1) && 
                array_key_exists('clave2.1', $var1['clave1.3']) &&
                array_key_exists('clave4.1', $var1['clave1.3']['clave2.1']) ) {
        }
    }
}

function ca_verificacion_a_una_instruccion_fallo_4d($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (@array_key_exists('clave4.1', $var1['clave1.3']['clave2.1']['clave3.0'])) {
        }
    }
}

function cb_verificacion_por_dimension_fallo_4d($var1) {
    for ($i = 0; $i < 10000; $i++) {
        if (array_key_exists('clave1.3', $var1) && 
                array_key_exists('clave2.1', $var1['clave1.3']) &&
                array_key_exists('clave3.0', $var1['clave1.3']['clave2.1']) &&
                array_key_exists('clave4.1', $var1['clave1.3']['clave2.1']['clave3.0'])) {
        }
    }
}


function da_verificacion_a_una_instruccion_fallo_3d_sobre_4d($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (@array_key_exists('clave4.1', $var1['clave1.3']['clave2.1']['clave3.1'])) {
        }
    }
}

function db_verificacion_por_dimension_fallo_3d_sobre_4d($var1) {
    for ($i = 0; $i < 10000; $i++) {
        if (array_key_exists('clave1.3', $var1) && 
                array_key_exists('clave2.1', $var1['clave1.3']) &&
                array_key_exists('clave3.1', $var1['clave1.3']['clave2.1']) &&
                array_key_exists('clave4.1', $var1['clave1.3']['clave2.1']['clave3.0'])) {
        }
    }
}
Aunque se podía intuir con total certeza que la verificación a una instrucción, directamente haciendo referencia a la clave en la dimensión que se quiere verificar, será más óptima que la verificación dimensión por dimensión hasta llegar a la profundidad de la dimensión que realmente se quiere comprobar; opté por realizar la prueba y así poder ver de manera cuantificable la diferencia que había, y tener que tragarme mis gustos y optar por hacer la comparación con isset(), haciendo referencia a posiciones de memoria inexistentes si las dimensiones de menor profundidad a al que se quiere verificar, ya no existen, como a comenté en la primera entrada de esta serie. Los resultados obtenidos fueron estos:

 Diagrama de llamadas a funciones de comparación de la dos maneras de utilizar array_key_exists()

Diagrama de llamadas a funciones de comparación de la dos maneras de utilizar array_key_exists() de menos a mayor % utilizado 

Bueno, una vez hecho un poco el ganso, tocaba realizar la comprobación definitiva, comparar la eficiencia de ejecución entre array_key_exists() e isset(), así que esté fue el script que utilicé:
function aa_usando_array_key_exists($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (@array_key_exists('clave4.1', $var1['clave1.3']['clave2.1']['clave3.0'])) {
        }
    }
}

function ab_usando_isset($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (isset($var1['clave1.3']['clave2.1']['clave3.0']['clave4.1'])) {
        }
    }
}

function ba_usando_array_key_exists_fallo_dimension_anterior($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (@array_key_exists('clave4.1', $var1['clave1.3']['clave2.5']['clave3.0'])) {
        }
    }
}

function bb_usando_isset_fallo_dimension_anterior($var1) {
   for ($i = 0; $i < 10000; $i++) {
        if (isset($var1['clave1.3']['clave2.1']['clave2.5']['clave4.1'])) {
        }
    }
}
Y los resultados obtenidos fueron estos:


Diagrama de llamadas a funciones de comparación entre la utilización de array_key_exists() e isset()


Diagrama de llamadas a funciones de comparación entre la utilización de array_key_exists() e isset() de menos a mayor % utilizado

Así que como dice el dicho, “una imagen vale más que 1000 palabras”, la eficiencia queda demostrada.

Para concluir solo me queda aclarar un aspecto que alguno le debe descuadrar; se trata de ¿porqué en el caso de la utilización de isset() no tengo ninguna pega por lo que he comentado con el resto? Me refiero al hecho de no gustarme hacer referencias a posiciones de memoria inexistentes. Pues bien, el caso de isset() lo veo sustancialmente distinto, ya que por definición la función sirve para “determinar si una variable esta definida y no es NULL”, así que aparte de considerar que lo que va a hacer es verificar la existencia o inexistencia de una posición de memoria (en términos de definición de posiciones de memoria alocadas en tu programa), si la posición no existe no aparece ningún mensaje de notificación informando de ello, claro está sin anteponer @ a la llamada de la función; así que no me da la sensación de estar realizando algo que no es del todo correcto.


Hasta la próxima enfermos.

19 de junio de 2011

[PHP] Verificación de entradas en arrays multidimensionales (1/2)

conjuntoEntradas() {
  [PHP] Verificación de entradas en arrays multidimensionales (1/2)
  [PHP] Verificación de entradas en arrays multidimensionales (2/2)
}

Ya hace un tiempo que empecé un desarrollo de unas de mis pajas mentales con el conocido framework Web MVC (Model View Controller) denominado Zend Framework. Desde que empecé este desarrollo, el cual he tenido que interrumpir en algunas algunas ocasiones por ciertas obligaciones las cuales me gusté o no tengo que darle preferencias, además de solo poderle dedicar parte de los días que no me empleo en la empresa en la cual estoy contratado a tiempo completo, y digo parte, porque me es imposible no dedicar una parte de esos días a descansar, atender asuntos personales, buscarme la vida por otras partes o hacer otros hobbies muy necesarios para mí, me han ido surgiendo dudas sobre ciertos aspectos del lenguaje PHP; uno de ellos ya lo dejé plasmado en una entrada en este mismo blog hace algún tiempo.

Cuando empecé a descubrir las cosas que me ofrece el propio framework, a día de hoy aún me queda alguna cosa que funcionalidad más bien no por descubrir, sino por utilizar y conocer bien como se utiliza, me dí cuenta que a medida que iba desarrollando mis idas de olla, podría ir modularizando ciertos componentes, que me podrían servir para futuras pajas mentales, además de poder utilizar de manera extensiva en el desarrollo de esta; es decir lo que resumiríamos en arquitecturar código de programación para favorecer el reaprovechamiento mediante creación de componentes genéricos; algo así como ya han hecho los propios desarrolladores de dicho Zend Framework, de ahí que haya acabado en eso, en un framework.

Con todas esta paranoia que me dio y me continúa dando, por el reaprovechamiento basándome en la modularización; una de las cosas que más utilizo es la configuración de los componentes mediante ficheros de configuración, ya sean INI o XML, para poder reutilizarlos sin tener que escribir código de programación.

Zend Framework, ofrece unas clases para leer este formato de ficheros, y alguno más, y después poder obtener un array multidimensional de clave=>valor para posteriormente poder tratar el contenido desde PHP de una manera sencilla. De hecho el propio framework utiliza estos formatos para especificar las configuraciones de la aplicación web que se va a desarrollar y también para configurar parte de sus componentes.

Al empezarlo a utilizarlos en mis componentes y ver que necesitaba, en muchos casos, verificar la existencia de ciertos parámetros obligatorios en los ficheros de configuración, y esto lo tenía que hacer con la verificación de la existencia de claves dentro de los arrays multidimensionales, descubrí que desde PHP se ofrecen varias funciones o maneras de poder verificar la existencia de valores dentro de arrays, así que la paranoia de la optimización se apoderó de mi, provocando una curiosidad, necesaria por hacer cada componente algo más óptimo, por saber cual de ellas tiene un mayor rendimiento en cuanto a velocidad de proceso.

En los comentarios que dejan los usuarios en la documentación de PHP, en varias de las funciones que se pueden utilizar para verificar la existencia de claves, ya se puede saber que algunas de ellas son más óptimas que otras, por ejemplo, la función is_null(), tiene un rendimiento mucho peor que la comparación directamente con NULL, pero esta tiene un rendimiento más bajo si la comparamos con isset(), no obstante de todos estas funciones, aunque nos podrían servir para verificar la existencia de una clave, a mí, particularmente, no me gusta mucho tener que hacer referencia a valores que no van a existir si la clave no existe, que sería lo que tendríamos que hacer con is_null() y con la comparación con NULL, pongamos un ejemplo:

Defino un array que voy a utilizar para todos mis ejemplos y pruebas, dentro de este conjunto de entradas

$var1 = array(
        'clave1.0' => 'valor1.0', 
        'clave1.1' => 'value1.1',
        'clave1.3' => array (
            'clave2.0' => 'value2.0', 
            'clave2.1' => array(
                'clave3.0' => array(
                    'clave4.0' => 'value4.0'
                )
            )
        )
    );

Si quisiéramos verificar la existencia de una clave, con las dos maneas que he comentado, tendríamos que hacer lo siguiente:

if (is_null($var1['clave1.4'])) {
      print "La clave1.4 no exise \n";
  }

  if($var1['clave1.4'] === NULL) {
      print "La clave1.4 no exise \n";
  }

Funcionar, funciona pero no me gusta, porque para la verificación accedemos a una posición del array inexistente, por lo cual se genera para ambos caso el siguiente mensaje de notificación (eso si tenemos el PHP configurado para que notifique los errores del nivel NOTICE):

  PHP Notice: Undefined index: clave1.4 in /path/al/script.php on line NN

Aunque dicho mensaje podríamos provocar que no apareciese anteponiendo @ delante la función o delante la variable en el caso de la comparación con NULL.


Hasta la próxima enfermos.