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.
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
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.
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_STRING
Cadena
IS_DOUBLE
Punto flotante de doble precisión
IS_LONG
Entero largo
IS_ARRAY
Matriz
IS_EMPTY
Ninguno
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:
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.
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
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:
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).
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.
Su función también puede devolver un tipo de datos
complejo como un objeto o una matriz.
Devolución de un objeto:
Llame object_init(valor_retorno).
Llénelo con valores. Las funciones disponibles para este
propósito se listan más adelante.
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:
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).
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:
Escriba todo el código de su módulo para que
trabaje con la lista de recursos normales mencionada en la
sección (9).
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í.
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:
Agregar la directiva a la estructura php3_ini_structure en
mod_php3.h.
En main.c, editar la función php3_module_startup y
agregar la llamada apropiada a cfg_get_string() o
cfg_get_long().
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.
Agregue la entrada apropiada para su directiva en
php3take1handler() o en php3flaghandler().
En la sección de configuración de la
función _php3_info() en functions/info.c necesita
agregar su nueva directiva.
Y, por último, debe por supuesto usar su directiva en
alguna parte. Esta será asequible como
php3_ini.directiva.