viernes, 7 de noviembre de 2014

Función json_last_error y json_last_error_msg de PHP

En el anterior post vimos el funcionamiento de la función json_decode() que decodificaba una cadena JSON y la convertía a una variable PHP. Ahí provocamos un error estableciendo una profundidad de la recursividad menor de la que tenía el JSON.

Con las funciones json_last_error() y json_last_error_msg() obtenemos el error, si se produce, que ha ocurrido en la última codificación o decodificación JSON mediante las funciones json_encode() o json_decode().

La primera de las funciones, json_last_error() devuelve un valor entero con el ID del último error que se haya producido. Esta función está disponible a partir de PHP 5.3.0.

En cambio, json_last_error_msg() está disponible únicamente a partir de PHP 5.5.0 y devuelve un string con el error de la última llamada a json_encode() o a json_decode().

Ejemplo

Con este ejemplo lo que haremos será provocar el mismo error que en el anterior post y obtener el resultado. El ejemplo lo vamos a ejecutar en un PHP 5.3.29, con lo que la función json_last_error_msg() no estará disponible, con lo que nos devolverá el resultado la función que creemos nosotros:

$json = '{"users":[{ "user": "Carlos", "age": 30, "country": "Spain" }, { "user": "John", "age": 25, "country": "United States" }]}';

// Si existe la función json_last_error_msg devuelve su valor. En caso contrario si existe json_last_error devuelve el texto que tenemos asociado. Si usamos una versión anterior a PHP 5.3 nos devuelve el mensaje de que no se puede mostrar el error
function getJSONError(){
 return (function_exists('json_last_error_msg')) ? json_last_error_msg() : ((function_exists('json_last_error')) ? getJSONErrorMsg(json_last_error()) : 'This PHP cannot show the error');
}

// A partir del código de error nos muestra un mensaje
function getJSONErrorMsg($code){
 switch ($code) {
  case JSON_ERROR_NONE:
   $msg = 'No errors';
  break;
  case JSON_ERROR_DEPTH:
   $msg = 'Maximum stack depth exceeded';
  break;
  case JSON_ERROR_STATE_MISMATCH:
   $msg = 'Underflow or modes mismatch';
  break;
  case JSON_ERROR_CTRL_CHAR:
   $msg = 'Unexpected control character found';
  break;
  case JSON_ERROR_SYNTAX:
   $msg = 'Syntax error, malformed JSON';
  break;
  case JSON_ERROR_UTF8:
   $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
  break;
  default:
   $msg = 'Unknown error';
  break;
 }
 return $code.' '.$msg;
}

// Limitamos la profundidad de la recursividad a 2
$obj = json_decode($json, true, 2);
var_dump($obj);
/* devuelve
NULL -> la profundidad es 4
*/

if ($obj==null){
 $error = getJSONError();
 echo $error;
 /* devuelve
 1 Maximum stack depth exceeded
 */
}

Función json_decode de PHP

La función json_decode() convierte un string codificado en JSON a una variable de PHP apropiada. Si la cadena no puede ser decodificada o se alcanza el límite de profundidad de la recursividad, devuelve un NULL. En caso de error, se puede usar la función json_last_error() para conocer el motivo exacto.

Sintaxis

mixed json_decode ( string $json [, bool $assoc = false [, int $depth = 512 [, int $options = 0 ]]] )

Parámetros

json: cadena JSON que queremos decodificar. 

assoc: si lo ponemos a TRUE indicaremos que queremos convertir el objeto resultado en un array asociativo

depth: límite de profundidad de la recursividad. Por defecto está a 512. Si se supera el límite devolverá un error.

options: se trata de una máscara de bits de opciones de la decodificación. Actualmente está disponible únicamente JSON_BIGINT_AS_STRING que convertiría los enteros grandes a cadena, en lugar de a float como lo haría por defecto.

Ejemplo

Veamos con un ejemplo su funcionalidad y lo que devuelve en cada caso. Los nombres y valores de la cadena JSON siempre deben ir entre comillas dobles:


$json = '{"users":[{ "user": "Carlos", "age": 30, "country": "Spain" }, { "user": "John", "age": 25, "country": "United States" }]}';

// Decodificamos la cadena JSON
$obj = json_decode($json);
var_dump($obj);
/* devuelve
object(stdClass)#1 (1) { ["users"]=> array(2) { [0]=> object(stdClass)#2 (3) { ["user"]=> string(6) "Carlos" ["age"]=> int(30) ["country"]=> string(5) "Spain" } [1]=> object(stdClass)#3 (3) { ["user"]=> string(4) "John" ["age"]=> int(25) ["country"]=> string(13) "United States" } } }
*/

// Devolvemos el resultado como array
$obj = json_decode($json, true);
var_dump($obj);
/* devuelve
array(1) { ["users"]=> array(2) { [0]=> array(3) { ["user"]=> string(6) "Carlos" ["age"]=> int(30) ["country"]=> string(5) "Spain" } [1]=> array(3) { ["user"]=> string(4) "John" ["age"]=> int(25) ["country"]=> string(13) "United States" } } }
*/

// Limitamos la profundidad de la recursividad a 2
$obj = json_decode($json, true, 2);
var_dump($obj);
/* devuelve
NULL -> la profundidad es 4
*/


martes, 4 de noviembre de 2014

Buscar múltiples textos en un texto con PHP

A la hora de buscar si un texto aparece dentro de un texto más amplio mediante PHP tenemos una función muy conocida llamada strpos() que nos devuelve la posición de dicho texto en el otro.

Con la función estándar, si queremos buscar varios textos deberíamos repetir la llamada tantas veces como necesitemos. 

Para facilitar la tarea tenemos esta función en la que la lista de cadenas a buscar la pasaremos mediante un array y además le podremos decir si es una búsqueda sensible a mayúsculas/minúsculas o no.

La función es la siguiente:


function getMultiPos($haystack, $needles, $sensitive=true, $offset=0){
    foreach($needles as $needle) {
        $result[$needle] = ($sensitive) ? strpos($haystack, $needle, $offset) : stripos($haystack, $needle, $offset);
    }
    return $result;
}

La llamada a la función sería como veremos a continuación. La primera llamada es sensible a mayúsculas/minúsculas y la segunda no:


$haystack = "A cat in gloves catches no mice.";
$needles = array("glove", "mouse", ".", "cat", "No");

var_dump(getMultiPos($haystack, $needles));
/* output:
array(5) { ["glove"]=> int(9) ["mouse"]=> bool(false) ["."]=> int(31) ["cat"]=> int(2) ["No"]=> bool(false) }
*/

var_dump(getMultiPos($haystack, $needles, false));
/* output:
array(5) { ["glove"]=> int(9) ["mouse"]=> bool(false) ["."]=> int(31) ["cat"]=> int(2) ["No"]=> int(24) }
*/

viernes, 31 de octubre de 2014

Contar los elementos de un array en PHP

Para contar cuántos elementos tiene un array que hayamos creado tenemos dos funciones en PHP. Se trata de las funciones count() y sizeof(). La segunda de ellas es realmente un alias de la primera, con lo que la sintaxis y comportamiento es exactamente el mismo.

En algunos blogs he leído que sizeof() puede llegar a ser más rápido que count() pero mis pruebas siempre han sacado unos resultados prácticamente idénticos.

Sintaxis

count(array, modo);
sizeof(array, modo);

Parámetros

array: Obligatorio. Especifica el array del que queremos contar los elementos
modo: Opcional. Especifica el modo de contar. Los valores posibles son:
             0 - Valor por defecto. No hace falta especificarlo. Cuenta los elementos de primer nivel
             1 - Cuenta el array recursivamente y devuelve el total de elementos si es multidimensional
Veamos un par de ejemplos de funcionamiento:

$comida = array('frutas' => array('naranja', 'pera', 'manzana'),
                          'verduras' => array('tomate', 'pimiento'));

// Count normal
count($comida); // devuelve un 2

// Count recursivo
count($comida, 1); // devuelve un 7


jueves, 30 de octubre de 2014

Obtener el tamaño de las tablas de una base de datos MySQL

En el anterior post hablábamos sobre cómo obtener el tamaño de las base de datos. En esta ocasión nos centramos en el tamaño de las tablas de una base de datos.

Lo que haremos será una SQL que nos devuelva todas las tablas de la base de datos test ordenadas de la que ocupa más tamaño a la que ocupa menos, expresada la información en MB.

La SQL sería así:

mysql> SELECT  table_name, table_rows,
        ROUND(data_length / (1024 * 1024), 2) AS `data_size`,
        ROUND(index_length / (1024 * 1024), 2) AS `index_size`,
        ROUND((data_length + index_length) / (1024 * 1024), 2) AS `total_size`,
    FROM information_schema.TABLES
    WHERE table_schema = 'test'
    ORDER BY 5 DESC;

Obtener el tamaño de una base de datos MySQL

Hace unos días me pidieron en una aplicación web en la que estaba trabajando que mostrase el tamaño que tenía la base de datos MySQL.

Como no tenía ninguna función a mano se me ocurrió investigar un poco y con esta simple SQL pude obtener la información que solicitaban.


Los datos del tamaño los muestro en MB y en la consulta me devuelve la información de todas las bases de datos a las que tiene acceso el usuario con el que lanzamos la query.

mysql> SELECT table_schema AS `name_bd`, sum( data_length + index_length ) / 1024 / 1024 AS `size`
    ->   FROM information_schema.TABLES
    ->   GROUP BY table_schema;

Lo que devuelve esta función es lo siguiente:

+--------------------+--------------+
| name_bd            | size         |
+--------------------+--------------+
| information_schema |   0.00390625 |
| test               | 173.52998352 |
+--------------------+--------------+
2 rows in set (0.80 sec)

Si quisiéramos obtener únicamente una base de datos en concreto, deberíamos añadir a la consulta una condición WHERE.

mysql> SELECT table_schema AS `name_bd`, sum( data_length + index_length ) / 1024 / 1024 AS `size`
    ->   FROM information_schema.TABLES
    ->   WHERE table_schema='test'
    ->   GROUP BY table_schema;

Y el resultado es:

+----------------+--------------+
| name_bd        | size         |
+----------------+--------------+
| test           | 173.52998352 |
+----------------+--------------+
1 row in set (0.84 sec)