You can read the English version of this post in http://phpsblog.agustinvillalba.com/objects-and-references-in-memory-in-php/

Hoy vamos a hablar sobre cómo PHP crea los objetos en memoria cuando realizamos una instrucción del tipo $a = new Foo(); y sobre cómo son gestionadas las referencias en memoria, dado que este es un tema que puede generar debate y diferencia de opiniones. Para ello hablaremos de lo qué NO son las referencias en PHP. Por último, veremos cómo funciona el recolector de basura (o garbage collector) en PHP.

Objetos y referencias en PHP

Una afirmación que está muy extendida en los libros de PHP, y en la red en general, es que en PHP los objetos, por defecto, se pasan por referencia. También hay quienes dicen que los objetos en PHP se asignan por copia. Estas afirmaciones no son completamente ciertas, y para comprobarlo primero hemos de analizar qué son (y qué NO son) las referencias en PHP.

Qué NO son las referencias en PHP

Creo que más importante que saber qué son las referencias en PHP, hemos de aclarar qué NO son las referencias en PHP. En PHP las referencias no son punteros al estilo C, no podemos realizar operaciones aritméticas con las referencias como se puede hacer en C. ¿Por qué? Porque en PHP las referencias no son en realidad direcciones de memoria como en C, no son números que indican una posición de memoria. Pero entonces… ¿qué son en realidad las referencias?

Qué son las referencias en PHP

En PHP, las referencias son “alias” que permiten que 2 variables distintas puedan escribir sobre un mismo valor. O visto de otra manera, son un mecanismo que nos permiten acceder un mismo valor desde nombre de variables distintos y que se comporten como si fueran la misma variable. Hay que tener en cuenta que, en PHP, el nombre de la variable y el contenido de esa variable son 2 cosas totalmente distintas que se enlazan en lo que se llama la tabla de símbolos. De forma que cuando creamos una referencia, simplemente se está añadiendo un alias de dicha variable en la tabla de símbolos de PHP. Veámoslo con un ejemplo, supongamos que tenemos el siguiente código:

$a = new Foo();

Cuando se ejecuta la instrucción anterior, realmente lo que está sucediendo es que se crea una variable “a” en memoria, se crea un objeto de tipo Foo en memoria y se añade una entrada en la tabla de símbolos de PHP en la que se indica que la variable $a “referencia” (o se relaciona, o “apunta”, o como se quiera llamar) al objeto Foo, pero NO es un puntero a dicho objeto. Si a continuación ejecutamos la instrucción:

$b = $a;

Lo que ocurre no es que se $b se convierta en una referencia de $a, tampoco se puede decir que $b sea una copia de $a. Lo que realmente ha sucedido es que se ha creado una nueva variable “b” en memoria y luego se ha añadido una nueva entrada en la tabla de símbolos indicando que la variable $b, también “referencia” al mismo objeto Foo que $a. Si a continuación ejecutamos la instrucción:

$c = &$a;

Lo que ocurre es que en memoria se habrá creado una tercera variable “c” pero NO se añade una nueva entrada en la tabla de símbolos para “c”, sino que en la tabla de símbolos se indica que “c” es un alias de “a”, por lo tanto se comportará de forma idéntica que esta, pero no es que “c” sea un puntero a “a” (lo que en C se conoce como punteros a punteros).
Veamos un ejemplos más completo:

<?php

class myClass {
    public $var;
		
    function __construct() {
	$this->var = 1;
    }

    function inc() { return ++$this->var; }
}

$a = new myClass(); // $a "referencia" a un objeto Foo
$b = $a; //b también referencia al mismo objeto Foo que a
//($a) == ($b) == <id> del objeto Foo, pero a y b son entradas distintas en la tabla de símbolos

echo "\$a = ";var_dump($a);
echo "\$b = ";var_dump($b);

$c = &$a; //$c es un alias de $a
//($a, $c) == <id> del objeto Foo, c es un alias de a en la tabla de símbolos
echo "\$c = ";var_dump($c);

$a = NULL;
//Se elimina la entrada en la tabla de símbolos donde se relacionaba a "$a" con el objeto Foo
//Al eliminarse esta entrada, $c también deja de estar relacionado con Foo
//A todos los efectos, Foo sigue existiendo en memoria, y sigue estando relacionado con $b
echo "\$a = ";var_dump($a);
echo "\$b = ";var_dump($b);
echo "\$c = ";var_dump($c);
echo "\$b->var: ".$b->inc();
echo "\$b->var: ".$b->inc();

$b = NULL;
//Se elimina la entrada en la tabla de símbolos donde se relacionaba a "$b" con el objeto Foo
//Ya no hay más entradas en la tabla de símbolos que se relacionen con Foo,
//Por lo que Foo ha dejado de estar referenciado y puede ser eliminado por el garbage collector

echo "\$b = ";var_dump($b);

Las salidas que produce la ejecución del script anterior son:

$a = object(myClass)#1 (1) { ["var"]=> int(1) } 
$b = object(myClass)#1 (1) { ["var"]=> int(1) } 

$c = object(myClass)#1 (1) { ["var"]=> int(1) } 
$a = NULL 
$b = object(myClass)#1 (1) { ["var"]=> int(1) } 
$c = NULL 
$b->var: 2
$b->var: 3

$b = NULL

Garbage collector en PHP

Por último, veamos cómo funciona el garbage collector (o recolector de basura) en PHP. Un objeto o una variable en PHP podrá ser eliminada de memoria por el garbage collector cuando no exista ninguna referencia a dicho objeto en la tabla de símbolos. Es decir, PHP mantiene un contador de referencias a un objeto desde el momento en que éste es creado, de forma que durante la ejecución del script PHP va incrementando y decrementando dicho contador de referencias en función de las variables que le van “apuntando”. Una vez ese contador de referencias llega a 0 (es decir, nadie está relacionado con ese objeto y por lo tanto, no se está utilizando dicho objeto), PHP marca ese objeto como basura o eliminable, de forma que en la siguiente pasada del garbage collector, éste eliminará dicho objeto de memoria y esa posición de memoria será utilizable por PHP nuevamente.

De esta forma esperamos haber aclarado un poco más cómo gestiona PHP los objetos y variables en memoria y cómo se “seleccionan” los objetos que han de ser eliminados por el recolector de basura en PHP.

You can read the English version of this post in http://phpsblog.agustinvillalba.com/objects-and-references-in-memory-in-php/


You can read the English version of this post in http://phpsblog.agustinvillalba.com/improvedcode-new-plug-tinymce/

En mi trabajo se utiliza mucho el editor web TinyMCE (http://www.tinymce.com), y me pidieron, si fuera posible, que el editor de código fuente HTML mostrara el código correctamente indentado y con los tags remarcados en distintos colores. Me puse a buscar plug-ins en la red para este editor que hicieran justamente lo que me pedían, pero no encontré ninguno que se ajustara exactamente a nuestras necesidades, por lo que me dispuse a hacerlo yo mismo basándome en el ya existente en el propio editor y utilizando la librería CodeMirror (http://codemirror.net) para el remarcado del código fuente. Así nació mi plug-in ImprovedCode para TinyMCE.

Descarga

Puedes descargarte el plug-in en su repositiorio de GitHub:

https://github.com/aguvillalba/improvedcode

Su licencia es LGPL v3 (http://www.gnu.org/copyleft/lesser.html) por lo que eres totalmente libre de modificar su código o incluir el plug-in en cualquier editor TinyMCE.

Instalación

Para instalar el plug-in ImprovedCode en tu editor TinyMCE, simplemente tienes que seguir estos 3 sencillos pasos:

  1. Extrae el archivo .zip en la carpeta plugins de tu editor TinyMCE
  2. En la sección de configuración, donde insertas tu editor, añade “improvedcode” a la lista de plugins
  3. Añade “improvedcode” en la barra de herramientas donde desees que se visualice el botón que lanza el plug-in. Habitualmente llamada “theme_advanced_buttonsN”, donde “N” es el número de la barra de herramientas en cuestión.
  4. Haz de asegurarte que el theme de tu editor TinyMCE está en modo “advanced”. Lo puedes comprobar en la opción de configuración de tu editor, en el mismo código dónde lo insertas en tu HTML:
tinyMCE.init({ 
        // General options 
        mode : "specific_textareas", 
        editor_selector: "mceEditor", 
        theme : "advanced", 
        plugins : "..., improvedcode, ...",
        theme_advanced_buttons1 : "..., improvedcode, ..."
});

Configuración

El plug-in dispone de varias opciones de configuración, permitiendo así que se ajuste mejor a tus necesidades. Como su propio nombre indica, estas son opciones de configuración, por lo que no es obligatorio establecer ninguna de ellas para el correcto funcionamiento del plug-in. Estas opciones son:

  • height: <int>, Valor entero que establece la altura inicial que ha de tener el editor. Por defecto: 580 (px).
  • indentUnit: <int>, Valor entero que establece el número de tabulaciones que ha de haber entre un bloque padre y un bloque hijo. Por defecto: 1
  • tabSize: <int>, Valor entero que determina cuántos espacios componen una tabulación. Por defecto: 4.
  • lineWrapping: <bool>, Valor booleano (true o false) que determina si ha de ajustarse las lineas al editor. Por defecto: true.
  • lineNumbers: <bool>, Valor booleano (true o false) que determina si han de mostrarse los números de línea en el margen izquierdo. Por defecto: true.
  • autoIndent: <bool>, Valor booleano (true o false) que determina si han de indentarse las líneas del editor cuando éste se inicia. Por defecto: true.
  • optionsBar: <bool>, Valor booleano (true o false) que determina si ha de mostrarse la barra de casillas de opciones en la parte superior del editor. Por defecto: true.
  • theme: <string>, Nombre de el tema estético que ha de aplicarse al editor. Por defecto: default. Esta es la lista de los posibles themes a aplicar:
    • ambiance
    • ambiance-mobile
    • blackboard
    • cobalt
    • eclipse
    • elegant
    • erlang-dark
    • lesser-dark
    • monokai
    • neat
    • night
    • rubyblue
    • solarized
    • twilight
    • vibrant-link
    • xq-dark
    • xq-light

Más información

Para más información puedes consultar el archivo “README” incluido en el propio plug-in.

Colaboración

Si encuentras algún error en el funcionamiento o código fuente del plug-in, o si tienes alguna idea o necesidad para añadirle más funcionalidades al plug-in, por favor, no dudes en ponerte en contacto conmigo en mi formulario de contacto

Cómo incluirlo en Joomla

Para añadir el plug-in en Joomla tan sólo tienes que seguir estos pasos:

  1. Descomprimir la última versión del plug-in en el siguiente directorio:
    dir_instalacion_Joomla/media/editors/tinymce/jscripts/tiny_mce/plugins/
  2. Una vez descomprimido, asegúrate de que el directorio del plugin tenga permisos de lectura por parte del servidor web (permisos 755).
  3. Accede a la parte de administración de tu Joomla y haz de ir a Extensiones -> Gestor de plugins -> Editor – TinyMCE
  4. En “Opciones básicas” asegúrate de que la Funcionalidad esté en “Extendida”.
  5. En el bloque “Parámetros extendidos”, en la parte final, en el campo “Plugin personalizado” añade “improvedcode”
  6. Añade “improvedcode” en el campo “Botón personalizado”
  7. Guarda los cambios

Con estos pasos ya deberías tener disponible el botón “HTML+” que lanza el editor HTML de ImprovedCode.

You can read the English version of this post in http://phpsblog.agustinvillalba.com/improvedcode-new-plug-tinymce/


You can read the English version of this post in http://phpsblog.agustinvillalba.com/javascript-difference-null-undefined/

Programando en JavaScript, seguramente todos alguna vez nos hemos encontrado en la situación de tener que ejecutar un bloque de código u otro dependiendo de si una variable es “null” o “undefined”. Hoy vamos a analizar la diferencia que existe en JavaScript entre “null” y “undefined“, dado que hemos de ser muy cuidadosos a la hora de compararlos.

“undefined” en Javascript

En primer lugar, para poder hablar de null y undefined en Javascript hemos de tener claro que “undefined” es un tipo de dato, al igual que lo son el Number, String, Boolean, Object o el Array. En Javascript, a diferencia de otros lenguajes de programación con fuerte tipado, podemos tener variables de tipo “undefined”. A priori puede sonar un poco extraño, pero si lo pensamos un momento, tiene sentido dado que Javascript, al ser un lenguaje débilmente tipado, nos permite declarar variables sin especificar de qué tipo serán los valores que contenga. De esa forma, Javascript considerará a la variable de un tipo de dato u otro en función de su contenido, por ejemplo:

var number1 = 123; // Tipo Number
var number2 = "123" // Tipo String
var number3;

En vista de que la variable “number3” no tiene un valor asignado, ¿de qué tipo es? Dado que todas las variables han de tener siempre un tipo, en este caso Javascript considera la variable “number3” de tipo undefined.

En resumen, en Javascript, todas aquellas variables que no han sido definidas (por lo tanto, no existen) o que han sido definidas sin asignárseles un valor, son siempre de tipo undefined.
Leer el resto de esta entrada »


Después de tanto tiempo escribiendo código PHP ya es hora de revisar nuestro código y nuestra forma de programar para, a partir de ahora, hacerlo de una forma más elegante y compatible con el resto de programadores. Aquí presentamos una guía de estilos de programación para PHP 5.2 o superior. Por supuesto estas no son reglas de obligado cumplimiento, pero sí nos ayudan a estandarizar un poco la forma de programar de todos, haciendo más fácil leer código de otro programador si todos seguimos estas sencillos consejos en la medida de lo posible. Repito, estos no son más que consejos, no leyes.

Apertura y cierre del tag php

Utilizar siempre el tag de apertura completo (<?php) y nunca la versión corta (<?). También es recomendable utilizar el constructor del lenguaje echo y no su versión abreviada (<?= … ?>). Esto nos ahorrará futuros errores si en la configuración del intérprete PHP en el servidor está deshabilitada la variable short_open_tag.

El uso del tag de cierre php (?>) en un documento PHP es opcional para el intérprete de PHP. Sin embargo, si se utiliza, cualquier espacio en blanco introducido a continuación del tag de cierre de php, ya sea por el desarrollador, el cliente FTP, administrador de sistemas, etc, puede provocar una salida inesperada, errores PHP, o incluso si éste es ignorado por el servidor, una página en blanco. Por esta razón, todos los archivos PHP que únicamente contienen código PHP deben omitir el tag de cierre php y en su lugar utilizar un comentario de bloque indicando que se ha llegado al final del archivo y, si se quiere, su ubicación en el sistema de ficheros relativa a la raíz de la aplicación. Este comentario también permite señalar un archivo como completo y que no está truncado.

Correcto

<?php
        echo "Here is my code!";
/* End of file myfile.php */
/* Location: /path/to/myfile.php */

Incorrecto

<?= "Here's my code!"; ?>

Globales, Constantes y Defines

Para nombrar constantes, valores globales y valores definidos mediante la estructura define, han de utilizarse nombres con todas las letras en mayúsculas, separados por guiones bajos si contienen más de una palabra y procurar que sean nombres descriptivos y con sentido, facilitando así su lectura en el futuro, tanto para el propio desarrollador como para otros desarrolladores.

Correcto

MI_CONSTANTE
define('VALOR_FIJO',1);

Incorrecto

miConstante // Evitemos utilizar camelCase
N // Constantes de una sola letra no nos dicen nada
S_C_VER // Este nombre no es descriptivo

Nombres de clases y métodos

Los nombres de clases han de comenzar siempre con una letra en mayúsculas. Los nombres con varias palabras han de separarse con guión bajo y no utilizar el estilo camelCase. Los nombres de los métodos de la clase han de escribirse siempre en minúsculas y procurar ser autodescriptivos, es decir, dejar bastante claro cuál es la funcionalidad del método, preferiblemente incluyendo un verbo. Por supuesto, los nombres de métodos de clase también han de utilizar guiones bajos para separar las palabras que lo componen. Hemos de evitar utilizar nombres de métodos excesivamente largos o con palabras redundantes.

Los nombres de métodos que únicamente son accedidos internamente por nuestra clase (métodos privados) deben ir precedidos por un guión bajo.

Los constructores de clase han de utilizar el estilo PHP5 (public function __constructor()) y no utilizar el estilo de PHP4, es decir, utilizar el nombre de la clase como constructor. Hemos de utilizar destructores de clase cuando sea apropiado.

Se recomienda declarar explícitamente la visibilidad de variables miembro de clase así como de los métodos.

Correcto

class Super_class
public function get_file_properties() //descriptivo, separado por guiones bajos, todas las letras en minúsculas y visibilidad declarada
private function _parse_string() //Igual que los métodos públicos pero precedido por guión bajo

Incorrecto

class superclass
class SuperClass
function fileproperties() // no descriptiva y sin guiones bajos
function get_the_file_properties_from_the_file() // demasiado larga y con palabras redundantes

Nombres de variables

Los estilos usados para los nombres de variables son muy similares a los utilizados para los métodos y miembros de clases. Las variables únicamente han de contener letras en minúsculas, separadas por guiones bajos y procurar ser autodescriptivas, es decir, tener un nombre que permita conocer cuál es la funcionalidad de dicha variable. Las variables con un nombre muy corto o con nombres que no sean palabras han de reservarse exclusivamente para los bucles for().

Correcto

for($j = 0; $j < 10; $j++)
$buffer
$group_id
$last_city

Incorrecto

$j = 'This a string'; // Variables de una sola letra han de reservarse para bucles for
$Str // no utilizar letras mayusculas en variables
$name_of_the_last_city // demasiado largo con palabras innecesarias

True, False y Null

Las palabras claves TRUE, FALSE y NULL han de escribirse siempre con todas sus letras en mayúsculas, dado que al ser valores propios de PHP se pueden considerar constantes, y por lo tanto, escribirse como tales.

Correcto

if (TRUE == $foo)
$bar = FALSE;
function foo( $bar = NULL)

Incorrecto

if( $foo == true)
$bar = false;

Estructuras de control

Con esto nos referimos a las estructuras if, for, while, switch, etc. Las estructuras de control han de tener un espacio en blanco entre la palabra clave de control y la apertura del paréntesis, para distinguirlo así de las llamadas a funciones.

Se recomienda utilizar siempre los abre y cierra llaves, incluso en situaciones donde son técnicamente opcionales. Si los ponemos aunque no sean necesarios, aumentamos la legibilidad del código y reducimos el riesgo de errores lógicos cuando queremos añadir nuevas líneas al bloque de control.

Se recomienda colocar el abre llaves del bloque a ejecutar en una línea nueva tras las condiciones a cumplir, delimitando así claramente cada bloque a ejecutar.

Correcto

if ((condicion1) || (condicion2))
{
    accion1;
}

Separar largas condiciones if en varias lineas

Las condiciones de bloques if se pueden separar en varias líneas si se excede el número límite de caracteres por línea. Las condiciones tiene que ser colocadas en una nueva línea e indentadas 4 caracteres. Los operadores lógicos (&&, ||, etc) deben colocarse al comienzo de cada nueva línea, facilitando así el comentar (y excluir) dicha condición. El cierre del paréntesis y de la llave han de colocarse en una nueva línea al final de las condiciones.

El colocar los operadores al comienzo de cada nueva línea tiene 2 ventajas:

  1. Es extremadamente fácil comentar y exlcuir una condición durante el desarrollo y prueba del código manteniendo bloque sintácticamente correcto.
  2. Revisar cada una de las condiciones es muy sencillo dado que todas están alineadas una debajo de otra.

Correcto

if (($condition1)
    || ($condition2)
    || ($condition3)
) {
    accion1
}

Comparación de variables y literales

Cuando se hacen comparaciones lógicas, ha de colocarse siempre la variable en el lado derecho del comparador. De esta forma, si olvidamos un signo de igual (=) en el comparador se lanzará un error en el intérprete de PHP en lugar de asignar el literal a la variable y que se cumpla como verdadera la asignación, procediendo a ejecutar el contenido del bloque. Realmente no cuesta nada hacerlo así y nos ahorraremos muchos dolores de cabeza de depuración de errores.

Correcto

if ('rock' == $music)
if (NULL == $name)

Incorrecto

if ($music == 'rock') // corremos el riesgo de hacer if ($music = 'rock')

Arrays

La asignación de los valores de un array han de estar alineadas cuando es necesario separarla en varias líneas. Así mismo, cada nueva línea ha de comenzar con una coma (,) separando este nuevo valor del anterior. Esto nos permite comentar dicha línea (y evitar la inclusión de ese par clave/valor) y mantener el bloque del array sintácticamente correcto. El cierre del paréntesis ha colocarse también en una nueva línea al final del array.

Correcto

$un_array = array (
    'foo'   => 'bar'
    ,'spam' => 'ham'
    ,'you'  => 'me'
);

Con esta guía simplemente pretendemos ayudarles a darle un formato más homogéneo a tu código PHP y facilitar la compartición y lectura de código entre varios programadores PHP. Por supuesto, los estilos son como los colores, cuestión de gustos, pero al menos sobre estilos de programación sí hay algo escrito.


Creo que con el título de este artículo poca introducción es necesaria. Hoy no voy a hablar de código ni de variables, pero sí de algo que tiene que ver con la web, y es el proveedor de hosting 1and1 (España) y la pésima experiencia que tuve con su servicio de atención al cliente la primera vez que lo necesité desde que soy cliente suyo.

Hace ya más de un mes, exactamente el día 27 de Octubre de 2011 recibí un correo electrónico del departamento de soporte de 1and1.es que decía así:

Estimado/a Agustín Villalba Casás:

Con este correo electrónico deseamos informarle de antemano de un cambio
en el servidor web de 1&1:

En el marco de unos trabajos de mantenimiento del sistema, durante la
semana
del 31 de octubre al 4 de noviembre se llevará su paquete de hospedaje 1&1
a un entorno operativo actualizado y así podremos seguir ofreciéndole un
entorno
óptimo y altamente seguro.

No se requieren trabajos preparatorios o de ajuste por su parte.
La conversión se lleva a cabo de forma totalmente automática. Sin embargo,
su paquete de hospedaje no estará disponible durante 5 o 10 minutos entre
las
8:45 y las 18:45. Una vez terminada la conversión, podrá disponer de todas
las aplicaciones tal y como está acostumbrado.

Lamentamos que por motivos técnicos no podamos indicar con exactitud la
hora de conversión de su paquete de hospedaje.

¿Cuáles son las mejoras?
=============================
Además de un mejor rendimiento y de mayor seguridad, la conversión a un
entorno operativo Linux (basado en Debian Squeeze/6.0) especialmente
adaptado
a 1& 1 y a sus clientes incluye una actualización de los componentes
siguientes:

Apache 2.2

http://apache.org/

Tenga en cuenta que con la conversión pueden darse algunos cambios en la
configuración del tiempo de ejecución. Así, por ejemplo, con la instrucción
"IndexOptions FancyIndex" se accede ahora a la función que anteriormente
ponía
a disposición el comando "FancyIndex On", entretanto nulo.

http://httpd.apache.org/docs/2.0/upgrading.html

Asimismo, si usa scripts o software muy antiguos, queremos indicarle que
determinadas variables internas, como, por ejemplo, "REMOTE_USER" solo
estarán
disponibles tras un redireccionamiento del Apache
como 'REDIRECT_REMOTE_USER'.

Python 2.6
http://www.python.org
http://docs.python.org/release/2.6.7/whatsnew/2.6.html

Perl 5.10.1
http://perldoc.perl.org/
http://perldoc.perl.org/http://perldoc.perl.org/5.10.1/index.html

Para estas actualizaciones no es necesario que realice acciones.
Estarán automáticamente disponibles tras la conversión. 

A pesar de todo, si tras la conversión algo no funcionara de la forma
acostumbrada
o si tuviera más preguntas, no dude en ponerse en contacto con nuestro
servicio de
soporte.

Como se desprende del correo, la actualización únicamente iba a ser relativa a la versión del servidor web y algunos aspectos de seguridad. Como ellos mismos mencionan, “No se requieren trabajos preparatorios por su parte” y “…si tras la conversión algo no funcionara de la forma acostumbrada o si tuviera más preguntas, no dude en ponerse en con nuestro servicio de soporte.” Con esto en mente uno piensa que su sitio web no tiene porqué dejar de funcionar tras la actualización, dado que no mencionan en ningún momento que se vaya a modificar la configuración del servidor PHP ni ninguno de sus módulos disponibles antes de la actualización, aún así, inspiran cierta tranquilidad invitando a pensar que si algo no funcionara tras la actualización todo se arreglaría con llamarlos, comentarles el problema y todo volvería a la normalidad.

Pues cuál fue mi sorpresa cuando la semana pasada, el día 28 de noviembre de 2011, estaba haciendo algunas pruebas en mi sitio web alojado en sus servidores y descubro que mi servicio de “Contacto” no envía correos electrónicos. Me extrañó mucho, dado que en mi servidor de desarrollo el código funcionaba a la perfección pero en el de producción (1and1) no. Para ello utilizo la librería PHPMailer, por lo que estuve durante 2 horas examinando el código de la librería por cualquier cosa que se haya podido corromper en la subida de los archivos, pero no encontraba nada extraño. Hasta que llegado a un punto recordé la actualización de la que me habían advertido hacía más de un mes. Comencé a comparar línea por línea la configuración de mi servidor PHP de desarrollo y la configuración del servidor PHP de 1and1, hasta que descubrí que el problema era que en la configuración del servidor PHP de 1and1 había desaparecido el módulo de Sockets. Estupendo, ya tenía el motivo, tan sólo tenía que llamar a 1and1, pedirles que me activaran este módulo y todo volvería a la normalidad como me habían dado a entender en su e-mail.

Llamo al servicio de atención al cliente (un número 902… nada barato) y me atiende un hombre bastante agradable inicialmente. Antes de hablar con este hombre, una contestador me advirtió de que la conversación iba a ser grabada, ahora tras la experiencia: ¡menos mal que la grabaron!. Al susodicho operador, le comento mi problema y me pide que espere para que él pueda comprobar la configuración de mi servidor PHP. Tras 3 minutos de espera, vuelve y me dice que efectivamente el módulo de Sockets no está instalado (algo que ya le había dicho yo al inicio de mi llamada). Le pido si por favor me lo pueden activar, que una parte de mi sitio depende de ese módulo para su correcto funcionamiento y me dice que “tendré que revisar mi código y 1and1 no da soporte al código de aplicaciones realizadas por terceros” (???). Le repito que no necesito que me de soporte a mi código PHP, además de que ese mismo código tal cual está funcionaba antes de la actualización, y en su correo mencionaban que no serían necesarios cambios por mi parte, y tras la actualización ese código sin que nadie lo manipulara dejó de funcionar. A esto, el personaje al teléfono, me responde que la actualización “no afectaba a los módulos de PHP, que era únicamente de seguridad de Apache para dar un mejor servicio a los usuarios y bla bla bla…”. Si no afectaba a los módulos, ¿por qué desapareció el módulo de Sockets de PHP?

Tras 10 minutos intentando que el buen hombre entendiera que no necesito soporte a mi código de PHP y que si la actualización no afectaba a los módulos de PHP por qué desapareció el módulo de sockets, me da una nueva excusa al problema diciéndome que “la nueva versión de hostings compartidos del servidor no habilita el módulo de Sockets” y dado que yo tengo contratado hosting compartido, ese módulo no me lo pueden activar. ¿Versión de hostings compartidos del servidor? Servidor PHP hay uno y cada uno lo configura como necesita, o ¿desde cuándo PHP saca versiones de hostings compartidos de su servidor? Pues tras esta excusa, intento pedirle si hay alguna posibilidad de que se active el módulo de Sockets mediante un php.ini para mí o (a la desesperada) mediante un .htaccess en mi sitio. A todo ello responde con un rotundo “NO” sin pararse ni medio segundo a meditar cada una de las opciones.

En vista de que ya llevaba 15 minutos al teléfono y el humanoide al otro lado no dejaba de repetir cíclicamente las 3 excusas que se tenía aprendidas: “No damos soporte al código de terceros”, “La actualización era de seguridad” y “la nueva versión de hostings compartidos no habilita el módulo de Sockets”, le pedí que me pasara con un superior para aclarar el asunto, y su respuesta no fue otra que “No le voy a pasar con un superior, yo soy el agente responsable de esta llamada”. ¡Increíble! No sólo no me había dado solución a mi problema sino que además me negaba el derecho de hablar con un superior suyo. Ante mi indignación, y tras 2 minutos insistiéndole para que me dejara hablar con un superior, me dice con altanería “lo siento señor, pero tengo liberar la línea” y me colgó el teléfono. Sencillamente espectacular pensé, un servicio de atención al cliente ¡que le cuelga el teléfono al cliente!

Tras dejar pasar un rato para calmarme, volví a llamar esperando hablar con otro operador que sí tuviera ganas de solucionar mi problema. La conversación discurrió en términos parecidos a la anterior, pero esta vez el operador, además de no colgarme el teléfono, se comprometió a comunicar mi caso al departamento de sistemas y ver qué se podía hacer.

En un intento desesperado de saber si existía algún operador capaz de ayudarme, volví a llamar por tercera y última vez. Tras comentarle mi problema y él comprobar que no estaba habilitado el módulo de sockets, me dijo que “iba a ser imposible solucionarlo”. Pasamos de un “ver que se puede hacer…” a un “imposible”, no pintaba bien la cosa. Le comenté que ese módulo era muy importante para mi sitio web y me dijo que no podía hacer nada. Le comenté la impotencia que me daba el saber que 1and1 me retiraba de forma unilateral y sin previo aviso un módulo que era necesario para el correcto funcionamiento de mi web y su respuesta fue… silencio. Le dije que “con esto me están empujando a abandonar 1and1, retirar de allí mi web, e irme con otro proveedor de hosting que sí me ofrezca el módulo de Sockets”, y su respuesta fue literlamente “usted puede emprender las acciones que considere oportunas…”, ¡vaya forma de cuidar a su clientela que tiene 1and1!

Al día siguiente pedí a 2 amigos que tienen hosting con CDMon y HostMonster que me permitieran ver sus configuraciones del servidor PHP, y cuál fue mi sorpresa cuando vi que esos hostings sí tienen módulos de Sockets, con lo cual, se confirma lo que ya sabíamos, la excusa de “la nueva versión del servidor de hostings compartidos no habilita el módulo de Sockets” era una burda y penosa mentira del operador que me atendió.

En definitiva, no activaron el módulo de sockets y yo SÍ tuve que modificar mi código para que mi web funcionara (a pesar de que ellos mintieran diciendo que no sería necesario) mientras hago la migración de mi web a otro proveedor de hosting que sí tiene activados los módulos de Sockets.

Mi humilde consejo: No contrates los servicios de 1and1. Todos estos dolores de cabeza que he tenido yo no tienes porqué pasarlos tú. No permitamos que se nos trate como si estuviéramos molestando a los señores de atención al cliente cuando, muy de vez en cuando, requerimos su ayuda. Me gustaría pedirles un favor. Todos aquellos que tengan Twitter, Facebook o cualquier red social… por favor, difundan la noticia y aporten su granito por llegar al mayor número de gente posible. Nos merecemos servicios de soporte de calidad, que eso también nos los cobran. En Twitter podríamos copiar y retwittear el mensaje “@1and1_ES por un servicio de #atencionalcliente de calidad http://wp.me/pIXJS-59 @agustinvillalb Por favor, retwittea!” Muchas gracias a todos!


Hoy escribo para comunicar que, después de bastante tiempo, por fin está terminado y funcionando mi nuevo portfolio en http://www.agustinvillalba.com. Allí pueden encontrar información acerca de mí y algunos de los trabajos que he realizado hasta ahora.

Espero que les guste y espero aquí sus comentarios y/o sugerencias sobre la web si lo desean!


You can read the English version of this post in http://phpsblog.agustinvillalba.com/parallelize-processes-php/

Hoy vamos a explicar cómo poder lanzar múltiples hilos en nuestros scripts PHP, pudiendo así paralelizar aquellos procesos que tengan una gran carga de procesador o que simplemente puedan lanzarse en paralelo dado que no tienen dependencias entre ellos y todos resuelven de forma parcial un problema común.

¿Por qué paralelizar procesos?

Cuando se programa un algoritmo, según el lenguaje en el que lo hagamos, podemos resolver las partes del algoritmo de forma secuencial o paralela. Si tenemos una parte del algoritmo que tiene una gran carga y requiere mucho tiempo para su resolución, podríamos ir resolviendo las demás partes mientras se resuelve la parte más lenta. De forma que el tiempo total de ejecución de nuestro algoritmo pasa de ser la suma de las partes a solamente la parte más lenta. Es cierto que la paralelización realmente se aprovecha cuando disponemos de recursos redundados, por ejemplo procesadores, pero en la gran mayoría de los servidores actuales disponemos de varios núcleos, por lo que tiene bastante utilidad aprender a paralelizar nuestros procesos.

La base teórica de la paralelización nos dice que cuando proceso padre lanza un proceso hijo, se crea en memoria un proceso idéntico al padre, con un identificador de proceso (pid) propio (diferente al del padre) y ejecutándose a partir de la instrucción siguiente a la que creó al propio proceso hijo.

Lanzando multiples hilos en PHP

Antes que nada hemos de tener en cuenta que para poder paralelizar procesos en PHP necesitamos tener instalada la extensión de control de procesos (Process Control Extension [http://www.php.net/manual/en/refs.fileprocess.process.php] en inglés). Dentro de esta extensión, los módulos que nos permitirán paralelizar nuestros procesos son el propio de Control de Procesos (PHP PCNTL [http://www.php.net/manual/en/book.pcntl.php] en inglés) y el de memoria compartida entre procesos (PHP Shared Memory [http://www.php.net/manual/en/book.shmop.php] en inglés). Para poder explicar la paralelización de procesos vamos a poner un ejemplo (bastante simple) en el que un proceso (padre) lanzará 10 hilos en paralelo (hijos) los cuales generarán un número aleatorio, colocarán en una zona de memoria compartida con su padre, éste recogerá cada número generado por sus hijos y devolverá la suma de todos ellos.

Veamos el código:

<?php
function multiple_forks()
{
    $array_pids = array();
    $sumatorio = 0;
    //Almacenamos el process id del proceso padre
    $parent_pid = getmypid();
    for($i=0;$i<10;$i++)
    {
        if(getmypid() == $parent_pid)
        {//Estamos en el proceso padre, asi que lanzamos el proceso hijo y guardamos si pid
            $array_pids[] = pcntl_fork(); //pcntl_fork nos permite lanzar un proceso hijo en paralelo
        }
    }

    //Una vez hemos lanzado los 10 hijos pasamos a generar los números aleatorios (en los hijos)
    //o ir sumandolos si estamos en el padre
    if(getmypid() == $parent_pid)
    {//Estamos en el proceso padre
        while(count($array_pids) > 0)
        {//Mientras queden hijos en ejecución, quedamos a la espera de que terminen
            $pid = pcntl_waitpid(-1,$status);
            //Abrimos la memoria compartida con nuestro hijo $pid
            $shared_id = shmop_open($pid,"a",0,0);
            $share_data = shmop_read($shared_id,0,shmop_size($shared_id));
            $sumatorio += $share_data;
            //Marcamos el bloque para que sea eliminado y lo cerramos
            shmop_delete($shared_id);
            shmop_close($shared_id);
            //Eliminamos el proceso de la cola de hijos en ejecucion
            foreach($array_pids as $key => $hijo)
            {
                if($pid == $hijo) unset($array_pids[$key]);
            }
        }
    }
    else
    {//Estamos en el hijo
        $num = rand(0,100);
        $shared_id = shmop_open(getmypid(),"c",0644,strlen($num));
        if(!$shared_id)
        {//No se pudo crear la memoria compartida
            echo "Error al crear la memoria compartida en el hijo ".getmypid()."\n";
        }
        else
        {
            if(strlen($num) != shmop_write($shared_id,$num,0))
            {
                echo "Error al intentar escribir el numero $num en el hijo ".getmypid()."\n";
            }
            else
            {
                shmop_close($shared_id);
            }
         }
         //Salimos indicando al padre que todo ha ido bien
         exit(0);
    }
    return $sumatorio;
}
?>

Muy probablemente el código se pueda optimizar aun más, pero no es el objetivo de este artículo. Veamos las funciones de control de procesos y memoria compartida que hemos utilizado en el código:

  • pcntl_fork. Nos permite lanzar hijos de un proceso padre, devolviendo el pid del hijo lanzado. (pcntl_fork en PHP)
  • pcntl_waitpid. Nos permite poner al padre en espera de que un hijo suyo termine su ejecución. El parámetro -1 indica que queda a la espera de que algún hijo termine, el primero que lo haga. (pcntl_waitpid en PHP).
  • shmop_open. Nos permite crear o abrir un bloque de memoria. El primer parámetro es a modo de identificador, nada mejor que usar el pid del hijo como identificador, así el padre podrá conocer el identificador con el que se creó dicho bloque de memoria y acceder a los datos compartidos. (shmop_open en PHP)
  • shmop_read. Nos permite leer un bloque de memoria compartida. (shmop_read en PHP)
  • shmop_delete. Marca el bloque de memoria para ser liberado. El bloque será liberado automáticamente por el sistema cuando todos los procesos concurrentes asociados con el bloque se desvinculen del bloque. (shmop_delete en PHP)
  • shmop_close. Cierra un bloque de memoria, indicandole al sistema que el proceso se desvincula del bloque. (shmop_close en PHP)
  • shmop_size. Nos permite conocer el tamaño que ocupa un bloque de memoria compartida. (shmop_size en PHP)
  • shmop_write. Nos permite escribir datos en un bloque de memoria compartida. (shmop_write en PHP)

Con esto tenemos la base necesaria para poder crear procesos paralelos en nuestros controladores y lanzar así tantos hilos como procesadores dispongamos en nuestro servidor.

You can read the English version of this post in http://phpsblog.agustinvillalba.com/parallelize-processes-php/