PHP: Diferencia entre == y ===

A la hora de realizar comparaciones en PHP disponemos de dos operadores == y ===, hoy me han preguntado en qué se diferencian, así que he pensado que no es mala idea explicarlo también aquí.

PHP es un lenguaje muy flexible en cuanto a los tipos de datos, puedes tener una variable con una cadena y compararla con un entero… y no sufrirás la hecatombe que te encontrarías con C o ASP. Y esto es lo que explica la existencia de dos operadores de comparación: == comprueba que los valores sean iguales, independientemente del tipo ya que realiza una conversión antes de comparar. En cambio el operador === comprueba que además de coincidir los valores también coincidan los tipos. Con un pequeño ejemplo se entiende perfectamente

/*Damos a una variable un valor entero*/
$var1 = 0;
/*Damos a otra un valor booleano*/
$var2 = FALSE
/*Si los comparamos con == verás como cumple el condicional, ya que tratará a ambos como booleanos*/
if($var1 == $var2){
  echo "Son iguales"
}
/*En cambio con === no entraría porque no realiza la conversión, de esta forma al tratarse de tipos distintos no entra*/
if($var1 === $var2){
  echo "Son idénticos"
}

CouchCMS: Creando una región editable con sólo una etiqueta

No hace mucho me hablaron de CouchCMS en Facebook, un CMS open source (que no libre) ligero y flexible. Apenas he podido trabajar con él todavía porque cada día ando más liado con proyectos varios (y en el curro, de momento, el que quiera CMS va a chupar WordPress por un tubo, que para algo es por el que mejor me muevo de momento).

En todo caso una de las ventajas de este cms es que nos permite fácilmente definir una región de nuestro HTML como editable, de forma que el usuario pueda cambiar fácilmente su contenido desde el panel de administración, como si de una publicación en un blog se tratara.

Vamos a suponer que ya tienes instalado el CMS en tu servidor y que dispones de una web estática HTML, en la que quieres declarar como editable una región. Lo primero es cambiar la extensión de tu archivo .html por .php y, lo siguiente, es añadir antes de la cabecera del HTML el script cms.php desde la carpeta donde guardaras el Couch:

<?php require_once( 'couch/cms.php' ); ?>

Y al final del archivo, en la última línea, la llamada a ejecución de Couch:

<?php COUCH::invoke(); ?>

Con esto tenemos CouchCMS funcionando sobre nuestra vieja página HTML estática. Ahora vamos a con el siguiente paso: declarar editable una región. Para esto basta con usar un par de etiquetas.

<!-- Imaginemos que este es nuestro código inicial -->
<h3>Le título!</h3>
<p>Un parrafillo estático del todo</p>

<!-- Con estas etiquetas ya sería editable la región -->
<h3>Le título!</h3>
<cms:editable name='contenido' type='richtext'>
<p>Un parrafillo estático del todo</p>
</cms:editable>

La etiqueta cms:editable es la que define como tal una región (ok, esto es de captain obvious). El nombre que le demos debería ser único, ya que cuando entremos a la zona de administración del cms la región será identificada con dicho nombre en el editor. En el type hemos puesto, ya que será un texto, richtext para poder modificarla y formatear con un editor WYSIWYG, si bien existen muchas posibilidades.

Próximamente, cuando vaya explorando un poco, iré añadiendo más cosas sobre este cms.

PHPExcel: Exportar e importar archivos ods, xls y xlsx

En PHP tenemos una clase que nos da utilidades para trabajar con hojas de cálculo, PHPExcel. Ya habíamos hablado antes sobre cómo trabajar con PHP y archivos CSV, pero con esta librería podemos trabajar directamente con archivos de hoja de cálculo. Nos permite generar documentos xls, xlsx, ods, pdf… formatear las celdas, aplicar fórmulas, validar datos… e importar hojas de cálculo.

Si descargáis el proyecto desde GitHub os econtraréis con un montón de ejemplos sobre cómo llevar a cabo distintas acciones, aquí vamos a recoger sólo uno, el más básico de creación de un archivo, traduciendo al castellano los comentarios:

error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
date_default_timezone_set('Europe/London');

define('EOL',(PHP_SAPI == 'cli') ? PHP_EOL : '<br />');

/** Cargamos la librería PHPExcel */
require_once '../Build/PHPExcel.phar';


// Creamos un objeto PHPExcel
echo date('H:i:s') , " Create new PHPExcel object" , EOL;
$objPHPExcel = new PHPExcel();

// Definimos las propiedades del documento
echo date('H:i:s') , " Set document properties" , EOL;
$objPHPExcel->getProperties()->setCreator("Creador")
->setLastModifiedBy("Creador")
->setTitle("PHPExcel Test Document")
->setSubject("PHPExcel Test Document")
->setDescription("Documento de Excel creado por clases PHP.")
->setKeywords("office PHPExcel php")
->setCategory("Resultado de la prueba");


// Añadimos datos
echo date('H:i:s') , " Add some data" , EOL;
$objPHPExcel->setActiveSheetIndex(0)
->setCellValue('A1', 'Hello')
->setCellValue('B2', 'world!')
->setCellValue('C1', 'Hello')
->setCellValue('D2', 'world!');

// Comprobamos que la codificación UTF-8 vaya bien
$objPHPExcel->setActiveSheetIndex(0)
->setCellValue('A4', 'Miscellaneous glyphs')
->setCellValue('A5', 'éàèùâêîôûëïüÿäöüç');

// Renombrar la hoja de trabajo
echo date('H:i:s') , " Rename worksheet" , EOL;
$objPHPExcel->getActiveSheet()->setTitle('Simple');


// Definimos la hoja de cálculo activa
$objPHPExcel->setActiveSheetIndex(0);


// Escribimos el resultado en una hoja de Excel 2007 (xlsx)
echo date('H:i:s') , " Write to Excel2007 format" , EOL;
$callStartTime = microtime(true);

$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
$objWriter->save(str_replace('.php', '.xlsx', __FILE__));
$callEndTime = microtime(true);
$callTime = $callEndTime - $callStartTime;

echo date('H:i:s') , " File written to " , str_replace('.php', '.xlsx', pathinfo(__FILE__, PATHINFO_BASENAME)) , EOL;
echo 'Call time to write Workbook was ' , sprintf('%.4f',$callTime) , " seconds" , EOL;

// Ahora salvamos los datos en Excel clásico
echo date('H:i:s') , " Write to Excel5 format" , EOL;
$callStartTime = microtime(true);

$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
$objWriter->save(str_replace('.php', '.xls', __FILE__));
$callEndTime = microtime(true);
$callTime = $callEndTime - $callStartTime;

echo date('H:i:s') , " File written to " , str_replace('.php', '.xls', pathinfo(__FILE__, PATHINFO_BASENAME)) , EOL;
echo 'Call time to write Workbook was ' , sprintf('%.4f',$callTime) , " seconds" , EOL;

// Imprimimos en pantalla que se ha finalizado el trabajo.
echo date('H:i:s') , " Done writing files" , EOL;
echo 'Files have been created in ' , getcwd() , EOL;

Como ya he dicho antes, la documentación incluye un montón de ejemplos, casi 40, así que podréis sacar de ahí mucha ayuda. Con esta librería podrás complacer a los clientes que exigen sacar los resultados de las consultas directamente a hojas de cálculo, por ejemplo.

Eliminar un directorio y su contenido en PHP

Estos días en uno de los proyectos del curro (váis a disculpar que esta semana sólo haya publicado citas, pero ha sido una semana dura en lo laboral y peor en lo personal) he tenido que hacer una función que vacíe un directorio completamente y lo borre en PHP. Alguna dirá rmdir y al carajo… pero cualquier usuario de linux/sistemas unix sabe que a ese rmdir le tienes que poner un -r para que borre los archivos que contiene, como no hay -r en PHP lo vamos a crear nosotros:

<?php
    function rmDir_rf($carpeta)
    {
      foreach(glob($carpeta . "/*") as $archivos_carpeta){             
        if (is_dir($archivos_carpeta)){
          rmDir_rf($archivos_carpeta);
        } else {
        unlink($archivos_carpeta);
        }
      }
      rmdir($carpeta);
     }

Explicación: Usamos glob para accedera todo lo que hay en la carpeta y lo recorremos (foreach). Si encontramos un directorio (is_dir nos lo confirma) entonces tiramos de recursivdad y ejecutamos la propia función dentro de si misma. Si no es un directorio, pues se borra el archivo. Al acabar de patear se borra la carpeta, que ahora que está vacía sí puede ser eliminada por rmdir.

Esta funcionalidad irá incluída en mi librería de seguridad DonnieSecure (próximamente en GitHub)

PHPMailer en gallego

Español:

Si anteayer os comenté que había hecho una traducción al gallego de Colorbox que ya se había integrado en el proyecto en GitHub hoy toca comentar lo mismo de PHPMailer, clase para el envío de correos desde PHP. Una pequeña contribución por mi parte a proyectos libres de los que he echado mano en más de un desarrollo.

Galego:

Se antonte comentéi que fixera unha traducción ó galego do Colorbox que xa fora integrada no proxecto de GitHub hoxe toca facelo con PHPMailer, a popular clase para o envío de correos dende PHP. Unha pequena contribución pola miña parte a proxectos libres dos que ben deitei máis dunha vez nos meus desenvolvementos.

Comprobar si un número es par o impar con PHP

El otro día me preguntaron si había alguna función de PHP que devolviera si un número es par o impar, tipo is_odd, is_even o similar. Realmente es una comprobación que puedes hacer con una línea de código, así que no existe una función que lo haga. Si lo que quieres es simplemente imprimir en pantalla par e impar la solución es simple (supongamos que el número a comprobar está en la variable $number):

echo ($number % 2 ? 'Impar' : 'Par');  

Tan solo necesitas la operación de módulo, que devuelve el resto de la división entera del número entre 2. Como el resultado en caso de que sea par será 0 este será interpretado en los condicionales o en un operador ternario como un FALSE.

¿Lo queréis como función que devuelva true o false? Ok, es simple. Esta es una función que devuelve TRUE si es impar y false si es par :

function esImpar($number){
    return $number % 2;
}

Y si te quieres ir de guay hasta puedes usar el operador & a nivel de bit para hacerlo (para quedar más pro)

if($number & 1){  
    echo 'Impar';  
}else{  
    echo 'Par';  
} 

Ale, ya tenéis por ahí tres soluciones para comprobar si un número es par o impar.

Función PHP para hash de contraseñas con GOST y salt

Se está poniendo de moda GOST (así, sin H, que no es inglés) desde todo el asunto Snowden porque, según han empezado a decir algunos expertos en seguridad, es uno de los algoritmos que la NSA no ha logrado romper (al menos no se sospecha que lo lograran). Se trata de un algoritmo criptográfico parido por matemáticos soviéticos en los últimos años de la Guerra Fría, revisado posteriormente un par de ocasiones. Para más info, como siempre, la wikipedia.

El caso es que con el tema de la paranoia de la seguridad me han pedido una función para encriptar passwords segura en PHP, aunque en PHP 5.5 ya tengamos unas muy cómoda y seguras funciones para trabajar con passwords. Pero cliente manda, y ha leído que GOST tralarí y no se fía de algo que sea standar… así que me he hecho una función propia, que os comparto por si queréis usarla.

<?
function getSecurePass($password_plano, $username){
    if(strlen($password_plano)<8){
	//exigimos un mínimo de 8 caracteres
        $response = array("aceptado"=>FALSE, "Resultado"=>"Error: Contraseña demasiado corta");
    }else{	
                if(strlen($password_plano) % 2){
                     $salt0="@fr!87$"; //aquí metemos una cadena, la que vosotros prefiráis
                }else{
                     $salt0="~m¿0kL" //aquí metemos otra, y según sea par o impar la longitud cogerá la que corresponda
                }		
		$salt1 = substr($password_plano, strlen($texto_plano)-6, 5);
		$salt2 = substr(md5($username), 6, 6);
		$salt3 = substr($username, strlen($username),-2);
		$arrayPss = str_split($password_plano,(strlen($password_plano)/2)+1);
		$hash = hash('gost', $salt3.$arrayPss[0].$salt0.$salt1.$arrayPss[1].$salt3.$salt2);
		sleep(1);
		$response = array("aceptado"=>True, "Resultado"=>$hash);
	}
    reutrn $response;
}

?>

Y para complementar el sistema de login contra ataques por fuerza bruta es una buena idea ralentizar la respuesta desde el servidor. ¿Y cómo hacerlo sin darle la vara al usuario mucho? Haciendo una función de login que espere, pero sólo cuando el password sea erróneo. En este caso no desarrollé nada y copié un ejemplo desde php.net que ahí os pego (en mi caso lo adapté para usarlo con el resto del sistema de login desarrollado, en los comentarios del enlace está la función original son su explicación):

<?php
public function handle_login() {
    if($uid = user::check_password($_REQUEST['email'], $_REQUEST['password'])) {
        return self::authenticate_user($uid);
    }
    else {
        // delay failed output by 2 seconds
        // to prevent bruit force attacks
        sleep(2);
        return self::login_failed();
    }
}
?>

Espero que os sirva de ayuda a la hora de asegurar vuestras contraseñas.

Poblar un combo dinámicamente con jQuery y JSON

Es habitual que, creando formularios, nos encontremos con la situación de tener dos combos (o campo select si lo preferís) y que uno tenga que cargar/modificar sus datos según el resultado seleccionado en el otro, de forma dinámica. Esto es posible con Javascript, y muy cómodo si utilizamos JSON y jQuery.

Os planteo un ejemplo simple: tenemos dos combos, uno con provincias y otro con ayuntamientos. El marcado HTML va a ser más o menos tal que así:

<select id="provincias" name="provincias">
  <option value=""></option>
  <option value="1">A Coruña</option>
  <option value="2">Lugo</option>
  <option value="3">Ourense</option>
  <option value="4">Pontevedra</option>
</select>
<select id="poblaciones" name="poblaciones" disabled="disabled">
</select>

Como véis, al momento de cargar la página el campo provincias tendrá las cuatro provincias gallegas, y el campo poblaciones estará desactivado. ¿Ahora qué necesitamos? Pues primero necesitamos un script que nos saque las poblaciones de la base de datos y nos las envíe como un JSON. Cualquier lenguaje de lado del servidor nos vale, para el ejemplo va a ser PHP (pero vamos, que podría aplicarse con Java, Ruby, node.js, Python…). Crearemos un script llamado getPoblacionesJson.php

<?php
include 'conexionbd.php';
if ($mysqli -> multi_query("CALL sp_GetPoblaciones(" . $_GET['pr'] . ")")) {
	$poblaciones = array();
	do {
		if ($result = $mysqli -> store_result()) {
			while ($fila = $result -> fetch_assoc()) {				
				$poblaciones[$fila['Id']] = $fila['Nombre'];
			}
		}
	} while($mysqli->next_result());
	print_r(json_encode($poblaciones));
}
?>

En este caso veis que lo que hacemos es llamar a un procedimiento almacenado que nos devuelve las provincias, recorremos el resultado y vamos guardándolo en un array. Finalmente lo convertimos a JSON y lo imprimios para que lo recoja la función de Javascript. Si os estáis preguntando cómo va el procedimiento almacenado, es una simple select en MySQL, tal que así:

DELIMITER $$
CREATE PROCEDURE sp_GetPoblaciones(IN provincia int)
begin
	SELECT Id, Nombre FROM poblaciones WHERE (provincia is null or IdProvincia = provincia) ORDER BY Nombre;
end $$
DELIMITER ;

Ok, tenemos entonces el script del servidor, el marcado y el procedimiento en la base de datos. ¿Qué nos queda? Pues el Javascript, vitaminado con jQuery para ganar productividad:

$("#provincias").change(function() {
	$("#poblaciones").empty();
	$.getJSON('http://localhost/getPoblacionesJson.php?pr='+$("#provincias").val(),function(data){
		console.log(JSON.stringify(data));
		$.each(data, function(k,v){
			$("#poblaciones").append("<option value=\""+k+"\">"+v+"</option>");
		}).removeAttr("disabled");
	});
});

La idea es simple: Si se registra algún cambio en el combo de provincias vaciamos el combo de poblaciones y lo rellenamos con los nuevos datos, obtenidos mediante la función getJSON() y que recorreremos con each() como un conjunto de claves/valores. Finalmente, por si está desactivado, lo reactivamos. He hecho un console.log por si queréis ver cómo funciona la cosa en la consola de javascript de Chrome o del Firebug.

Espero que os sirva de ayuda esta entrada para trabajar con combos dinámicos.

Diferencias entre isset(), empty() e is_null() en PHP

Entre las múltiples funciones que existen en PHP para testear los valores de una variable encontramos tres que resultan de gran ayuda para conocer si una variable está definida y/o es nula, que son isset(), empty() e is_null(). Rehaciendo código de alguna gente en mantenimientos o actualizaciones de proyectos me he encontrado con dos hechos:

  1. Muchos no las usan (y luego vienen los errores por usar una variable vacía)
  2. Muchos no tienen muy claro cómo y cuándo usarlas

La cosa es muy sencilla si se explica bien. Vamos a ver en qué consisten las tres, y veréis qué rápido lo entendéis:

  • isset(): Determina si una variable ha sido definida y no es nula. Devuelve FALSE en caso de le pasemos una variable sin definir, una variable definida pero sin valor o con el valor puesto a null, y en el resto de casos devuelve TRUE.
  • empty(): Determina si la variable tiene un valor vacío, por llamarlo de alguna forma. En este caso devuelve TRUE en muchos supuestos: una cadena vacía (es decir «», si es una cadena con un espacio en blanco tal que » » devolverá FALSE), un número 0 (sea tanto un entero, un float o una cadena con el número cero tal que «0»), una variable con el valor FALSE, una variable con el valor NULL, una variable definida pero sin valor y un array vacío. Al contrario que isset(), no puede evaluar variables que no hayan sido definidas.
  • is_null(): En cierto modo es la complementaria a isset(), ya que devuelve TRUE en caso de que la variable sea NULL o que sea una variable definida pero sin valor, y en el resto de los casos FALSE. La diferencia es que no puede evaluar variables que no estén definidas, provocará un error.

Como podéis ver no es especialmente complicado comprender el funcionamiento de estas tres funciones, y su uso os salvará de muchos quebraderos de cabeza. Por ejemplo trabajando con variables pasadas por formularios o recogidas de una tabla de base de datos que acepte nulos, que todavía hay gente que va a lo loco, así, sin comprobación ni condón… y luego vienen las rupturas en el código.