Napovídání skalárních typů
V PHP 5 je možné v deklaraci funkce uvést u objektových parametrů třídu, ze které mají být tyto objekty vytvořeny. Totéž lze provést i s polem, s ostatními datovými typy ale ne a ani se to nechystá. Sara Golemon připravila PECL rozšíření params, které dovoluje typ parametrů určit v těle funkce zavoláním např. list($string, $int, $bool) = params_parse("sd|b");
.
Mě napadlo jiné řešení – typ parametrů se dá určit v dokumentačních komentářích, se kterými umí pracovat třída Reflection (v PHP 4 by se daly najít ručně). Tyto komentáře by se daly využít i pro kontrolu nebo převod typu skutečně předaných parametrů.
Řešení bych si představoval tak, že na začátku funkce zavolám prosté check_params()
, což se postará o vše potřebné. Pro řešení jsem chtěl použít „konstantu“ __FUNCTION__ a funkci func_get_args, která vrací pole parametrů předaných funkci. S konstantou __FUNCTION__ je problém ten, že vrací název aktuálně zavolané funkce a i když by se místo funkce check_params()
vkládal třeba soubor check_params.inc.php
, tak konstanta bude uvnitř tohoto souboru prázdná, protože se vyhodnocuje už při kompilaci. S funkcí func_get_args je zase problém ten, že vrací parametry hodnotou, i když byly předané referencí.
První problém jsem vyřešil díky funkci debug_backtrace, druhý problém nakonec tak, že se místo volání funkce vloží soubor, který může přímo nastavovat proměnné funkce, do které je vložen.
<?php // check_params.inc.php $_backtrace = debug_backtrace(); $_reflection = ($_backtrace[1]["class"] ? new ReflectionMethod($_backtrace[1]["class"], $_backtrace[1]["function"]) : new ReflectionFunction($_backtrace[1]["function"])); $_function = ($_backtrace[1]["class"] ? $_backtrace[1]["class"] . "::" : "") . $_backtrace[1]["function"]; preg_match_all("~^[ \t]*\\*[ \t]*@param[ \t]+(\\S+)[ \t]+\\\$(\\S+)~m", $_reflection->getDocComment(), $_matches, PREG_SET_ORDER); for ($_i = 0; $_i < func_num_args(); $_i++) { if (!isset($_matches[$_i])) { // missing doc comment } elseif (in_array($_matches[$_i][1], array("boolean", "bool", "integer", "int", "float", "double", "string", "object", "null"))) { settype($$_matches[$_i][2], $_matches[$_i][1]); } elseif ($_matches[$_i][1] == "mixed") { // do nothing } elseif ($_matches[$_i][1] == "callback") { if (!is_callable($$_matches[$_i][2])) { trigger_error("$_function(): Argument '" . $_matches[$_i][2] . "' should be a valid callback", E_USER_WARNING); return false; } } elseif ($_matches[$_i][1] == "resource") { if (!is_resource($$_matches[$_i][2])) { trigger_error("$_function(): Argument '" . $_matches[$_i][2] . "' is not a valid resource", E_USER_WARNING); return false; } } elseif ($_matches[$_i][1] == "array") { if (!is_array($$_matches[$_i][2])) { trigger_error("$_function(): Argument '" . $_matches[$_i][2] . "' must be an array, " . gettype($$_matches[$_i][2]) . " given", E_USER_ERROR); return false; } } else { // class if (get_class($$_matches[$_i][2]) != $_matches[$_i][1]) { trigger_error("$_function(): Argument '" . $_matches[$_i][2] . "' must be an instance of " . $_matches[$_i][1] . ", " . (is_object($$_matches[$_i][2]) ? "instance of " . get_class($$_matches[$_i][2]) : gettype($$_matches[$_i][2])) . " given", E_USER_ERROR); return false; } } } /** Ukázka použití check_params.inc.php * @param int testovací parametr * @return null výpis testovacího parametru s převedeným typem */ function check_params_test($i) { if (!(include "./check_params.inc.php")) { return false; } var_dump($i); // int(5) } check_params_test("5"); ?>
Kód se snaží chovat stejně jako vestavěné funkce – skalární typy bez řečí převede, u neshody typů callback, resource, array a u objektů vyvolá chybu a vrátí false. Volající funkce může tento stav ošetřit podle vlastního úsudku.
K řešení problému by se dalo přistoupit ještě jedním způsobem, to popíšu zase příště.
Diskuse
finc:
Je to sice hezke, ale videl bych problem ve vykonu a "ukecaneho kodu".
Pokud PHP nema typovou kontrolu, tak se s tim bohuzel stejne moc nenadela.
Jedine, co napadlo me, je obalit zakladni typy vlastnimi objekty, ktere pak mohu definovat u parametru metody.
Integer => int
String => text
Date => datum (asi nejzasadnejsi)
...
Mohu tim samozrejme ziskat i dalsi vlastnosti, jako equals, ruzne check metody, atd.
Jinak komentare jsou dobre parsovany i v PHP IDE (Eclipse). Muze to alespon pomoci pri automatickem doplnovani kodu.

dgx:
Jelikož PHP není nativní ale čistě skriptovací jazyk, byl by přístup k základních typům jakožto k objektům vůbec tím nejlepší. Potřebovalo by to ale podporu ze strany jazyka, tj. abych takové objekty mohl vytvářet přes literály a použivat na ně operátory. Takže: Sara Golemon, go go!> Jakub: je docela vtipné, jak se silně typové jazyky snaží zavádět netypové vychytávky a slabě typové jazyky opak :-)
Jakub Vrána
:
Za tímto účelem je k dispozici extenze http://www.php.net/manual/en/book.spl-types.php.


optik:
pro datum a čas je v php core objekt DateTime http://maetl.coretxt.net.nz/datetime-in-php
optik:
Pořád se snaží někdo z PHP dělat co není, jak bylo v ZWS "PHP is weakly typed and will remain so", type hinting u polí a objektů plus mít takové věci v PECL je dobrý kompromis, navíc skoro pokaždé při zavádění podobných věcí do jazyka klesne výkon, vsadím se že se tak stane i pro LSB a jmenných prostorů.
Diskuse je zrušena z důvodu spamu.

