Temas etiquetados como: ‘php’

Como probar diferentes versiones de un mismo código

27 marzo, 2010

Tengo una necesidad y no se como resolverla, llevo tiempo dándole vueltas, buscando información y no encuentro la forma correcta de hacerlo. Por eso pido que si alguien me pudiera orientar un poco, se lo agradecería.

Necesito poder tener varias versiones de un mismo código PHP corriendo, es decir, tengo la aplicación A en su versión 2 corriendo, pero quiero que algunos usuarios me prueben la versión 3 y otros la versión 4. Claro que eso debe hacerse de forma transparente para el usuario, debe loguearse con la misma dirección, con sus mismos datos, etc.

Una dificultad añadida es la base de datos, pero si resuelvo lo del código esto no me supone mayor problema. Se puede afrontar de varias formas, desde tener las tablas con diferentes nombres según la versión, o diferentes bases de datos para cada versión: bd_v1, bd_v2, etc…. y según se van migrando los usuarios se va pasando de una versión a otra. Claro que según va aumentando el número de usuarios esto se complica, por el tiempo necesario para la migración, pero hay técnicas para ello.

Evidentemente existirá una tabla/lista donde indique cada usuario a que versión de código ha de acceder.

El objetivo, por si alguien necesita una aclaración, es poder hacer pruebas de código sin que afecte al 100% de usuarios. Primero con un grupo reducido de betatesters, luego con uno más amplio y así hasta que se decide pasarlo a todos.

¿Alguien me puede pasar alguna referencia, algún artículo donde se explique cómo resolverlo?, porque se ve que no soy capaz de encontrarlo por mi mismo. Muchas gracias.

Utilizar AmazonS3 para descargas

20 agosto, 2009

Un cliente nos pidió que pusiéramos en su web un apartado de descargas de ficheros, nada fuera de lo común si no hubiera sido por el tamaño de los ficheros a descargar: uno de 700MB y otro de 1,5GB

Lo hubiéramos podido poner en su web, por espacio de disco no era, pero para evitar posibles saturaciones de transferencia del servidor y que afectara a otras webs alojadas en el mismo optamos por otra solución: Amazon S3

datacenterPara quien no lo sepa: Amazon S3 es un servicio de almacenamiento de archivos de la empresa Amazon que tiene un coste normal, pero la ventaja que cobra según el espacio consumido y la transferencia (además de otros pequeños conceptos), teniendo la gran ventaja su escalabilidad, es decir, podemos almacenar todo lo que queramos (y podamos pagar) sin preocuparnos de ir añadiendo discos ni contratando más espacio.

Volviendo al problema inicial, optamos también por proporcionar una dirección que no implicara el nombre de amazon S3 (s3.amazonaws.com) para poder cambiar de proveedor o realizar otros cambios sin tener que ir revisando todos los enlaces. Para ello creamos en las entradas de nuestro dominio arcadina.net un CNAME (un alias) llamado “cdn” que apuntara a cdn.arcadina.net.s3.amazonaws.com. De esa forma un fichero que tenga como dirección http://cdn.arcadina.net/descargas/fichero.zip realmente apuntará a http://cdn.arcadina.net.s3.amazonaws.com/descargas/fichero.zip

Pero para que ésto funcione hemos de crear en nuestra cuenta de Amazon S3 un bucket (un contenedor) que se llame exactamente cdn.arcadina.net. Para los puristas, se que S3 no es un CDN como tal, que para eso Amazon tiene otro servicio: CloudFront, pero no se me ocurrió otro nombre ;)

Para subir los ficheros a S3 hemos utilizado un script en PHP que utiliza una clase para gestionar la conexión con AmazonS3. La clase se puede encontrar aqui: http://undesigned.org.za/2007/10/22/amazon-s3-php-class

De esta forma cuando alguien se descargue el fichero de 1,5GB el servidor donde se encuentra la web no se verá afectado de ninguna forma.

Tratamiento del AJAX en un solo fichero

18 agosto, 2009

Últimamente estoy desarrollando sobre todo con PHP+JavaScript (jQuery). En un proyecto interno de Arcadina, que hace un uso intensivo del AJAX, decidí probar un planteamiento diferente del utilizado hasta ahora.

Normalmente las llamadas a la parte AJAX del servidor se hacen a diferentes scripts PHP en función de lo que deseamos obtener (query) o guardar (submit). Por ejemplo, si estamos tratando una lista de clientes podemos llamar a get_cliente.php para solicitar los datos de un cliente pasando su identificador, podemos llamar a delete_cliente.php para eliminarlo o a save_cliente.php para guardar cambios en la ficha, etc. (tengo la manía de utilizar spanglish en el nombre de funciones, procedimientos, etc.). Ejemplo de llamada según esté método:

?Descargar form1.php
1
2
3
4
5
<form name="ejemplo1" action="/api/save_cliente.php" method="post">
<input type="hidden" name="id" value="<?= $cliente->id ?>">
Nombre: <input type="text" name="nombre" value="<?= $cliente->nombre ?>"><br/>
EMail: <input type="text" name="email" value="<?= $cliente->email ?>"><br/>
</form>

Según el proyecto va avanzando nos podemos encontrar con decenas de scripts: get_<em>loquesea</em>, delete_<em>loquesea</em>.... En cada uno de ellos añadir el código de validación, los includes, etc. Se me ocurrió centralizarlo en dos ficheros ajaxsubmit.php y ajaxquery.php, y utilizar un parametro para indicar la acción a realizar. Ejemplo de un formulario utilizando este sistema:

?Descargar form2.php
1
2
3
4
5
6
<form name="ejemplo2" action="/api/ajaxsubmit.php" method="post">
<input type="hidden" name="ajaxsubmit" value="save_cliente">
<input type="hidden" name="id" value="<?= $cliente->id ?>">
Nombre: <input type="text" name="nombre" value="<?= $cliente->nombre ?>"><br/>
EMail: <input type="text" name="email" value="<?= $cliente->email ?>"><br/>
</form>

Como se ve tampoco es que varíe mucho en la parte que se envía al navegador, se añade un nuevo campo oculto “ajaxsubmit” que indica la acción que queremos realizar. Veamos el ajaxsubmit.php:

?Descargar ajaxsubmit.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<?php
 
// Incluimos código mínimo de autenticación y librerías
include('includes.inc.php');
 
// Si no está logeado el usuario, devolvemos error
if (!isset($usuario_login) || ($usuario_login == '')) {
 
	$respuesta['resultado'] = 'nok';
	$respuesta['mensaje'] = 'Error: primero debe identificarse';
 
} else {
 
	$respuesta['resultado'] = 'nok';
	$respuesta['mensaje'] = 'Error no identificado';
 
	$ajaxsubmit = $_POST['ajaxsubmit'];
 
	// "AS_" viene de AjaxSubmit ... ;)	
	$funcion = 'AS_' . $ajaxsubmit;
	// Comprobamos que tenemos una función con ese nombre
	if (function_exists($funcion)) {
		$funcion(); // Llamamos a la función AS_ que indique el ajaxsubmit
	} else {
		$respuesta['resultado'] = 'nok';
		if ($ajaxsubmit == '') {
			$respuesta['mensaje'] = 'No se ha indicado "ajaxsubmit"';
		} else {
			$respuesta['mensaje'] = 'AjaxSubmit: orden "' . $ajaxsubmit . '" no reconocida.';
		}		
	}
 
}
 
// Si el resultado es NOK registramos en el log de errores
if ($respuesta['resultado'] == 'nok') {
	error_log('*** AJAXSUBMIT - error - ' . $respuesta['mensaje']);
}
 
// ---------------------------------------------------------------------------------------
// Devolvemos respuesta en formato JSON
$json = new Services_JSON;
echo $json->encode($respuesta);
 
exit;
 
// =======================================================================================
// Procesos de guardar, eliminar, actualizar, etc...
// =======================================================================================
 
function AS_save_cfg() {
 
	global $respuesta;
	$cfg = array();
 
	foreach($_POST as $clave => $valor) {
		// Comprobar si $clave empieza por 'cfg_'
		if (ereg('^cfg_', $clave)) {
			$k = ereg_replace("^cfg_", "", $clave);
			$cfg[$k] = $valor;
		}
	}
	$errormsg = SaveConfig($cfg);
	if ($errormsg == 'ok') {
		$respuesta['resultado'] = 'ok';
		$respuesta['mensaje'] = 'Configuración guardada';
	} else {
		$respuesta['resultado'] = 'nok';
		$respuesta['mensaje'] = $errormsg;
	}
 
}
 
function AS_save_cliente() {
 
	global $respuesta;
 
	// Código simplificado para el ejemplo: se debería
	// añadir validación y protección contra SQL injection
	$cliente = new Cliente($_POST['id']);
	$cliente->nombre = $_POST['nombre'];
	$cliente->email = $_POST['email'];
 
	$errormsg = $cliente->Save();
	if ($errormsg == 'ok') {
		$respuesta['resultado'] = 'ok';
		$respuesta['mensaje'] = 'Cliente guardado';
	} else {
		$respuesta['resultado'] = 'nok';
		$respuesta['mensaje'] = $errormsg;
	}
}
 
?>

La “gracia” del código está en ésta parte:

19
20
21
22
23
24
25
	$ajaxsubmit = $_POST['ajaxsubmit'];
 
	// "AS_" viene de AjaxSubmit ... ;)	
	$funcion = 'AS_' . $ajaxsubmit;
	// Comprobamos que tenemos una función con ese nombre
	if (function_exists($funcion)) {
		$funcion(); // Llamamos a la función AS_ que indique el ajaxsubmit

Lo que se envia en la variable “ajaxsubmit” es el nombre de la función a llamar, pero la precedemos de “AS_” para evitar conflictos con otros nombres de funciones. Entonces gracias a la magia de PHP, porque es un lenguaje de script, aprovechamos la variable $funcion para realizar una llamada a una funcion: $funcion()

¿Qué ventajas tiene este sistema? Tenerlo centralizado, de esa forma, añadir una capa de autenticación o variar el formato de salida de JSON a XML, por ejemplo, es mucho más sencillo. Evidentemente si alguna función es compleja se puede separar en un fichero aparte y hacer el correspondiente include, que además lo recomiendo.

Para facilitar la labor de serializar los inputs de un formulario y enviarlo por ajax estoy utilizando el plugin ajaxForm para jQuery.

Depurar PHP: dump_var

5 mayo, 2009

Hay muchas formas de depurar software escrito en PHP. Dependiendo del entorno que utilicemos para desarrollar podemos acudir a XDebug, a ZendDebugger, etc. No voy a escribir un articulo extenso sobre ello, quizás otro día.

A veces tenemos que depurar un código en ejecución sobre un servidor en el que no podemos poner ningún software, entonce recurrimos a varias técnicas como escribir mensajes en el log de errores, con error_log, o ir mostrando cosas en el resultado que se envía al navegador, o recurrimos a var_dump para ver un array o un objeto.

Hoy me he encontrado con la necesidad de utilizar var_dump, pero sobre una estructura que no conocía y que además es compleja, y no es que precisamente sea una delicia leer los datos de var_dump. Así que me he puesto a buscar librerías, funciones, utilidades que sacaran la información de var_dump de una forma más legible, y he encontrado una que me ha gustado: dump_var, está alojada en Google Code, es la versión 0.1, pero a mi me ha funcionado muy bien.

Minipost: Excelente artículo sobre escalabilidad

23 febrero, 2009

Si estás interesado, como yo lo estoy en estos momentos, en todo lo relacionado con la escalabilidad de aplicaciones web te recomiendo el blog HighScalability

A través de ese blog he encontrado un excelente artículo que trata varios temas de como escalar una aplicación típica web basada en LAMP: Database sharding at Netlog