Entradas más recientes tenemos 3 entradas hasta el momento

Segmentation Fault en PHP con phpAds 0

Recientemente configuramos un servidor de desarrollo local para que nuestros programadores tuvieran acceso a los ficheros de forma más cómoda y transparente y, al mismo tiempo, minimizar la utilización del ancho de banda internacional.
Al principio todo andaba bien pero, de un momento al otro, se comenzaron a producir algunos problemas cuando se intentaba acceder a los scripts PHP de un determinado proyecto. PHP producia una violación de segmento – o acceso – y finalizaba la aplicación.

El volcado con gdb era el siguiente:

# gdb  /usr/local/apache2/bin/httpd core.25582
[...]
Reading symbols from /usr/lib/php/modules/mysql.so...done.
Loaded symbols for /usr/lib/php/modules/mysql.so
Reading symbols from /usr/lib/php/modules/mysqli.so...done.
Loaded symbols for /usr/lib/php/modules/mysqli.so
Reading symbols from /usr/lib/php/modules/pdo.so...done.
Loaded symbols for /usr/lib/php/modules/pdo.so
Reading symbols from /usr/lib/php/modules/pdo_mysql.so...done.
Loaded symbols for /usr/lib/php/modules/pdo_mysql.so
Reading symbols from /usr/lib/php/modules/pdo_sqlite.so...done.
Loaded symbols for /usr/lib/php/modules/pdo_sqlite.so
Reading symbols from /lib/librt.so.1...done.
Loaded symbols for /lib/librt.so.1
Core was generated by `/usr/sbin/httpd'. Program terminated with
signal 11, Segmentation fault.
[New process 9713]
#0  0x001a2c39 in vfprintf () from /lib/libc.so.6

Ahora vemos el backtrace:

(gdb) bt full
#0  0x00466bf3 in vfprintf () from /lib/libc.so.6
No symbol table info available.
#1  0x0048589c in vsprintf () from /lib/libc.so.6
No symbol table info available.
#2  0x00470efe in sprintf () from /lib/libc.so.6
No symbol table info available.
#3  0x01027e18 in vio_new (sd=-1087675456, type=VIO_TYPE_SOCKET, flags=3) at vio.c:146
vio = (Vio *) 0x9d6254c
#4  0x01024dc9 in mysql_real_connect (mysql=0xa313ec8, host=0x9e20578 "localhost", user=0x9e45e24 "webuser", passwd=0x9e45e50 "xxxxxxxx", db=0x0, port=3306, unix_socket=0x102a422 "/var/lib/mysql/mysql.sock", client_flag=0) at client.c:1963
buff = '\0' , "QKI\000\000\000\000\000\000\000\000\000\002\b6\001", '\0' , "do+¿pÑV\000\003", '\0' , "s", '\0'
end =
host_info = 0x102a552 "Localhost via UNIX socket"
sock = 48
ip_addr =
sock_addr = {sin_family = 16072, sin_port = 2609, sin_addr = {s_addr = 3207294824}, sin_zero = "±!\002\001\035A1\n"}
pkt_length =
UNIXaddr = {sun_family = 0,
sun_path = '\0' , "Á\204E\000\000\000\000\000\000\000\000\000\002", '\0' , "äo+¿^Î3\001tèð¶\000\000\000\000\231\231\231\031\005", '\0' , "pÍÿ\000À>1\nÄ\003\000\000ho+¿\000\000\000\000pF\020\001"}
old_signal_handler = (sig_return) 0
#5  0x00a31ebb in php_mysql_do_connect (ht=, return_value=0xa313eb0, return_value_ptr=, this_ptr=0x0, return_value_used=1, persistent=0) at /home/brewbuilder/rpms/BUILD/php-5.2.11/ext/mysql/php_mysql.c:754
index_ptr = (zend_rsrc_list_entry *) 0xa313ea8
new_index_ptr = {ptr = 0x18, type = 20, refcount = 12}
user = 0x9e45e24 "webuser"
passwd = 0x9e45e50 "xxxxxxxxxx"
host_and_port = 0xa313e98 "localhost:3306"
socket = 0x0
tmp =
host = 0x9e20578 "localhost"
user_len = 11
passwd_len = 12
host_len = 14
hashed_details = 0x9e1da44 "mysql_localhost:3306_webuser_xxxxxxxx"
hashed_details_length = 47
port = 3306
client_flags = 0
mysql =
free_host = 1 '\001'
new_link = 0 '\0'
connect_timeout = 60
#6  0x013866a0 in zend_do_fcall_common_helper_SPEC (execute_data=0xbf2b742c) at /home/brewbuilder/rpms/BUILD/php-5.2.11/Zend/zend_vm_execute.h:200
return_reference = 0 '\0'
opline = (zend_op *) 0x9e5ce58
original_return_value =
current_scope = (zend_class_entry *) 0x0
current_this = (zval *) 0x0
should_change_scope = 0 '\0'
#7  0x01379d5d in execute (op_array=0x9d60048) at /home/brewbuilder/rpms/BUILD/php-5.2.11/Zend/zend_vm_execute.h:92
execute_data = {opline = 0x9e5ce58, function_state = {function_symbol_table = 0x4, function = 0x9bc1638, reserved = {0xa313db0, 0x133d6b2, 0xa313db0, 0x0}}, fbc = 0x0, op_array = 0x9d60048, object = 0x0, Ts = 0xbf2b7110, CVs = 0xbf2b70f0,
original_in_execution = 1 '\001', symbol_table = 0xa313d80, prev_execute_data = 0xbf2b764c, old_error_reporting = 0xbf2b7318}
#8  0x0138606e in zend_do_fcall_common_helper_SPEC (execute_data=0xbf2b764c) at /home/brewbuilder/rpms/BUILD/php-5.2.11/Zend/zend_vm_execute.h:234
opline = (zend_op *) 0x9e5d880
original_return_value = (zval **) 0xbf2b7a38
current_scope = (zend_class_entry *) 0x0
current_this = (zval *) 0x0
should_change_scope = 1 '\001'
#9  0x01379d5d in execute (op_array=0x9d601a0) at /home/brewbuilder/rpms/BUILD/php-5.2.11/Zend/zend_vm_execute.h:92
execute_data = {opline = 0x9e5d880, function_state = {function_symbol_table = 0xa313d80, function = 0x9d60048, reserved = {0xa313ca8, 0x133d6b2, 0xa313ca8, 0x0}}, fbc = 0x0, op_array = 0x9d601a0, object = 0x0, Ts = 0xbf2b7580,
CVs = 0xbf2b7560, original_in_execution = 1 '\001', symbol_table = 0xa313c78, prev_execute_data = 0xbf2b7abc, old_error_reporting = 0x0}
#10 0x0138606e in zend_do_fcall_common_helper_SPEC (execute_data=0xbf2b7abc) at /home/brewbuilder/rpms/BUILD/php-5.2.11/Zend/zend_vm_execute.h:234
opline = (zend_op *) 0x9e5d06c
original_return_value = (zval **) 0xbf2b7c50
current_scope = (zend_class_entry *) 0x0
current_this = (zval *) 0x0
should_change_scope = 1 '\001'

Bueno, ya tenemos un indicio del problema. La función que nos devulve la traza es mysql_real_connect(), pero… ¿En qué parte de nuestro código se produce esta llamada y por qué provoca segmentation fault?

Dentro del proyecto en concreto, estamos utilizando phpAds para servir los banners.
Si la violación se produce justo en la llamada a mysql_real_connect(), entonces se debe estár originando en el script que realiza la conexión a la base de datos. Aunque esto parezca obvio, no lo es tanto cuando se tienen muchisimas de líneas de código.
Verificando el script me doy cuenta de que el nombre de usuario de conexión a la DB es incorrecto: algún desarrollador lo ha cambiado sin malas intenciones.
¿Pero esto no debería generar un error MYSQL 1044 (ER_DBACCESS_DENIED_ERROR) en lugar de segmentation fault?

Veamos el código de conexión:

function phpAds_dbConnect()
{
global $phpAds_config;
global $phpAds_db_link;

// Add port to connect, if needed
if (!isset($phpAds_config['dbport']) || !$phpAds_config['dbport'])
 $phpAds_config['dbport'] = 3306;

$host = $phpAds_config['dbhost'];

if ((!isset($phpAds_config['dblocal']) || !$phpAds_config['dblocal']) && $host{0} != ':')
 $host .= ':'.$phpAds_config['dbport'];

 if ($phpAds_config['persistent_connections'])
 $phpAds_db_link = @mysql_pconnect ($host, $phpAds_config['dbuser'], $phpAds_config['dbpassword']);
 else
 $phpAds_db_link = @mysql_connect ($host, $phpAds_config['dbuser'], $phpAds_config['dbpassword']);

if ($phpAds_config['mysql4_compatibility'])
 phpAds_dbQuery("SET SESSION sql_mode='MYSQL40'");

if ($phpAds_config['compatibility_mode'])
 return $phpAds_db_link;

if (@mysql_select_db ($phpAds_config['dbname'], $phpAds_db_link))
 return $phpAds_db_link;
}

[...]

function phpAds_dbQuery($query)
{
 global $phpAds_last_query;
global $phpAds_db_link;

// Connect to the database, if needed
if (!$phpAds_db_link && !phpAds_dbConnect())
 return false;

 $phpAds_last_query = $query;
 return @mysql_query ($query, $phpAds_db_link);
}

Como podemos ver, si $phpAds_config['mysql4_compatibility'] es TRUE, entonces se realiza a una llamada a la función $phpAds_dbQuery(). El problema surge en cuanto la conexión no ha sido establecida, ya que se volverá a llamar a la función $phpAds_dbConnect() desde $phpAds_dbQuery() entrando en un loop infinito hasta producir una violación de acceso.
La solución

Eliminar el bucle infinito mediante una comprobación antes de realizar la llamada a la función que realiza el query.

 if ($phpAds_config['mysql4_compatibility'] && $phpAds_db_link)
 phpAds_dbQuery("SET SESSION sql_mode='MYSQL40'");

Conclusión

Al utilizar gdb y para realizar el backtrace del coredump, podemos tener un volcado minucioso del error y saber exacto qué es lo que debemos buscar.

Variables globales dentro de clases en PHP5 0

Abr22

Desde PHP5 este lenguaje soporta POO muchísimo mejor que sus versiones anteriores: implementa varios modificadores tales como public, private y protected y nos posibilita la creación de interfaces y clases abstractas.
Igualmente, el trabajar con PHP y clases puede presentarnos el problema de que, al no ser un lenguaje POO nativo como Java, se mezclen un poco los paradigmas y la estructura lógica del código si no se tiene algo de experiencia.

Las variables globales

Las variables globales son variables definidas en el ámbito más alto de la estructura del código. Estas variables pueden ser utilizadas por todas las funciones y clases de nuestra aplicación mediante el modificador global o a través de la superglobal $GLOBALS. Trabajar con variables globales dentro de una clase nos puede llevar a ir en contra de una de las características más importantes de la POO: la encapsulación. Ésta nos permite diseñar clases que sean independientes del resto, las cuales se comunicarán con ésta a través la interface que implemente, permitiendo su reutilización en otros proyectos.

Nunca se deben utilizar variables globales dentro de las clases. En su lugar pueden pasarse los argumentos por referencia a los parámetros de una función miembro de la clase.

class MiClase{
$private var

public function __construct($variableGlobal){
$this->var = $variableGlobal;
}
}

$variable = 'Esta es una variable global';

$ob = new MiClase(&$variable);

Las variables superglobales

Otro problema es el de las variable superglobales. Estas variables estan disponibles para toda la aplicación y no respetan la declaración global. Pueden ser útiles, aunque casi siempre son un verdadero dolor de cabeza para los programadores que deseen realizar un código limpio.
Existen varias formas de trabajar con POO y, al mismo tiempo, con las variables superglobales: ninguna de ellas es 100% efectiva.

Podemos crear una clase con miembros estáticos que no será instanciada, o sea que no se creará ningun objeto de la clase, y puede ser utilizada como una colección de métodos (algo así como una implementación recauchutada de un namespace).

class ClaseStatic {

// No permitimos que la clase sea instanciada
private function __construct(){}

public static function getVar($key){
return $GLOBALS[$value];
}

public static function setVar($key, $value){
$GLOBALS[$key] = $value;
}
}

// Asignamos una variable global
ClaseStatic::setVar('miVariable','Esta es una variable');
// Leemos la variable
ClaseStatic::getVar('miVariable');

Como podemos observar, no es la forma más cómoda pero resulta un poco más elegante.
Otra forma es mediante una única instancia de una clase que implemente el patrón Singleton:

 class MiClaseSingleton {

private static $instance;

private function __construct() {}

public static function singleton()
{
if (!isset(self::$instance)) {
 $className = __CLASS__;
 self::$instance = new $className;
}
return self::$instance;
}

public static function getVar($key){
return $GLOBALS[$value];
}

public static function setVar($key, $value){
$GLOBALS[$key] = $value;
}
}

$ob = new MiClaseSingleton;
$ob->setVar('miVariable','Esta es una variable');
$ob->getVar('miVariable');

Conclusiones

Si bien PHP5 tiene características de un lenguaje orientado a objetos, las falencias en cuanto al ámbito de las variables superglobales provoca ciertas restricciones cuando intentamos generar un código que pueda ser reutilizable.

Namespaces en PHP 5.3 0

Abr22

¿Qué son los espacios de nombres o namespaces?

Podría tomarme el tiempo para intentar dar una definición exacta un namespace, pero la que encontré en wikipedia me parece que cumple mejor la tarea:

“Un espacio de nombres es un contexto en el que un grupo de uno o más identificadores pueden existir. Un identificador definido en un espacio de nombres está asociado con ese espacio de nombres. El mismo identificador puede independientemente ser definido en múltiples espacios de nombres, eso es, el sentido asociado con un identificador definido en un espacio de nombres es independiente del mismo identificador declarado en otro espacio de nombres. Los lenguajes que manejan espacio de nombres especifican las reglas que determinan a qué espacio de nombres pertenece una instancia de un identificador.”- Fuente: Wikipedia

En otras palabras y adecuando esta definición a la programación de aplicaciones, los namespaces solucionan el problemas de la duplicidad de nombres haciendo más fácil la portabilidad y compatibilidad entre librerias.
Los programadores de JAVA o C# están acostumbrados a trabajar con estos namespaces. Sin embargo los desarrolladores que trabajan (o intentan trabajar) con POO en PHP debiamos solventar esta carencia utilizando técnicas que podían resultar engorrosas: nombres extremadamente largos para nuestras clases, utilización de prefijos para delimitar paquetes, etc.
Sin embargo a partir de PHP 5.3 se ha incluido un sistema de namespaces que facilita todas estas tarea.

Haciendo uso de namepaces en PHP 5.3

Desde PHP 5.3 podemos utilizar namespaces para empaquetar todas nuestras librerias mediante la palabra reservada namespace. Estos deben definirse al comienzo del script antes de cualquier otro comando, con excepción los declare.
Como los namespaces son jerárquicos, podemos definir sub-namespaces. Los mismos se separan mediante una barra invertida o backslash:


namespace Common\Error;
// código

Namespaces y __autoload()

Como nosotros vamos a trabajar dentro del paradigma POO, esto prestaría funcionalidad en cuanto a la portabilidad y en cuanto a la compatibilidad de las librerías, evitando la duplicidad en las definiciones. Para poder utilizar las librerias definidas mediante namespaces se debe incluir el fichero directamente mediante la función include().
Uno de los grandes problemas que se nos presenta es que para llamar a una librería necesitamos llamar a todos los ficheros mediante includes, lo que puede resultar engorroso y conducir a errores. En lenguajes en donde esta característica se encuentra mejor implementada (lease JAVA) podemos crear bibliotecas realmente poderosas. Sin embargo existe una alternativa para suplir esta carencia mediante la función __autoload(). Cuando una clase es instanciada – o llamamos a alguno de sus miembros de clase, en suma, cuando hacemos uso de esa clase – y PHP no encuentra dicha clase, antes de generar un Fatal errornos da una segunda chance llamando a la función __autoload(). Si esta función no está definida o si la misma no consigue cargar el fichero que contiene la clase, entonces si obtendremos un Fatal error.

Existen varias formas de implementar una función __autoload(), pero la que yo encuentro más cómoda y funcional es la siguiente:

<pre>// Definimos un alias para el namespace
use error\ErrorHandler as ErrorHandler;

// Constante que apunta al directorio que contiene las librerias
define ('LIBPATH', $_SERVER["DOCUMENT_ROOT"] .'/libs/');

// Autoload
function __autoload($class) {
// convert namespace to full file path
$class = LIBPATH .str_replace('\\', '/', $class) . '.php';
require_once($class);
}

ErrorHandler::triggerError('Inclusion dinamica');</pre>

Nótese que no incluimos el fichero que contiene la clase, sino que definimos el namespace y el alias correspondiente. Cuando intentamos acceder a uno de sus métodos de clase y al no encontrarse la misma, el interprete automaticamente llamará a la función __autoload(). Esta a su vez tomará en namespace y convertirá las barras invertidas para generar el path correspondiente e incluirá la clase para que esté disponible.
Para utilizar este método es necesario que el namespace sea igual a la estructura del directorio. En nuestro caso, la librería se encontraría en el path /libs/MiLibreria/ErrorHandler.php.
Otro punto a tener en cuenta es que la librería no se incluye hasta que no es requerida, por lo que nos ahorra tiempo de ejecución y memoria si esa librería no es llamada explicitamente.

Conclusiones finales

Si bien los namespaces de PHP no son tan poderosos como los de otros lenguajes de programación, nos permiten ahorrarnos varios dolores de cabeza y acelerar la distribución de nuestro código para su reutilización.

Referencias

Pueden encontrar más información sobre namespaces en PHP 5.3 en los siguientes artículos:
“How to use PHP namespaces”, Craig Buckler, sitepoint.com
“Namespaces”, PHP Documentation, php.net

Pizarra Dev is powered by WordPress and FREEmium Theme.
developed by Dariusz Siedlecki and brought to you by FreebiesDock.com