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 je zrušena z důvodu spamu.