Dodatok F. Rozšírenie PHP 3

Obsah
Pridávanie funkcií do PHP
Volanie užívateľských funkcií
Hlásenie chýb

Táto sekcia je už dosť zastaralá a demonštruje ako rozšíriť PHP 3. Ak sa zaujímate o PHP 4, prosím, prečítajte si sekciu na Zend API. Tiež si môžete prečítať rôzne súbory nachádzajúce sa v PHP zdroji, súbory ako sú README.SELF-CONTAINED-EXTENSIONS a README.EXT_SKEL.

Pridávanie funkcií do PHP

Prototyp funkcie

Všetky funkcie vyzerajú takto:
void php3_foo(INTERNAL_FUNCTION_PARAMETERS) {
     
}
Dokonca ak vaša funkcia neobsahuje žiadne argumenty, tak sa takto volá.

Argumenty funkcie

Argumenty sú vždy typu pval. Tento typ obsahuje zväz, ktorý má aktuálny typ argumentu. Takže ak má vaša funkcia dva argumenty, mali by ste na začiatku funkcie urobiť niečo podobné tomuto:

Príklad F-1. Vyvolávanie argumentov funkcie

pval *arg1, *arg2;
if (ARG_COUNT(ht) != 2 || getParameters(ht,2,&arg1,&arg2)==FAILURE) {
   WRONG_PARAM_COUNT;
}
POZNÁMKA: Argumenty sa dajú podať buď hodnotou alebo odkazom. V oboch prípadoch budete musieť dať &(pval *) do getParameters. Ak chcete skontrolovať, či vám bol n-tý parameter poslaný odkazom alebo nie, môžete použiť funkciu ParameterPassedByReference(ht,n). Vráti buď 1 alebo 0.

Keď meníte hocijaký prešlý parameter, či už je poslaný odkazom alebo hodnotou, môžete buď začať odznova zavolaním pval_destructor, alebo ak je to POLE, ktoré chcete pridať, môžete použiť funkcie podobné tým v internal_funcions.h, ktoré manipulujú s return_value ako s POĽOM.

Rovnako, ak meníte parameter na IS_STRING, uistite sa, že najprv priradíte nový estrdup()'ovaný reťazec a dĺžku reťazca, a až potom zmeňte typ na IS_STRING. Ak zmeníte reťazec parametra, ktorý je už IS_STRING alebo IS_ARRAY, mali by ste na neho najprv použiť pval_destructor.

Pohyblivé funkčné argumenty

Funkcia môže nadobudnúť pohyblivé množstvo argumentov. Ak vaša funkcia môže nadobudnúť buď argument 2 alebo 3, použite následovné:

Príklad F-2. Pobyblivé funkčné argumenty

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;
}

Použitie funkčných argumentov

Typ každého argumentu sa ukladá do typového poľa pval. Tento typ môže byť jeden z následujúcich:

Tabuľka F-1. Interné typy PHP

IS_STRINGReťazec
IS_DOUBLEPohyblivá čiarka s dvojitou presnosťou
IS_LONGDlhý integer
IS_ARRAYPole
IS_EMPTYNič
IS_USER_FUNCTION??
IS_INTERNAL_FUNCTION?? (ak niektoré z týchto nemôžu byť vložené do funkcie - delete)
IS_CLASS??
IS_OBJECT??

Ak dostanete argument jedného typu a chceli by ste ho použiť ako iný, alebo ak chcete prinútiť argument, aby bol príslušného typu, môžete použiť následujúce konverzné funkcie:
convert_to_long(arg1);
convert_to_double(arg1);
convert_to_string(arg1); 
convert_to_boolean_long(arg1); /* Ak je retazec "" alebo "0", bude 0, inak 1 */
convert_string_to_number(arg1);  /* Konvertuje retazec bud na LONG alebo DOUBLE zavisiac na retazci */

Tieto funkcie robia konverziu na mieste. Nevracajú nič.

Aktuálny argument je uložený v zväzku; členmi sú:

  • IS_STRING: arg1->value.str.val

  • IS_LONG: arg1->value.lval

  • IS_DOUBLE: arg1->value.dval

Manažment pamäte vo funkciách

Všetka pamäť, ktorú funkcia potrebuje by sa mala vyhradiť buď s emalloc() alebo estrdup(). Toto sú abstraktné funkcie manipulujúce s pamäťou, ktoré vyzerajú ako normálne funkcie malloc() a strdup(). Pamäť by sa mala uvoľniť s efree().

V tomto programe sú dva druhy pamätí: pamäť, ktorá je vrátená do parsera v premennej a pamäť, ktorú potrebujete pre dočasné uloženie vo vašej internej funkcii. Keď priraďujete reťazec k premennej, ktorá sa vracia do parsera, musíte sa uistiť, že pamäť najprv vyhradíte buď s emalloc() alebo estrdup(). Túto pamäť by ste NIKDY nemali uvoľniť vy, pokiaľ neskôr v tej istej funkcii neprepíšete svoje pôvodné priradenie (avšak tento typ programovacej techiky nie je dobrý).

Pre hocijakú dočasnú/trvalú pamäť, ktorú potrebujete vo svojich funkciách/knižnici, by ste mali použiť tri funkcie emalloc(), estrdup() a efree(). Správajú sa PRESNE ako ich opačné funkcie. Všetko, čo emalloc()'ujete alebo estrdup()'ujete, musíte na niektorých miestach efree()'ovať, pokiaľ kód do konca neuviazne; inak pamäť pretečie. Významom "funkcie sa správajú presne ako ich opačné funkcie" je: ak efree()'ujete niečo, čo nebolo emalloc()'ované alebo estrdup()'ované, môže sa objaviť zlyhanie segmentácie. Takže sa prosím o to postarajte a uvoľnite celú zbytočnú pamäť.

Ak kompilujete s "-DDEBUG", tak po dokončení špecifického skriptu, PHP vypíše zoznam všetkej pamäte, ktorá bola vyhradená použitím emalloc() a estrdup(), ale ktorá nebola nikdy uvoľnená s efree().

Nastavovanie premenných v Tabuľke premenných

Existuje množstvo makier, ktoré uľahčujú nastavenie premennej v tabuľke symbolov:

  • SET_VAR_STRING(name,value)

  • SET_VAR_DOUBLE(name,value)

  • SET_VAR_LONG(name,value)

Varovanie

Buďte opatrný s SET_VAR_STRING. Časť hodnoty musí byť malloc'ovaná manuálne, pretože kód pamäťového manažmentu sa neskôr pokúsi tento ukazovateľ uvoľniť. Nepresúvajte staticky vyhradenú pamäť do SET_VAR_STRING.

Tabuľky symbolov v PHP sú implementované ako tabuľky hash. V hocakom danom čase je &symbol_table ukazovateľom 'hlavnej' tabuľky symbolov a active_symbol_table ukazuje na teraz aktívnu tabuľku symbolov (tieto môžu byť identické ako pri štarte, alebo rôzne, ak ste vo vnútri funkcie).

Následujúce príklady používajú 'active_symbol_table'. Mali by ste ju nahradiť s &symbol_table, ak chcete špecificky pracovať s 'hlavnou' tabuľkou symbolov. Rovnako aj rovnaké funkcie sa môžu použiť na polia, ako je vysvetlené nižšie.

Príklad F-3. Kontrola, či $foo existuje v tabuľke symbolov

if (hash_exists(active_symbol_table,"foo",sizeof("foo"))) { existuje... }
else { neexistuje }

Príklad F-4. Hľadanie veľkosti premennej v tabuľke symbolov

hash_find(active_symbol_table,"foo",sizeof("foo"),&pvalue);
check(pvalue.type);
Polia sú v PHP implementované použitím rovnakých hash tabuliek ako tabuliek symbolov. To znamená, že dve uvedené funkcie sa tiež dajú použiť na kontrolu premenných vo vnútri polí.

Ak chcete definovať nové pole v tabuľke symbolov, mali by ste urobiť následujúce.

Najprv by ste mali zistiť, či existuje a vhodne ju zrušiť, a to použitím hash_exists() alebo hash_find().

Ďalej, inicializujte pole:

Príklad F-5. Inicializovanie nového poľa

pval arr;
  
if (array_init(&arr) == FAILURE) { zlyhal... };
hash_update(active_symbol_table,"foo",sizeof("foo"),&arr,sizeof(pval),NULL);
Tento kód deklaruje nové pole, nazvané $foo, v tabuľke symbolov. Toto pole je prázdne.

Tu je uvedené ako sa do neho pridávajú položky:

Príklad F-6. Pridávanie položiek do nového poľa

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

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

/* definuje dalsie volne miesto v $foo[],
 * $foo[8], aby bol 5 (pracuje ako php2)
 */
hash_next_index_insert(arr.value.ht,&entry,sizeof(pval),NULL);
Ak by ste chceli zmeniť hodnotu, ktorú ste vložili do hashu, najprv by ste ju z hashu museli obnoviť. Aby ste sa vyhli prebytku, môžete poskytnúť pval ** do prídavnej funkcie hash a ona sa aktualizuje s pval * adresou vloženého elementu v hashi. Ak je tá hodnota NULL (ako vo všetkých horeuvedených príkladoch) - parameter sa ignoruje.

hash_next_index_insert() používa viac-menej tú istú logiku ako "$foo[] = bar;" v PHP 2.0.

Ak budujete pole na vrátenie z funkcie, môžete inicializovať pole práve tak ako sa to urobilo vyššie:

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

...a potom pridaním hodnôt s pomocníkom funkcií:

add_next_index_long(return_value,long_value);
add_next_index_double(return_value,double_value);
add_next_index_string(return_value,estrdup(string_value));

Samozrejme, ak sa pridanie neurobí hneď po inicializácii poľa, pravdepodobne sa budete musieť najprv postarať o pole:
pval *arr;
  
if (hash_find(active_symbol_table,"foo",sizeof("foo"),(void **)&arr)==FAILURE) { nemoze najst... }
else { pouzi arr->value.ht... }

Všimnite si, že hash_find zachytáva ukazovateľ do pval ukazovateľ, a nie pval ukazovateľ.

Vlastne akákoľvek hash funkcia vracia SUCCESS alebo FAILURE (okrem hash_exists(), ktorá vracia pravdivú hodnotu booleanu).

Vrátenie jednoduchých hodnôt

Je k dispozícií veľa makier, aby sa vrátenie hodnôt z funkcie uľahčilo.

Všetky makra RETURN_* nastavujú vrátenú hodnotu a vrátia ju z funkcie:

  • RETURN

  • RETURN_FALSE

  • RETURN_TRUE

  • RETURN_LONG(l)

  • RETURN_STRING(s,dup) Ak je dup TRUE, reťazec sa reprodukuje

  • RETURN_STRINGL(s,l,dup) Vráti reťazec (-ce) stanovenej dĺžky (l).

  • RETURN_DOUBLE(d)

Makra RETVAL_* nastavujú vrátenú hodnotu, ale nevrátia ju.

  • RETVAL_FALSE

  • RETVAL_TRUE

  • RETVAL_LONG(l)

  • RETVAL_STRING(s,dup) Ak je dup TRUE, reťazec sa reprodukuje

  • RETVAL_STRINGL(s,l,dup) Vráti reťazec (-ce) stanovenej dĺžky (l).

  • RETVAL_DOUBLE(d)

Tieto horeuvedené reťazcové makra estrdup()'ujú vložený argument 's', takže môžete bezpečne uvoľniť argument po volaní makra, alebo po prípade použiť staticky vyhradenú pamäť.

Ak vaša funkcia vracia odozvy v podobe úspech/chyba, vždy použite RETURN_TRUE a RETURN_FALSE jednotlivo.

Vrátenie komplexných hodnôt

Vaša funkcia môže tiež vrátiť komplexný dátový typ ako je objekt alebo pole.

Vrátenie objektu:

  1. Zavolajte funkciu object_init(return_value).

  2. Doplňte hodnoty. Funkcie slúžiace na tento účel sú vypísané nižšie

  3. Prípadne, pre tento objekt zaregistrujte funkcie. Aby ste získali hodnoty objektu, funkcia by "ich" mala vybrať z active_symbol_table. Jej typ by mal byť IS_OBJECT, a je to pôvodne platná hash tabuľka (t.j., môžete použiť platné hash funkcie na .value.ht). Registrácia funkcie sa dá urobiť použitím:
    add_method( return_value, function_name, function_ptr );

Funkcie používané na obsadenie objektu sú:

  • add_property_long( return_value, property_name, l ) - Pridá vlastnosť nazvanú 'property_name', typu long, rovnú 'l'

  • add_property_double( return_value, property_name, d ) - To isté, len pridá double

  • add_property_string( return_value, property_name, str ) - To isté, len pridá reťazec

  • add_property_stringl( return_value, property_name, str, l ) - To isté, len pridá reťazec dĺžky 'l'

Vrátenie poľa:

  1. Zavolajte array_init(return_value).

  2. Doplňte hodnoty. Funkcie slúžiace na tento účel sú uvedené nižšie.

Funkcie používané na obsadenie poľa sú:

  • add_assoc_long(return_value,key,l) - pridá associatívnu položku s kľúčom 'key' a hodnotou long 'l'

  • add_assoc_double(return_value,key,d)

  • add_assoc_string(return_value,key,str,duplicate)

  • add_assoc_stringl(return_value,key,str,length,duplicate) - špecifikuje dĺžku reťazca

  • add_index_long(return_value,index,l) - pridá položku indexu 'index' s hodnotou long 'l'

  • add_index_double(return_value,index,d)

  • add_index_string(return_value,index,str)

  • add_index_stringl(return_value,index,str,length) - špecifikuje dĺžku reťazca

  • add_next_index_long(return_value,l) - pridá položku poľa do ďalšieho voľného offsetu s hodnotou long 'l'

  • add_next_index_double(return_value,d)

  • add_next_index_string(return_value,str)

  • add_next_index_stringl(return_value,str,length) - špecifikuje dĺžku reťazca

Použitie zoznamu zdrojov

PHP má štandardný spôsob zaobchádzania s rôznymi typmi zdrojov. Toto nahrádza všetky miestne linkované zoznamy v PHP 2.0.

Dostupné funkcie:

  • php3_list_insert(ptr, type) - vracia 'id' novo vloženého zdroja

  • php3_list_delete(id) - odstráni zdroj s určeným id

  • php3_list_find(id,*type) - vracia ukazovateľ zdroja s určeným id, aktualizuje 'type' na typ zdroja

Typicky sa tieto funkcie používajú pre SQL ovládače, ale dajú sa použiť pre hocičo iné; napríklad na zachovávanie popisovačov súborov.

Typický zoznam kódu by vyzeral takto:

Príklad F-7. Pridanie nového zdroja

RESOURCE *resource;

/* ...vyhradi pamat pre zdroj a ziska zdroj... */
/* prida novy zdroj do zoznamu */
return_value->value.lval = php3_list_insert((void *) resource, LE_RESOURCE_TYPE);
return_value->type = IS_LONG;

Príklad F-8. Použitie existujúceho zdroja

pval *resource_id;
RESOURCE *resource;
int type;

convert_to_long(resource_id);
resource = php3_list_find(resource_id->value.lval, &type);
if (type != LE_RESOURCE_TYPE) {
	php3_error(E_WARNING," index %d zdroja ma nespravny typ",resource_id->value.lval);
	RETURN_FALSE;
}
/* ...pouzi zdroj... */

Príklad F-9. Odstránenie existujúceho zdroja

pval *resource_id;
RESOURCE *resource;
int type;

convert_to_long(resource_id);
php3_list_delete(resource_id->value.lval);
Typy zdroja by mali byť registrované v php3_list.h, v list_entry_type. Okrem toho, ku kažkému definovanému typu zdroja by sa mal pridať kód vypnutia, v list_entry_destructor() v súbore list.c (dokonca aj keď nemáte čo urobiť pri vypnutí, musíte pridať prázdny prípad).

Použitie trvalej tabuľky zdroja

PHP má štandardný spôsob ukladania trvalých zdrojov (t.j, zdroje, ktoré sa uchovávajú medzi zásahmi). Prvý modul, ktorý mal používať túto vlastnosť bol modul MySQL, a následne mSQL, takže pri čítaní mysql.c môžete nadobudnúť dojem, aký trvalý zdroj by sa mal použiť. Funkcie, na ktoré by ste sa mali pozrieť sú:

php3_mysql_do_connect
php3_mysql_connect()
php3_mysql_pconnect()

Všeobecný význam trvalých modulov je tento:

  1. Nakódujte celý svoj modul tak, aby pracoval so zoznamom platných zdrojov, ako bolo spomenuté v sekcii (9).

  2. Nakódujte extra prepojenia na funkcie, ktoré zistia, či už zdroj existuje v zozname trvalých zdrojov. Ak áno, zaregistrujte ho tak ako v zozname platných zdrojov ako ukazovateľ na zoznam trvalých zdrojov (pretože zvyšok kódu by mal pracovať okamžite). Ak nie, tak ho vytvorte, pridajte ho do zoznamu trvalých zdrojov A pridajte naň ukazovateľ zo zoznamu platných zdrojov, takže by celý kód fungoval, pretože je v zozname platných zdrojov, ale na druhom prepojení by sa zdroj našiel v zozname trvalých zdrojov a používal by sa bez potreby znovuobnovenia. Tieto zdroje by ste mali zaregistrovať ako rozličné typy (t.j. LE_MYSQL_LINK pre dočasný odkaz a LE_MYSQL_PLINK pre trvalý odkaz).

Ak si prečítate mysql.c, zistíte, že okrem komplexnejších funkcií spojenia, sa nič, čo zostalo v module, nesmie zmeniť.

Úplne také isté rozhranie existuje pre zoznam platných zdrojov a zoznam trvalých zdrojov, iba 'list' sa nahradil s 'plist':

  • php3_plist_insert(ptr, type) - vracia 'id' novo vloženého zdroja

  • php3_plist_delete(id) - odstraňuje zdroj so špecifickým id

  • php3_plist_find(id,*type) - vracia ukazovateľ zdroja so špecifickým id, aktualizuje 'type' na typ zdroja

Ale je viac ako pravdepodobné, že tieto funkcie by sa pre vas preukázali ako zbytočné, keď by ste sa pokúšali implementovať trvalý modul. Niekto by typicky chcel využiť fakt, že zoznam trvalých zdrojov je v skutočnosti hash tabuľka. Napríklad, v moduloch MySQL/mSQL, keď sa volá pconnect() (trvalé prepojenie), funkcia vybuduje reťazce z host/user/passwd, ktoré sa funkcii predali, a hashuje SQL odkaz s týmto reťazcom ako kľúč. Ďalej, niekto zavolá pconnect() s rovnakým host/user/passwd, vygeneruje sa rovnaký kľúč a funkcia vyhľadá SQL odkaz v trvalom zozname.

Kým sa pozriete na ďalej zdokumentované, mali by ste sa pozrieť na mysql.c alebo msql.c, aby ste videli, ako by sa schopnosti hash tabuľky plistu mali použiť.

Jedna dôležitá poznámka: zdroje, ktoré idú do zoznamu trvalých zdrojov, sa *NESMÚ* vyhradiť s pamäťovým manažérom PHP, t.j., NEMALI by sa vytvárať s emalloc(), estrdup(), atď. Radšej by sa mali použiť regulérne malloc(), strdup(), atď. Dôvod je jednoduchý - na konci požiadavky (konci zásahu), sa každá pamäťová informácia, ktorá bola vyhradená pomocou pamäťového manažéra PHP, vymaže. Nakoľko trvalý zoznam nie je na to, aby sa na konci požiadavky vymazal, manažér pamäte PHP sa nesmie používať na vyhradenie zdrojov, ktoré do neho idú..

Keď registrujete zdroj, ktorý bude v trvalom zozname, mali by ste k nemu pridať deštruktorov v dočasnom aj v trvalom zozname. Deštruktor by nemal v dočasnom zozname deštruktora urobiť nič. Ten v trvalom zozname deštruktora by mal vhodne uvoľniť akékoľvek zdroje získané tým typom (t.j. pamäť, SQL odkazy, atď). Práve tak ako s dočasnými zdrojmi, *MUSÍTE* ku každému zdroju pridať deštruktora, aj keď nevyžaduje žiaden a keby bol aj prázdny. Pamätajte, nakoľko emalloc() a 'spol.' sa nemajú používať spoločne s trvalým zoznamom, ani tu nesmiete použiť efree().

Pridani runtime konfiguračných direktív

Mnoho vlastností PHP sa môže konfigurovať v runtime. Tieto konfiguračné direktívy sa môžu zjaviť buď v určenom php3.ini súbore alebo, v prípade Apache modulu, v Apache .conf. súboroch. Ich výhodou v Apache .conf súboroch je, že sa dajú konfigurovať na pre-adresárovom základe. To znamená, že jeden adresár môže mať napríklad jasný safemodeexecdir, zatiaľ čo druhy môže mať iný. Táto konfiguračná diskrétnosť je užitočná, hlavne keď sever podporuje členité virtuálne hosty.

Kroky nutné na pridanie novej direktívy:

  1. Pridajte štruktúru do php3_ini_structure v mod_php3.h.

  2. V main.c, editujte funkciu php3_module_startup a vhodne pridajte cfg_get_string() alebo cfg_get_long() volanie.

  3. Pridajte direktívu, vymedzenie a komentár do štruktúry php3_commands v mod_php3.c. Všimnite si časť vymedzení. RSRC_CONF sú direktívy, ktoré môžu byť prítomné len v Apache .conf súboroch. Direkívy OR_OPTIONS môžu byť prítomné všade, vrátane normálnych .htaccess súborov. files.

  4. Buď v php3take1handler() alebo php3flaghandler(), pridajte vhodnú položku pre vašu direktívu.

  5. V konfiguračnej sekcii funkcie _php3_info() v functions/info.c, musíte pridať svoju novú direktívu.

  6. A nakoniec, samozrejme musíte niekde svoju novú direktívu použiť. Bude adresovateľná ako php3_ini.directive.