Incluso si su función no lleva argumentos, es así como se le
llama_
Argumentos de Función
Los argumentos son siempre de tipo pval_ Este tipo contiene una
unión que es el tipo actual del argumento_ Así, si su función
tiene dos argumentos, deberá hacer algo como lo que sigue al
principio de la misma:
NOTA: Los argumentos pueden pasarse tanto por valor como por referencia_
En ambos casos, necesitará pasar &(pval *) a getParameters_ Si
desea comprobar si el enésimo parámetro le ha sido enviado o no por
referencia, puede utilizar la función
ParameterPassedByReference(ht,n)_ Esta devolverá 1 ó 0, según corresponda_
Cuando cambie alguno de los parámetros pasados, tanto si son enviados
por referencia o por valor, puede volver a comenzar con éste
llamando la función pval_destructor sobre el mismo, o, si es una ARRAY
a la que quiere añadir algo, puede utilizar funciones similares a las
incluídas en internal_functions_h, que manipulan el valor return_value
como si fuera de tipo ARRAY_
Además, si cambia un parámetro a IS_STRING, asegúrese primero de
asignar el valor y el tamaño a la cadena creada por estrdup() y sólo
entonces cambiar su tipo a IS_STRING_ Si modifica la cadena de un
parámetro que ya es IS_STRING o IS_ARRAY, deberá primero aplicarle
la función pval_destructor_
Argumentos de Función Variables
Una función puede tomar un número variable de argumentos_ Si su función
puede tomar tanto 2 como 3 argumentos, utilice el siguiente código:
El tipo de cada argumento se guarda en el campo type del pval_ Este
tipo puede ser:
Tabla E_1_ Tipos Internos de PHP
IS_STRING
Cadena
IS_DOUBLE
Coma flotante de doble precisión
IS_LONG
Entero largo
IS_ARRAY
Matriz
IS_EMPTY
Nada
IS_USER_FUNCTION
??
IS_INTERNAL_FUNCTION
?? (N_D_: si alguno de estos no se puede pasar a una función, bórrese)
IS_CLASS
??
IS_OBJECT
??
Si obtiene un argumento de un tipo y desea utilizarlo como si
fuera de otro, o si quiere forzar a que un argumento sea de un
tipo determinado, 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" pasa a ser 0; si no, vale 1 */
convert_string_to_number(arg1); /* Convierte la cadena a LONG o a DOUBLE, dependiendo de su contenido */
Estas funciones convierten el valor in_situ_ No devuelven nada_
El argumento real es almacenado en una unión cuyos miembros son:
IS_STRING: arg1_>value_str_val
IS_LONG: arg1_>value_lval
IS_DOUBLE: arg1_>value_dval
Manejo de Memoria en las Funciones
La memoria necesitada por una función deberá ser asignada usando
emalloc() o estrdup()_ Estas son funciones abstractas de manejo de
memoria que son similares a las funciones normales malloc() y
strdup()_ La memoria deberá liberarse con efree()_
Hay dos tipos de memoria en este programa: la memoria que se devuelve
al troceador (parser) en una variable, y la memoria que se necesita
para almacenamiento temporal de datos en sus funciones_ Cuando asigne
una cadena a una variable que se devolverá al troceador deberá asegurarse
previamente de asignar la memoria con emalloc() o con estrdup()_ Esta
memoria NUNCA debe ser liberada por usted, salvo si más adelante,
en la misma función, sobreescribe la asignación original (aunque este
hábito de programación no es bueno)_
Para cada trozo de memoria temporal/permanente que precise
en sus funciones/librería deberá utilizar las funciones
emalloc(), estrdup(), y efree()_ Estas se comportan
EXACTAMENTE como sus funciones equivalentes_ Cualquier cosa
que asigne con emalloc() o estrdup() deberá liberarla con efree()
en uno u otro momento, salvo que se suponga que deba permanecer
activa hasta el final del programa; de otro modo, se producirá
una fuga de memoria_ El significado de "estas se comportan exactamente
como sus funciones equivalentes" es: si llama a efree() sobre algo
que no ha sido asignado previamente con emalloc() o con estrdup(),
puede provocar un fallo de segmentación_ Por ello debe tener cuidado y
liberar toda la memoria desperdiciada_
Si compila con "_DDEBUG", el PHP3 mostrará una lista de toda la
memoria que fue asignada usando emalloc() y estrdup(), pero que
nunca fue liberada con efree(), al terminar de ejecutar el guión
especificado_
Asignando Variables en la Tabla de Símbolos
Están disponibles una serie de macros que hacen más fácil el
asignar una variale en la tabla de símbolos:
Las tablas de símbolos en PHP 3_0 se implementan como tablas hash
(con extracto)_ En todo momento, &symbol_table es un puntero a
la tabla de símbolos 'principal', mientras que active_symbol_table
apunta a la tabla de símbolos activa (pueden ser idénticas, al
principio de todo, o diferentes, si se está dentro de una función)_
Los ejemplos siguientes utilizan 'active_symbol_table'_ Deberá
reemplazarla por &symbol_table si desea trabajar específicamente
con la tabla de símbolos 'principal'_ También se pueden aplicar las
mismas funciones a matrices, como se explica más abajo_
Ejemplo E_3_ Comprobando si $algo existe en una tabla de símbolos
if (hash_exists(active_symbol_table,"algo",sizeof("algo"))) { existe___ }
else { no existe }
Ejemplo E_4_ Hallando el tamaño de una variable en una tabla de símbolos
Las matrices en PHP 3_0 se implementan utilizando las mismas tablas
hash que para las tablas de símbolos_ Ello quiere decir que las dos
funciones anteriores se pueden usar también para comprobar variables
dentro de matrices_
Si desea definir un nuevo símbolo de matriz en una tabla de símbolos,
deberá hacer lo que sigue_
Primero, deberá comprobar si ya existe usando hash_exists() o hash_find()
y abortar la ejecución de forma apropiada_
Luego inicialice la matriz:
Ejemplo E_5_ Inicializando una nueva matriz
pval matriz;
if (array_init(&matriz) == FAILURE) { falló___ };
hash_update(active_symbol_table,"algo",sizeof("algo"),&matriz,sizeof(pval),NULL);
Este código declara una nueva matriz, llamada $algo, en la tabla de símbolos
activa_ Esta matriz está vacía_
Ahora se muestra cómo añadirle elementos:
Ejemplo E_6_ Añadir entradas a una nueva matriz
pval elemento;
elemento_type = IS_LONG;
elemento_value_lval = 5;
/* define $algo["bar"] = 5 */
hash_update(matriz_value_ht,"bar",sizeof("bar"),&elemento,sizeof(pval),NULL);
/* define $algo[7] = 5 */
hash_index_update(matriz_value_ht,7,&elemento,sizeof(pval),NULL);
/* define el siguiente puesto libre en $algo[],
* $algo[8], como 5 (funciona como en php2)
*/
hash_next_index_insert(matriz_value_ht,&elemento,sizeof(pval),NULL);
Si desea modificar un valor que ha insertado en una matriz asociativa,
deberá primero extraerlo de ella_ Para evitar esa sobrecarga, puede
pasarle un puntero pval ** a la función para insertar en una matriz
asociativa, y será actualizada con la dirección pval * del elemento
insertado dentro de la matriz_ Si dicho valor es NULL (como en todos
los ejemplos anteriores), el parámetro se ignora_
hash_next_index_insert() usa más o menos la misma lógica que
"$algo[] = bar;" en el PHP 2_0_
Si está preparando una matriz como valor devuelto por una función,
puede inicializar la misma como antes, haciendo:
if (array_init(return_value) == FAILURE) { falló___; }
___ y luego añadiéndole valores con las funciones auxiliares:
Por supuesto, si la adición no se realiza justo después de
inicializar la matriz, probablemente tenga que buscarla antes:
pval *matriz;
if (hash_find(active_symbol_table,"algo",sizeof("algo"),(void **)&matriz)==FAILURE) { no se hayó___ }
else { usar matriz_>value_ht___ }
Nótese que hash_find recibe un puntero a un puntero a pval, y no
un puntero a pval_
Casi cualquier función de matrices asociativas devuelve SUCCESS o
FAILURE (excepto por hash_exists(), que devuelve un valor lógico de
certeza)_
Devolviendo valores simples
Están disponibles varias macros para facilitar la devolución
de valores de una función_
Todas las macros RETURN_* fijan el valor y retornan de 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 la cadena (s) especificando el largo (l)_
RETURN_DOUBLE(d)
Las macros RETVAL_* fijan el valor, pero no retornan_
RETVAL_FALSE
RETVAL_TRUE
RETVAL_LONG(l)
RETVAL_STRING(s,dup) Si dup es TRUE, duplica la cadena
RETVAL_STRINGL(s,l,dup) Devuelve la cadena (s) especificando el largo (l)_
RETVAL_DOUBLE(d)
Las macros anteriores harán un estrdup() del argumento 's',
de modo que puede liberar con seguridad el argumento después
de llamar a la macro, o, alternativamente, utilizar memoria
asignada estáticamente_
Si su función devuelve respuestas lógicas de éxito/error, use
siempre RETURN_TRUE y RETURN_FALSE respectivamente_
Devolviendo valores complejos
Su función también puede devolver un tipo de datos complejo, tal
como un objeto o una matriz_
Devolviendo un objeto:
Llame a object_init(return_value)_
Rellénela con valores_ Las funciones disponibles para
ello son listadas más abajo_
Posilemente registre funciones para este objeto_
Para obtener valores del objeto, la función deberá de obtener
"this" desde la active_symbol_table_ Su tipo deberá ser IS_OBJECT,
y básicamente se trata de una matriz asociativa estándar (es decir,
que podrá usar funciones de matriz asociativa sobre _value_ht)_ El
registro en sí de la función se puede hacer utilizando:
Las funciones utilizadas para rellenar un objeto son:
add_property_long( return_value,
nombre_propiedad, l ) _ Añade una propiedad llamada 'nombre_propiedad', de
tipo long, y con valor 'l'
add_property_double( return_value,
nombre_propiedad, d ) _ Igual, pero añadiendo un double
add_property_string( return_value,
nombre_propiedad, cad ) _ Igual, pero añadiendo una cadena
add_property_stringl( return_value,
nombre_propiedad, cad, l ) _ Igual, pero añadiendo una cadena de longitud 'l'
Devolviendo una matriz:
Llame a array_init(return_value)_
Rellénela con valores_ Las funciones disponibles para
ello son listadas más abajo_
Las funciones utilizadas para rellanar una matriz son:
add_assoc_long(return_value,clave,l) _ añade un
elemento asociativo con clave 'clave' y valor long 'l'
add_assoc_double(return_value,clave,d)
add_assoc_string(return_value,clave,cad,duplicar)
add_assoc_stringl(return_value,clave,cad,largo,duplicar)
_ especifica el largo de la cadena
add_index_long(return_value,indice,l) _ añade un
elemento en la posición 'indice' con valor long 'l'
add_index_double(return_value,indice,d)
add_index_string(return_value,indice,cad)
add_index_stringl(return_value,indice,cad,largo)
_ especifica el largo de la cadena
add_next_index_long(return_value,l) _ añade un
elemento a la matriz en la próxima posición libre con valor long 'l'
add_next_index_double(return_value,d)
add_next_index_string(return_value,cad)
add_next_index_stringl(return_value,cad,largo)
_ especifica el largo de la cadena
Usando la lista de recursos
El PHP 3_0 tiene una forma estandarizada de tratar con distintos
tipos de recursos_ Esto sustituye a las listas enlazadas locales
del PHP 2_0_
Funciones disponibles:
php3_list_insert(ptr, tipo) _ devuelve el 'id'
del recurso recién insertado
php3_list_delete(id) _ borra el recurso con el
id especificado
php3_list_find(id,*tipo)
_ devuelve el puntero al recurso con el id especificado, y
actualiza 'tipo' al tipo del mismo
Estas funciones se utilizan típicamente para controladores SQL, pero
pueden utilizarse para cualquier otra cosa, como, por ejemplo, para
mantener descriptores de archivo_
El código típico de un lista sería como este:
Ejemplo E_7_ Añadiendo un nuevo recurso
RESOURCE *recurso;
/* ___asignar memoria para el recurso y adquirirlo___ */
/* añadir un recurso a la lista */
return_value_>value_lval = php3_list_insert((void *) recurso, LE_RESOURCE_TYPE);
return_value_>type = IS_LONG;
Ejemplo E_8_ Utilizando 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 recurso número %d tiene el tipo equivocado",id_recurso_>value_lval);
RETURN_FALSE;
}
/* ___usar recurso___ */
Ejemplo E_9_ Borrando un recurso
pval *id_recurso;
RESOURCE *recurso;
int tipo;
convert_to_long(id_recurso);
php3_list_delete(id_recurso_>value_lval);
Los tipos de recursos deben registrarse en php3_list_h, en la
enumeración list_entry_type_ Además, hay que añadir código de
desconexión para cada tipo de recurso definido en la función
list_entry_destructor() de list_c (incluso si no hay nada que
hacer para la desconexión, deberá añadir un caso vacío)_
Utilizando la tabla de recursos persistentes
El PHP 3_0 tiene una forma estándar de almacenar recursos persistentes
(es decir, recursos que se mantienen entre accesos)_ El primer módulo
que utilizó esta característica fue el MySQL y tras él fue el mSQL,
así que uno puede hacerse una buena idea de cómo utilizar un recurso
persistente leyendo mysql_c_ Las funciones a revisar son:
php3_mysql_do_connect
php3_mysql_connect()
php3_mysql_pconnect()
La idea general de los módulos persistentes es:
Codifique todos sus módulos para que funcionen con
la lista regulares de recursos mencionadas en la sección (9)_
Codifique funciones extra de conexión que comprueben
si el recurso ya está en la lista de recursos persistentes_ Si ya
está, regístrelo en la lista regular como un puntero a la lista
de recursos persistentes (debido a 1_, el resto del código deberá
funcionar de inmediato)_ Si no está en la lista, créelo, añádalo
a la lista de recursos persistentes Y añada un puntero al mismo
desde la lista regular de recursos_ Así todo el código funcionará
porque está en la lista regular, pero en la siguiente conexión el
recurso ya estará en la lista persistente y podrá ser usado sin
re_crearlo_ Deberá registrar estos recursos con un tipo diferente
(por ejemplo, LE_MYSQL_LINK para el enlace no persistente y
LE_MYSQL_PLINK para un enlace persistente)_
Si se leyera mysql_c, notaría que, salvo por que hay una función de
conexión más compleja, no hay que cambiar nada más del resto del
módulo_
Existe exactamente la misma interfaz para la lista de recursos
regular y para la lista de recursos persistente, pero cambiando
únicamente 'lista' por 'listap':
php3_plist_insert(ptr, tipo) _ devuelve el 'id'
del recurso recién insertado
php3_plist_delete(id)_ borra el recurso con el
id especificado
php3_plist_find(id,*tipo)
_ devuelve el puntero al recurso con el id especificado, y
actualiza 'tipo' al tipo del mismo
Sin embargo, es más que probable que estas funciones se muestren
inútiles cuando intente implementar un módulo persistente_ Típicamente
usted querrá usar el hecho de que la tabla de recursos persistentes
es en realidad una matriz asociativa_ Por ejemplo, en los módulos
MySQL/mSQL, cuando hay una llamada a pconnect() (conexión
persistente), la función combina en una cadena el servidor/usuario/clave
que se pasaron a la función y codifica el enlace SQL con esta
cadena como clave_ La siguiente vez que alguien llame a pconnect()
con el mismo servidor/usuario/clave, se generará la misma clave,
y la función hayará el enlace SQL en la lista persistente_
Hasta que se documente mejor, deberá mirar en mysql_c o en msql_c
para ver como utilizar las capacidades de matriz asociativa de la
listap_
Una cosa importante: a los recursos que van a parar a la lista de
recursos persistentes *NO* se les debe asignar memoria usando el
gestor de memoria del PHP, es decir, que NO deben ser creados
utilizando emalloc() o estrdup(), etc_ En este caso se debe usar
las funciones habituales malloc(), strdup(), etc_ La razón para
esto es simple: al final de la petición (final del acceso), se
borran todos los trozos de memoria asignados con el gestor de
memoria del PHP_ Como la lista persistente se supone que no se
debe borrar al final de una petición, no se debe utilizar el gestor
de memoria del PHP para asignar memoria a los recursos de la misma_
Cuando registre un recuros que vaya a estar en la lista persistente,
deberá añadir destructores tanto a la lista persistente como a la
no persistente_ El destructor de la lista no persistente no deberá
hacer nada_ El de la lista persistente deberá liberar adecuadamente
los recursos obtenidos por dicho tipo (por ejemplo, memoria, enlaces
SQL, etc_)_ Tal y como pasa para los recursos no persistentes,
DEBERÁ añadir destructores para cada recurso aunque no sean necesarios
y estén vacíos_ Recuerde que como no se pueden usar emalloc() y
similares en conjunción con la lista persistente, tampoco podrá
utilizar efree() aquí_
Añadiendo directivas de configuración en tiempo de ejecución
Muchas de las características del PHP3 pueden ser configuradas en
tiempo de ejecución_ Estas directivas de configuración pueden aparecer
tanto en el fichero php3_ini o, en el caso de la versión de módulo
del Apache, en los archivos _conf del propio Apache_ La ventaja de
tenerlos en los archivos _conf del Apache es que se puden configurar
directorio por directorio_ Esto quiere decir que cada uno puede tener
un cierto safemodeexecdir, por ejemplo, mientras otro directorio
puede tener otro_ Esta granularidad en la configuración es especialmente
útil cuando un servidor soporta múltiples servidores virtuales_
Los pasos necesarios para añadir una nueva directiva:
Añada la directiva a la estructura php3_ini_structure en mod_php3_h_
En main_c, edite la función php3_module_startup
y añada la llamada a cfg_get_string() o a cfg_get_long()
según se requiera_
Añada la directiva, las restricciones y un
comentario a la estructura php3_commands en mod_php3_c_ Cuidado con
la parte de restricciones_ Las de tipo RSRC_CONF sólo puede aparecer
en los archivos _conf del Apache_ Las directivas de tipo OR_OPTIONS
pueden aparecer en cualquier parte, incluso en los habituales
archivos _htaccess_
Añada el elemento apropiado para su directiva,
bien en php3take1handler(), bien en php3flaghandler()_
Necesita añadir su nueva directiva a la
sección de configuración de la función _php3_info() en
functions/info_c_
Y finalmente, por supuesto, deberá utilizar
su nueva directiva en algún sitio_ Estará accesible como
php3_ini_directiva_
Tenga cuidado aquí_ El valor a usar se debe asignar dinámicamente
y de forma manual, pues el código de manejo de memoria intentará
liberar este puntero más adelante_ Nunca pase memoria asignada
de forma estática a SET_VAR_STRING_