Jak psát kód: Inicializujte proměnné

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

Vzhledem k tomu, že proměnné není třeba v PHP deklarovat, tak je někdo ani neinicializuje. Jde o zásadní chybu, která může způsobit vážné problémy. PHP sice nabízí částečnou ochranu v podobě chybové úrovně E_NOTICE, ta ale nevaruje před přidáním prvku do neinicializovaného pole. Na to doplatíme třeba v následujícím kódu:

<?php
foreach ($results as $result) {
    $item['id'] = $result['id'];
    foreach ($result['children'] as $key => $child) {
        $item['children_ids'][$key] = $child['id'];
    }
    // Tady něco uděláme s $item.
}
?>

První iterace proběhne v pořádku, některé další možná taky, PHP neoznámí žádnou chybu. Ale pokud má prvek míň dětí než kterýkoliv předchozí, tak do něj prosáknou děti z předchozích prvků. Chybu lze opravit přidáním $item = array(); na začátek iterace a ideálně i $item['children_ids'] = array();. Pro nalezení této chyby je vhodné použít externí nástroj. K této chybě může dojít v mnoha variantách, častá je např. také tato:

<?php
function setFromData(array $data) {
    $this->name = $data['name'];
    if (!empty($data['enabled'])) {
        // V opačném případě zůstane $this->enabled původní hodnota. Správně by se mělo přiřadit false.
        $this->enabled = true;
    }
}
?>

Přijďte si o tomto tématu popovídat na školení Bezpečnost PHP aplikací (15.6.2016, Praha).

Jakub Vrána, Dobře míněné rady, 29.5.2013, diskuse: 13 (nové: 0)

Diskuse

Patrik Šíma:

+1. Inicializace proměnných patří k základům dobrého programování.

v6ak:

Lze tu chybu v setFromData nějak strojově detekovat. Podle článku bych skoro řekl, že ano, ale IMHO to je dost velký problém. Stroj nemá moc možností, jak vhodně odhadnout, jestli je toto chování záměr.

Samozřejmě, chybu bych řešil řádkem <?php $this->enabled = !empty($data['enabled']); ?>.

ikona Jakub Vrána OpenID:

Tuto chybu skutečně detekovat nelze (pokud je $this->enabled deklarováno).

v6ak:

Díky za odpověď.

Mimochodem, http://www.vrana.cz/php-initialized/ to nedetekuje ani bez deklarovaných enabled a name. Je to záměr, nebo chyba?

Vím, že u dynamicky typovaných jazyků to není úplně snadné, ale aspoň u $this by to šlo...

ikona Jakub Vrána OpenID:

php-initialized kontroluje bohužel jenom proměnné. Vlastnosti objektů ani klíče pole nekontroluje.

Michal:

Škoda, mám jeden velký projekt, na který bych to hned pustil. Ale život jde dál :-)

Lawondyss OpenID:

Ahoj Jakube,
chtěl jsem se tě zeptat, jaký je tvůj názor na "inicializaci" vlastností ve třídě.
Může se nechat
<?php
class Example {
  public
$foo;
}
?>
nebo je lepší
<?php
class Example {
  public
$foo = NULL;
}
?>

ikona Adam Klvač:

Není v tom rozdíl, hodnotu NULL bude mít vlastnost foo v obou případech. Pokud vím, nelze toto ani detekovat (jestli už byla přiřazena hodnota, i kdyby byla NULL).

ikona Jakub Vrána OpenID:

Hodnotu null vlastnostem explicitně nepřiřazuji, protože je implicitní.

František Musil:

Stalo by mozna za zminku, ze kazda neinicializovana promenna zpomaluje kod. Provadel jsem na jednoducchy test a ve vysledku to na vetsim kodu to nejaka ta ms je (byt jde o jednotky). Nemluve o tom, ze je kod citelnejsi a clovek se pak dopousti mene chyb. Dale odpadaji problemy jako: isset($data) && !empty($data). Nicmene jeste jedna zkusenost: pokud ladujete viceurovnove pole a je to v cyklech tak zamotane, ze by inicializace znamenala podminku, tak se to casove neoplati a prehledne to taky neni. PHP pro stesti staci, kdyz bude vedet, ze zakladni promenna je array. Nicmene pak riskujete vyse zminene.

Diky za moznost sem napsat neco hodnotneho :-)

František Musil:

Mel bych souvisejici dotaz. Doporucujete vychozi hodnoty deklarovat primo pri inicializaci (konkretne array()) nebo v constructoru? Pripadne proc.

ikona Jakub Vrána OpenID:

Rovnou v deklaraci: private $data = array(). Je z toho hned vidět typ dat, navíc se ušetří řádek. Kdysi jsem někde četl, že není vhodné takhle inicializovat obří hodnoty, protože PHP zabere paměť dvakrát, ale obří hodnoty neinicializuji. Navíc už to neplatí – od PHP 5.3 to zabere stejně paměti a v PHP 5.2 to je dokonce naopak.

František Musil:

Dekuji :-)

Vložit příspěvek

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2016 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.