Apéndice F. Extensión de PHP 3

Tabla de contenidos
Adición de funciones a PHP
Llamados a Funciones de Usuario
Reporte de Errores

Esta sección se encuentra bastante desactualizada y demuestra el modo de extender PHP 3. Si está interesado en PHP 4, por favor lea la sección sobre la interfaz de programación Zend. Así mismo, usted querrá leer varios archivos encontrados en el código fuente de PHP, archivos como README.SELF-CONTAINED-EXTENSIONS y README.EXT_SKEL.

Adición de funciones a PHP

Prototipo de Función

Todas las funciones lucen de este modo:
void php3_foo(INTERNAL_FUNCTION_PARAMETERS) {
     
}
Incluso si su función no recibe argumentos, ésta es la forma de llamarla.

Argumentos de Función

Los argumentos son siempre de tipo pval. Este tipo posee una unión que contiene el tipo real del argumento. Así que, si su función recibe dos argumentos, usted haría algo como lo siguiente al comienzo de su función:

Ejemplo F-1. Recuperación de argumentos de función

pval *arg1, *arg2;
if (ARG_COUNT(ht) != 2 || getParameters(ht,2,&arg1,&arg2)==FAILURE) {
   WRONG_PARAM_COUNT;
}
NOTA: Los argumentos pueden ser pasados ya sea por valor o por referencia. En ambos casos necesitará pasar &(pval *) a getParameters. Si desea chequear si el parámetra n'ésimo le fue enviado por referencia o no, puede usar la función ParameterPassedByReference(ht,n). Ésta devolverá 1 o 0.

Cuando usted modifica cualquiera de los parámetros pasados, ya sea que hayan sido enviados por referencia o por valor, puede o bien comenzar con el parámetro llamando pval_destructor sobre él, o si es un ARRAY al que desea agregar valores, puede usar funciones similares a aquellas en internal_functions.h que manipulan return_value como un ARRAY.

También, si modifica un parámetro a IS_STRING asegúrese de asignar primero la nueva cadena mediante estrdup() y la longitud de la cadena, y sólo después modifique el tipo a IS_STRING. Si modifica la cadena de un parámetro que ya es IS_STRING o IS_ARRAY, debe ejecutar pval_destructor sobre éste primero.

Argumentos de Función Variables

Una función puede recibir un número variable de argumentos. Si su función puede recibir ya sea 2 o 3 argumentos, use lo siguiente:

Ejemplo F-2. Argumentos de función variables

pval *arg1, *arg2, *arg3;
int arg_count = ARG_COUNT(ht);

if (arg_count < 2 || arg_count > 3 ||
    getParameters(ht,arg_count,&arg1,&arg2,&arg3)==FAILURE) {
    WRONG_PARAM_COUNT;
}

Uso de los Argumentos de Función

El tipo de cada argumento es almacenado en el campo type de pval. Este tipo puede ser cualquiera de los siguientes:

Tabla F-1. Tipos Internos de PHP

IS_STRINGCadena
IS_DOUBLEPunto flotante de doble precisión
IS_LONGEntero largo
IS_ARRAYMatriz
IS_EMPTYNinguno
IS_USER_FUNCTION??
IS_INTERNAL_FUNCTION?? (si alguno de éstos no puede ser pasado a una función - eliminar)
IS_CLASS??
IS_OBJECT??

Si recibe un argumento de un tipo y quisiera usarlo como otro, o si tan sólo desea obligar al argumento a que sea de un cierto tipo, puede usar una de las siguientes funciones de conversión:
convert_to_long(arg1);
convert_to_double(arg1);
convert_to_string(arg1); 
convert_to_boolean_long(arg1); /* Si la cadena es "" o "0" se convierte a 0, 1 de lo contrario */
convert_string_to_number(arg1);  /* Convierte una cadena a un LONG o DOUBLE, dependiendo de la cadena */

Estas funciones todas realizan conversión en-el-lugar. No devuelven nada.

El argumento como tal es almacenado en una unión; los miembros son:

  • IS_STRING: arg1->value.str.val

  • IS_LONG: arg1->value.lval

  • IS_DOUBLE: arg1->value.dval

Administración de Memoria en las Funciones

Cualquier segmento de memoria necesitado por una función debe ser reservado ya sea con emalloc() o estrdup(). Estas son funciones que abstraen la gestión de memoria y lucen y huelen como las funciones normales malloc() y strdup(). La memoria debe ser liberada con efree().

Hay dos tipos de memoria en este programa: la memoria que es devuelta al intérprete en una variable, y la memoria que necesita para el almacenamiento temporal en su función interna. Cuando asigna una cadena a una variable que es devuelta al intérprete, necesita asegurarse de reservar primero la memoria con emalloc() o estrdup(). Esta memoria no debería ser liberada por usted NUNCA, a menos que más adelante en la misma función sobrescriba su asignación original (aunque este tipo de práctica no se considera apropiada).

Para cualquier segmento de memoria temporal/permanente que necesite en sus funciones/bibliotecas, usted debería usar las tres funciones emalloc(), estrdup(), y efree(). Éstas se comportan EXACTAMENTE como sus contrapartes. Cualquier cosa que reserve con emalloc() o estrdup() debe liberarla con efree() en alguno u otro punto, a menos que espere que permanezca hasta el final del programa; de otro modo, habrá una fuga de memoria. El significado de "las funciones se comportan exactamente como sus contrapartes" es: si usted usa efree() sobre algo que no fue reservado con emalloc() ni estrdup(), puede que reciba un fallo de segmentación. De modo que, por favor, tenga cuidado y libere toda su memoria desperdiciada.

Si compila con "-DDEBUG", PHP imprimirá una lista de toda la memoria que fue reservada usando emalloc() y estrdup() y nunca liberada con efree() una vez termina la ejecución el script especificado.

Definición de Variables en la Tabla de Símbolos

Un número de macros se encuentra a su disposición para facilitar la definición de una variable en la tabla de símbolos:

  • SET_VAR_STRING(nombre,valor)

  • SET_VAR_DOUBLE(nombre,valor)

  • SET_VAR_LONG(nombre,valor)

Aviso

Tenga cuidado con SET_VAR_STRING. La parte del valor debe ser reservada manualmente con malloc, ya que el código de gestión de memoria intentará liberar este apuntador más adelante. No pase memoria reservada estáticamente a un llamado a SET_VAR_STRING.

Las tablas de símbolos en PHP se encuentran implementadas como tablas asociativas. En cualquier momento dado, &symbol_table es un apuntador a la tabla de símbolos 'principal', y active_symbol_table apunta a la tabla de símbolos activa actualmente (éstas pueden ser idénticas, como al arranque, o diferentes, si se encuentra en el interior de una función).

Los siguientes ejemplos usan 'active_symbol_table'. Debe reemplazar este valor con &symbol_table si desea trabajar específicamente con la tabla de símbolos 'principal'. Asimismo, las mismas funciones pueden ser aplicadas sobre matrices, como se explica más adelante.

Ejemplo F-3. Chequear si $foo existe en la tabla de símbolos

if (hash_exists(active_symbol_table,"foo",sizeof("foo"))) { existe... }
else { no existe }

Ejemplo F-4. Encontrar el tamaño de una variable en una tabla de símbolos

hash_find(active_symbol_table,"foo",sizeof("foo"),&pvalue);
check(pvalue.type);
Las matrices en PHP son implementadas usando las mismas tablas asociativas como tablas de símbolos. Esto quiere decir que las dos funciones anteriores pueden ser usadas también para chequear variables al interior de matrices.

Si desea definir una nueva matriz en una tabla de símbolos, debe hacer lo siguiente.

Primero, puede que desee chequear si existe, y abortar la ejecución apropiadamente, usando hash_exists() o hash_find().

A continuación, inicialice la matriz:

Ejemplo F-5. Inicialización de una nueva matriz

pval arr;
  
if (array_init(&arr) == FAILURE) { fallo... };
hash_update(active_symbol_table,"foo",sizeof("foo"),&arr,sizeof(pval),NULL);
Este código declara una nueva matriz, llamada $foo, en la tabla de símbolos actual. Esta matriz se encuentra vacía.

Este es el modo de agregar entradas en ella:

Ejemplo F-6. Adición de nuevas entradas en una matriz nueva

pval entrada;
  
entrada.type = IS_LONG;
entrada.value.lval = 5;
  
/* define $foo["bar"] = 5 */
hash_update(arr.value.ht,"bar",sizeof("bar"),&entrada,sizeof(pval),NULL); 

/* define $foo[7] = 5 */
hash_index_update(arr.value.ht,7,&entrada,sizeof(pval),NULL); 

/* define el siguiente lugar libre en $foo[],
 * $foo[8], como 5 (funciona como en php2)
 */
hash_next_index_insert(arr.value.ht,&entrada,sizeof(pval),NULL);
Si desea modificar un valor que ha insertado a una matriz asociativa, primero debe recuperarlo desde la matriz. Para prevenir ineficiencias, puede ofrecer un pval ** a la función de adición de la matriz asociativa, y éste será actualizado con la dirección pval * del elemento insertado en la matriz. Si tal valor es NULL (como en todos los ejemplos anteriores) - el parámetro es ignorado.

hash_next_index_insert() usa más o menos la misma lógica que $foo[] = bar; en PHP 2.0.

Si está construyendo una matriz para devolverla desde una función, puede inicializar la matriz tal y como se ha indicado, haciendo:

if (array_init(return_value) == FAILURE) { fallo...; }

...y luego agregar valores con las funciones de ayuda:

add_next_index_long(valor_retorno,valor_long);
add_next_index_double(valor_retorno,valor_double);
add_next_index_string(valor_retorno,estrdup(valor_string));

Por supuesto, si la adición no es realizada correctamente luego de la inicialización de la matriz, probablemente tenga que verificar la existencia de la matriz primero:
pval *arr;
  
if (hash_find(active_symbol_table,"foo",sizeof("foo"),(void **)&arr)==FAILURE) { no se pudo encontrar... }
else { use arr->value.ht... }

Note que hash_find recibe un apuntador a un apuntador pval, y no un apuntador pval.

Prácticamente toda función de matriz asociativa devuelve SUCCESS o FAILURE (excepto por hash_exists(), que devuelve un valor booleano de verdad).

Devolución de valores simples

Un número de macros se encuentra a su disposición para facilitar la devolución de valores desde una función.

Las macros RETURN_* todas establecen el valor de retorno y generan una devolución desde la función:

  • RETURN

  • RETURN_FALSE

  • RETURN_TRUE

  • RETURN_LONG(l)

  • RETURN_STRING(s,dup) Si dup es TRUE, duplica la cadena

  • RETURN_STRINGL(s,l,dup) Devuelve una cadena (s) especificando la longitud (l).

  • RETURN_DOUBLE(d)

Las macros RETVAL_* establecen el valor de retorno, pero no devuelven.

  • RETVAL_FALSE

  • RETVAL_TRUE

  • RETVAL_LONG(l)

  • RETVAL_STRING(s,dup) Si dup es TRUE, duplica la cadena

  • RETVAL_STRINGL(s,l,dup) Devuelve una cadena (s) especificando la longitud (l).

  • RETVAL_DOUBLE(d)

Todas las macros de cadena anteriores aplicarán estrdup() sobre el argumento 's' pasado, de modo que puede liberar de forma segura el argumento después de llamar la macro, o alternativamente puede usar memoria reservada estáticamente.

Si su función devuelve repuesteas booleanas de éxito/error, use siempre RETURN_TRUE y RETURN_FALSE respectivamente.

Devolución de valores complejos

Su función también puede devolver un tipo de datos complejo como un objeto o una matriz.

Devolución de un objeto:

  1. Llame object_init(valor_retorno).

  2. Llénelo con valores. Las funciones disponibles para este propósito se listan más adelante.

  3. Posiblemente, registre funciones para este objeto. Para obtener valores del objeto, la función tendría que recuperar "this" desde active_symbol_table. Su tipo debe ser IS_OBJECT, y es básicamente una tabla asociativa regular (esto quiere decir, puede usar las funciones de matrices asociativas regulares sobre .value.ht). El registro como tal de la función puede realizarse usando:
    add_method( valor_retorno, nombre_funcion, apuntador_funcion );

Las funciones usadas para poblar el objeto son:

  • add_property_long( valor_retorno, nombre_propiedad, l ) - Agregar una propiedad llamada 'nombre_propiedad', de tipo long, igual a 'l'

  • add_property_double( valor_retorno, nombre_propiedad, d ) - Igual, sólo agrega un double

  • add_property_string( valor_retorno, nombre_propiedad, str ) - Igual, sólo agrega una cadena

  • add_property_stringl( valor_retorno, nombre_propiedad, str, l ) - Igual, sólo agrega una cadena de longitud 'l'

Devolución de una matriz:

  1. Llame array_init(valor_retorno).

  2. Llénela con valores. Las funciones disponibles para este propósito se listan más adelante.

Las funciones usadas para poblar una matriz son:

  • add_assoc_long(valor_retorno,clave,l) - agregar una matriz asociativa con la clave 'clave' y el valor largo 'l'

  • add_assoc_double(valor_retorno,clave,d)

  • add_assoc_string(valor_retorno,clave,cadena,duplicar)

  • add_assoc_stringl(valor_retorno,clave,cadena,longitud,duplicar) especifica la longitud de la cadena

  • add_index_long(valor_retorno,indice,l) - agregar una entrada en el indice 'indice' con el valor long 'l'

  • add_index_double(valor_retorno,indice,d)

  • add_index_string(valor_retorno,indice,cadena)

  • add_index_stringl(valor_retorno,indice,cadena,length) - especificar la longitud de la cadena

  • add_next_index_long(valor_retorno,l) - agregar una entrada de la matriz en la siguiente ubicación libre con el valor long 'l'

  • add_next_index_double(valor_retorno,d)

  • add_next_index_string(valor_retorno,cadena)

  • add_next_index_stringl(valor_retorno,cadena,length) - especificar la longitud de la cadena

Uso de la lista de recursos

PHP posee una forma estándar de tratar con los varios tipos de recursos. Esto reemplaza todas las listas enlazadas locales usadas en PHP 2.0.

Funciones disponibles:

  • php3_list_insert(apuntador, tipo) - devuelve el 'id' del recurso recién insertado

  • php3_list_delete(id) - eliminar el recurso con el id especificado

  • php3_list_find(id,*tipo) - devuelve el apuntador del recurso con el id especificado, actualiza 'tipo' al tipo del recurso

Típicamente, estas funciones son usadas para gestores de SQL, pero pueden ser usadas para cualquier otra cosa; por ejemplo, mantener descriptores de archivo.

Un listado de código típico luciría de la siguiente forma:

Ejemplo F-7. Adición de un nuevo recurso

RESOURCE *recurso;

/* ...reservar memoria para el recurso y adquirirlo... */
/* agregar un nuevo recurso a la lista */
valor_retorno->value.lval = php3_list_insert((void *) recurso, LE_RESOURCE_TYPE);
valor_retorno->type = IS_LONG;

Ejemplo F-8. Uso de un recurso existente

pval *id_recurso;
RESOURCE *recurso;
int tipo;

convert_to_long(id_recurso);
recurso = php3_list_find(id_recurso->value.lval, &tipo);
if (tipo != LE_RESOURCE_TYPE) {
    php3_error(E_WARNING,"el indice de recurso %d tiene el tipo equivocado",id_recurso->value.lval);
    RETURN_FALSE;
}
/* ...usar el recurso... */

Ejemplo F-9. Eliminar un recurso existente

pval *id_recurso;
RESOURCE *recurso;
int tipo;

convert_to_long(id_recurso);
php3_list_delete(id_recurso->value.lval);
Los tipos de recurso deberían estar registrados en php3_list.h, en enum list_entry_type. Adicionalmente, debe procurarse la implementación de código de finalización para cada nuevo tipo de recurso definido, en list_entry_destructor() ubicado en list.c (incluso si no tiene nada que hacer en la finalización, debe agregar un caso vacío).

Uso de la tabla de recursos persistentes

PHP posee una forma estándar de almacenar recursos persistentes (es decir, recursos que son conservados entre peticiones). El primer módulo en usar esta característica fue el módulo MySQL, y mSQL a continuación, de modo que puede obtener una idea general de cómo debe ser usado un recurso persistente leyendo mysql.c. Las funciones que debe consultar son:

php3_mysql_do_connect
php3_mysql_connect()
php3_mysql_pconnect()

La idea general de los módulos de persistencia es la siguiente:

  1. Escriba todo el código de su módulo para que trabaje con la lista de recursos normales mencionada en la sección (9).

  2. Escriba el código de funciones de conexión extra que revisen si el recurso ya existe en la lista de recursos persistentes. Si es así, regístrelo en la lista de recursos normal como un apuntador a la lista de recursos persistentes (debido a 1., el resto del código debe funcionar inmediatamente). Si no existe, entonces créelo, agréguelo a la lista de recursos persistentes Y agregue un apuntador hacia él desde la lista normal de recursos, de modo que todo el código pueda funcionar; esto ya que se encuentra en la lista de recursos regulares, pero, en la siguiente conexión, el recurso sería encontrado en la lista de recursos persistentes y usado sin tener que crearlo de nuevo. Debe registrar éstos recursos con un tipo diferente (p.ej. LE_MYSQL_LINK para un enlace no-persistente y LE_MYSQL_PLINK para un enlace persistente).

Si lee mysql.c, notará que, con la excepción de la función de conexión más compleja, nada del resto del módulo tiene que ser modificado.

La misma interfaz existe para la lista de recursos regulares y la lista de recursos persistentes, tan sólo 'list' se reemplaza por 'plist':

  • php3_plist_insert(apuntador, tipo) - devuelve el 'id' del recurso recién insertado

  • php3_plist_delete(id) - eliminar el recurso con el id especificado

  • php3_plist_find(id,*tipo) - devuelve el apuntador del recurso con el id especificado, actualiza 'tipo' al tipo del recurso

Sin embargo, es más que probable que estas funciones le resulten inútiles cuando intente implementar un módulo persistente. Típicamente, es deseable aprovechar el hecho de que la lista de recursos persistentes es realmente una tabla asociativa. Por ejemplo, en los módulos MySQL/mSQL, cuando hay un llamado a pconnect() (conexión persistente), la función crea una cadena a partir de los valores de host/usuario/contraseña que fueron pasados a la función, y asocia el enlace SQL con ésta cadena como clave. La siguiente vez que alguien haga un llamado a pconnect() con la misma información de host/usuario/contraseña, se generará la misma clave, y la función encontrará el enlace SQL en la lista persistente.

Hasta que sea documentado más a fondo, debería echarle un vistazo a mysql.c o msql.c para ver cómo pueden usarse las capacidades de tabla asociativa de una lista plist.

Una cosa importante a notar: los recursos que van a la lista de recursos persistentes *NO* debe ser reservada con el gestor de memoria de PHP, es decir, NO debe ser creada con emalloc(), estrdup(), etc. En su lugar, deben ser usadas las funciones normales malloc(), strdup(), etc. La razón de esto es simple - al final de la petición (final de cada visita), cada trozo de memoria que fue ubicado usando el gestor de memoria de PHP es eliminado. Ya que la lista persistente no se supone que deba ser eliminada el final de cada petición, no debe utilizarse el gestor de memoria de PHP para reservar recursos que vayan a la lista.

Cuando registra un recurso que va a ser usado en la lista persistente, debe agregar destructores para ésta tanto en la lista no-persistente como en la persistente. El destructor en la lista no-persistente no debería hacer nada. Aquel en la lista persistente debería liberar apropiadamente cualquier recurso obtenido por ese tipo (p.ej. memoria, enlaces SQL, etc). Tal como con los recursos no-persistentes, usted *DEBE* agregar destructores para cada recurso, incluso si no requieren ser destruidos y el destructor puede estar vacío. Recuerde, ya que emalloc() y amigos no deben ser usados junto con la lista persistente, tampoco debe usar efree() aquí.

Añadir directivas de configuración de tiempo de ejecución

Muchas de las características de PHP pueden ser configuradas en tiempo de ejecución. Estas directivas de configuración pueden aparecer en el archivo php3.ini designado, o, en el caso de la versión módulo de Apache, en los archivos .conf de Apache. La ventaja de tenerlas en los archivos .conf de Apache es que pueden ser configuradas por cada directorio. Esto quiere decir que un directorio puede tener cierto valor para safemodeexecdir, por ejemplo, mientras que otro directorio puede tener otro. Esta especificidad en la configuración es especialmente útil cuando un servidor soporta múltiples hosts virtuales.

Los pasos requeridos para agregar una nueva directiva:

  1. Agregar la directiva a la estructura php3_ini_structure en mod_php3.h.

  2. En main.c, editar la función php3_module_startup y agregar la llamada apropiada a cfg_get_string() o cfg_get_long().

  3. Agregar la directiva, restricciones y un comentario a la estructura php3_commands en mod_php3.c. Fíjese en la parte de restricciones. RSRC_CONF son directivas que pueden estar presentes sólo en las archivos .conf de Apache, Cualquier directiva OR_OPTIONS puede estar presente en cualquier parte, incluyendo archivos .htaccess normales.

  4. Agregue la entrada apropiada para su directiva en php3take1handler() o en php3flaghandler().

  5. En la sección de configuración de la función _php3_info() en functions/info.c necesita agregar su nueva directiva.

  6. Y, por último, debe por supuesto usar su directiva en alguna parte. Esta será asequible como php3_ini.directiva.

Hosting by: hurra.com
Generated: 2007-01-26 18:00:42