Míra standardizace

Školení, která pořádám

V PHP existují obraty, které se snadno dají vyřešit v user-landu, byť často ne úplně přímočaře. Spousta lidí ale volá po jejich integrování do jazyka, často nejen kvůli pohodlnějšímu způsobu zápisu, ale také kvůli požadavku standardizace těchto obratů.

Příkladem takového obratu jsou pojmenované parametry funkcí, které se dají řešit např. takto:

<?php
/** Ukázka vlastního řešení pojmenovaných parametrů
* @param array předané parametry
* @return null vypíše předané parametry
*/
function named_args($args) {
    $default_args = array('arg1' => 1, 'arg2' => 2);
    $args += $default_args;
    print_r($args);
}
named_args(array('arg1' => 3));
?>

Kromě mírně krkolomného volání a nutnosti ručního zpracování parametrů jsou další nevýhody také zřejmé: potenciální nejednotnost řešení v různých projektech, pracné kontrolování předání povinných parametrů, chybějící návaznost na dokumentační nástroje, atd. Ze stejného soudku je i ruční převod parametrů funkcí na určitý typ VS type hinting.

Problém stejné kategorie se nedávno řešil i ve vývojářské konferenci. Pokud existuje magická metoda __call sloužící k přetěžování metod, neměla by zároveň existovat i metoda __iscallable nebo podobná? Tím pádem by bylo možné volat standardní funkci method_exists. Řešení v PHP je opět jednoduché, buď odděleně pro každý objekt nebo implementací jednotného interface:

<?php
interface iCallable {
    public function iscallable($function);
}

class OverloadedClass implements iCallable {
    function __call($function, $arguments) {
        switch ($function) {
            case 'divide': return $arguments[0] / $arguments[1]; break;
            default: throw new Exception('Invalid function');
        }
    }
    function iscallable($function) {
        return ($function == 'divide');
    }
}

$o = new OverloadedClass;
if ($o->iscallable('divide')) {
    echo $o->divide(6, 2);
}
?>

Nevýhody jsou prakticky stejné jako u vlastní implementace pojmenovaných parametrů: nepohodlnost a nejednotnost.

Ze stejného soudku je i vlastní implementace funkce ifsetor. Tam se sice krkolomnost použití nezvýší, zato přibude vedlejší efekt.

Osobně jsem spíše zastáncem integrování složitějších obratů přímo do jazyka, i když to povede k nižší jednoduchosti jeho pochopení. Např. přetěžování metod jsem v praxi nikdy nepoužil, ale když už v jazyce je, mělo by být kompletní. Stejně tak používání pojmenovaných parametrů by mohlo zprůhlednit např. předávání pravdivostních parametrů, o kterém jsem dříve psal.

Jakub Vrána, Ze zákulisí, 24.3.2006, diskuse: 8 (nové: 0)

Diskuse

PAtrik:

Poprve sa Vam chcem podakovat za skvelu stranku, vela som sa naucil. Mal by som ale otazku. Nepochopil som vyznam overloadingu v PHP. Podla mna je najlogickejsie riesenie je moznost nastavit pre kazdu vlastnost triedy setter a getter, ale takto tie vlastnosti musim skryvat aby neboli pristupne len cez __get a __set. Samozrejme to potom je neprehladne a podla mna proti zasad OOP. Takisto nechapem __call. Preco by som volal taku metodu triedy (presnejsie objektu) ktory neexistuje. Ked uz napisem do tej metody __call nejaky kod pre nejaku metodu tak to mozem hned aj to metody, nie? Dakujem za odpoved, a len tak dalej.

ikona Jakub Vrána OpenID:

Overloading je hezky použit např. v Zend Frameworku, viz http://www.phparch.com/zftut/index.php?p=1. Obecně se dá použít všude tam, kde množina vlastností nebo metod není pevná. Mám-li např. objekt pro řádek z databázové tabulky a chci dovolit získávat nebo měnit hodnoty jednotlivých sloupců, je overloading dobrý nástroj, protože dopředu nevím, jaké sloupce v objektu budou:

<?php
// klasický přístup
echo $row->column["id"];

// přetěžování
echo $row->id;
?>

Implementace je v tomto příkladě jednoduchá, v __get() a __set() bude práce s polem vlastností.

ikona llook:

Zrovna u těch getterů a setterů je často stejný kód třeba pro deset metod. Například:
<?php
public function setFoo($value) {
    $this->fields['foo'] = $value;
    $this->modified['foo'] = true;
}
?>
Proti OOP mi připadá spíše Copy&Paste, tak oblíbené v Javě (zejména u POJO). Overloading je velkým pomocníkem při odstraňování duplicitního kódu.

Více OO jazyky nabízí ještě jedno řešení. Například Javascript:
foo = new Object();
foo.novaMetoda = function() { alert('Nová metoda'); }
foo.novaMetoda();

V PHP máme sice Runkit, ale to přecejen není ono.

Martin:

Myslím, že to jde udělat i v PHP. Jen se musí přidat 1 krok navíc.

<?php
class A {}

$a = new A();
$a->novaMetoda = create_function('','echo "Nova metoda"; ');
$b = $a->novaMetoda;

$b();    // funguje
$a->novaMetoda(); //fatal error
?>

ikona llook:

Případně <?php call_user_func($a->novaMetoda); ?> by mělo fungovat. Zkoušet to nebudu, ale AFAIK to vytvoří jen funkci, nikoli metodu (nebude mít proměnnou $this).

ikona dgx:

create_function() vytvoří prostřednictvím eval() novou fci s unikátním názvem a tento název vrátí. Proto $a->novaMetoda = create_function() znamená, že do proměnné $a->novaMetoda se uloží název nově vytvořené globální fce, nic víc.

Miloš Brecher:

Vytvářet globální funkce dynamicky za běhu funkcí eval() mě přijde za hranou, ale možná se jenom zbytečně bojím.

Olda:

Z c++ ukazovat na promenou neni totez jako ji volat ;)

Diskuse je zrušena z důvodu spamu.

avatar © 2005-2024 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.