Autor Zpráva
Joker
Profil
Autor: Joker
Odkaz na aktuální verzi textu: Základní kurz 16: „Zapovězené“ funkce
zveřejněno

Odkud se vede diskuse k aktuálnímu stavu textu:
První příspěvek po poslední revizi textu

Dodělal jsem k základnímu kurzu ještě „dodatkovou“ kapitolu na téma „jak neprogramovat“.
Smyslem je informovat začátečníky, že pokud v nějaké staré učebnici, příkladu, atd. tyhle věci uvidí, aby je nepoužívali.

Jako obvykle uvítám komentáře a doplnění.
Alphard
Profil
Já bych jednotlivé kapitoly seřadil obráceně. Třeba goto, které máš na prvním místě, je realtivní novinka od 5.3 a jeho použití v návodech vidím opravdu zcela výjimečně. Je to jen vysvětlování, že začátečníci nemají používat něco, co téměř jistě neznají.
Naopak mysql_* a další z konce seznamu jsou na každém kroku, takže by to byla první věc, před kterou bych varoval.
Jan Tvrdík
Profil
Navíc správně použité goto vede ke zpřehlednění kódu, je to obvykle to nejlepší, co PHP má k dispozici pokud se programátor rozhodne nepoužít výjimky. Viz např. problém popsaný v mém 8 let starém dotazu.

Srovnej následující tři řešení stejného problému:

// klasické řešení
if (!problémA) {
    if (!problémB) {
        if (!problém C) {
            akce();
            foreach (...) {
                if (!problémD) {
                    if (!problémE) {
                        if (!problém F) {
                            akce2();
                        } else {
                            $chyba = problémF;
                            break;
                        }
                    } else {
                        $chyba = problémE;
                        break;
                    }
                } else {
                    $chyba = problémD;
                    break;
                }
            }
            akce3();
        } else {
            $chyba = problémC;
        }
    } else {
        $chyba = problémB;
    }
} else {
    $chyba = problémA;
}

if (isset($chyba)) {
    echo $chyba;
}


// řešení s DO-WHILE(FALSE)
do {
    if (problémA) {
        $chyba = problémA;
        break;
    }

    if (problémB) {
        $chyba = problémB;
        break;
    }

    if (problémC) {
        $chyba = problémC;
        break;
    }

    akce();

    foreach (...) {
        if (problémD) {
            $chyba = problémD;
            break 2;
        }
    
        if (problémE) {
            $chyba = problémE;
            break 2;
        }
    
        if (problémF) {
            $chyba = problémF;
            break 2;
        }

        akce2();
    }

    akce3();

} while (false);

if (isset($chyba)) {
    echo $chyba;
}



// řešení s GOTO
if (problémA) {
    $chyba = problémA;
    goto zpracováníChyb;
}

if (problémB) {
    $chyba = problémB;
    goto zpracováníChyb;
}

if (problémC) {
    $chyba = problémC;
    goto zpracováníChyb;
}


akce();

foreach (...) {
    if (problémD) {
        $chyba = problémD;
        goto zpracováníChyb;
    }

    if (problémE) {
        $chyba = problémE;
        goto zpracováníChyb;
    }

    if (problémF) {
        $chyba = problémF;
        goto zpracováníChyb;
    }

    akce2();
}

akce3();

zpracováníChyb: {
    if (isset($chyba)) {
        echo $chyba;
    }
}
Joker
Profil
Alphard:
Já z nějakého důvodu byl názoru, že goto je v PHP už dlouho. I proto ten oddíl je napsaný ve smyslu, že to je jakýsi pozůstatek.
V tom případě bych možná část o goto úplně vypustil staré příklady s ním nejsou a že by se to začalo masově používat neočekávám.

Jinak ty mysql_* jsou na konci proto, že to beru jako trochu jiný druh problému, než ty ostatní. I když je otázka, jestli to opravdu jiný druh problému je, možná není.

Jan Tvrdík:
Navíc správně použité goto vede ke zpřehlednění kódu
To se občas tvrdí, ale podle mě se to začátečník jen málokdy naučí tak, aby to kód skutečně zpřehledňovalo, a po čase si už zvykne programovat bez goto.

Srovnej následující tři řešení stejného problému
Já bych asi preferoval jiné klasické řešení:

$chyba = problemA ? KOD_A : KOD_OK;

if (($chyba == KOD_OK) && problemB) 
  $chyba = KOD_B;

if (($chyba == KOD_OK) && problemC) 
  $chyba = KOD_C;

if ($chyba == KOD_OK) {
  akce();
  
  for ($i = 0; ($chyba == KOD_OK) && …) {
    if (problemD) 
      $chyba = KOD_D;
      
    if (problemE) 
      $chyba = KOD_E;
        
    if (problemF) 
      $chyba = KOD_F;
      
    if ($chyba == KOD_OK) {
      akce2();
    }
  }
}

if ($chyba != KOD_OK) {
  echo $chyba;
}

Je to jednak kratší, než ty ostatní, ale hlavně to podle mě je přehlednější.
lionel messi
Profil
Joker:
Zdravím,

myslím si, že podobný článok môže začiatočníkov odradiť od niektorých najhorších návykov a do koncepcie učebnice sa mi hodí.

Možno by som doplnil problematické a (aspoň z môjho pohľadu) neveľmi užitočné superglobálne pole $_REQUEST (o jeho problémoch písal napríklad J. Vrána). Nedávno som v nejakej pomerne novej anglickej knižke o PHP videl niekoľko príkladov, kde bolo toto pole používané na prístup k premenným z formulárov namiesto $_GET/$_POST (nespomeniem si na názov, v prípade záujmu dohľadám).

Taktiež by som zvážil zmienku o už odstránenej direktíve http_long_arrays a s ňou súvisiacim superglobálnym poľom $HTTP_*_VARS, hoci s príkladom používajúcim túto direktívu som sa v aktuálnych zdrojoch ešte nestretol.
snazimse
Profil
Joker

Hodně dobrý článek, super!
No magic_quotes_gpc je z toho určitě největší šílenost. S tím bylo tolik problémů.
Tomáš123
Profil
Joker:
Chcel by som sa opýtať na funkciu eval(). Aj po prečítaní príslušného oddielu v článku mi stále nie je jasné, prečo je táto funkcia tak nebezpečná. Prečo stále upozorňujete na jej nepoužívanie najmä začiatočníkov? Aké problémy môžu nastať vplyvom jej nesprávneho použitia?

K článku nemám čo povedať, ale myslím, že táto otázka s ním okrajovo súvisí a po vyjasnení by sa tam mohli lepšie špecifikovať nevýhody používania tejto funkcie.
snazimse
Profil
Tomáš123:

Myslím, že je to tím, že může sloužit jako dobrý přístup dovnitř webu.Uživatel tam může cokoliv vložit viz:článek.
tiso
Profil
ad premenné premenné, čo toto:
list($controller, $method, $params) = $router->match();
$c = new $controller();
$c->$method($params);
či:
foreach($this->observers as $obs) {
    $obs->update($this);
}
Kubo2
Profil
tiso:
Až na to, že v tom príklade s poslucháčmi premenné premenné vôbec nepoužívaš. :-)
Chro.
Profil
V článku chybějí zmínky o zapovězených funkcích ereg, eregi, ereg_replace a eregi_replace.
EDIT: A o mém oblíbeném while(true) :)
Joker
Profil
Upravil jsem článek:

Ad [#2], [#3], odstranil jsem část o goto ([#4]: „V tom případě bych možná část o goto úplně vypustil staré příklady s ním nejsou a že by se to začalo masově používat neočekávám.“)

lionel messi [#5]: O existenci $_REQUEST se v základním kurzu mluví, možná bych přidal spíš zmínku tam. Navíc jsem ho viděl používat jen sporadicky.
edit: Přidal jsem zmínku do kapitoly o polích.

Ad $HTTP_*_VARS: „hoci s príkladom používajúcim túto direktívu som sa v aktuálnych zdrojoch ešte nestretol.“ - no právě, ani já ne. Asi nebude potřeba na to upozorňovat.

Tomáš123:
Aj po prečítaní príslušného oddielu v článku mi stále nie je jasné, prečo je táto funkcia tak nebezpečná.

Popis jsem rozšířil. Je to teď lepší?

Chro.:
V článku chybějí zmínky o zapovězených funkcích ereg, eregi, ereg_replace a eregi_replace.

Přidal jsem je k části o Mysql (která se tím změnila spíš na „zastaralá rozšíření“).
juriad
Profil
Joker:
Trochu bych se snažil čtenáře přesvědčit, že dokumentace je ten správný zdroj informací (na rozdíl od různých rádoby zkušených blogů na webu). Zkušení uživatelé, pokud si nejsou jistí, se jsou podívat do oficiální dokumentace.
Připiš také poznámku, že oficiální dokumentace často obsahuje varování ohledně používání zavržených nebo nebezpečných funkcí a rozhodně by se neměly brát na lehnou váhu. Jiné funkce (addslashes) varování v rámečku neobsahují, ale je obsaženo v samotném textu.
Tomáš123
Profil
Joker:
Je to teď lepší?
Áno, teraz rozumiem, nebezpečenstvu. Keď ale nahliadneme do oficiálnej dokumentácie, vidíme príklad, kde mi príde použitie funkcie eval() úplne zbytočné. Ak by pochádzali dve premenné definované na začiatku od užívateľa, vieme rovnakého efektu dosiahnuť (iba) použitím úvodzoviek pri definovaní premennej $str. Vlastne by ju ani nebolo treba definovať...

Načo teda toľko nebezpečenstva okolo? Kedy je použitie tejto funkcie naozaj potrebné?
Alphard
Profil
Tomáš123 [#14]:
Někdo by eval() mohl chtít použít třeba na vyhodnocení matematického výrazu místo toho, aby správně použil matematický parser.
Uživatel má třeba zadat 2*(4+5), tento výraz se má (bezpečně) vyhodnotit pomocí eval(), jenže pak přijde zlý uživatel a vloží tam kód např. copy('http:...', 'hack.php'). Pokud se mu to podaří zapsat tak, aby nezpůsobil syntax error, funkce to vykoná, nahraje na server útočníkův php script a ten získá plný přístup k webu.

Kromě bezpečnostního hlediska to často ukazuje na špatný návrh. Umím si představit třeba nějaké helpery uložené v databázi, které chci dynamicky vykonávat. Ale ve chvíli, kdy mám nějaký kód mimo prostor, kam mi dosáhne IDE, mám při refaktoringu problém :-)


Joker:
Ano, je to lepší, aspoň dle mého názoru. Hezké.
Jan Tvrdík
Profil
Tomáš123:
Funkce eval má i jiné než bezpečností problémy, např. je praktické nemožné debuggovat chybu, která při provádění evalu nastane.

Kedy je použitie tejto funkcie naozaj potrebné?
Když potřebuješ spustit dynamicky generovaný kód z paměti. Hodí se to např. na dynamické sestavování tříd za běhu, což používají mockovací nástroje. Normální programátor (tm) to nepotřebuje vůbec nikdy a pokud má pocit, že to potřebuje, tak téměř určitě má špatně navrženou aplikaci a celý problém je řešitelný lépe.


Joker:
Reakce na „Svého času jsem se dokonce snažil přijít na nějaký scénář, který by nějak ospravedlnil použití proměnných proměnných. Na nic jsem nepřišel.“ z vedlejšího vlákna, kde nechci motat začátečníkům hlavu:
Hodí se to tam, kde bys normálně použil extract (příklad), ale je pro tebe kritická efektivita. Funkce extract totiž bohužel v PHP < 7.0.0 nepoužívá copy-on-write, takže spotřebuje děsně moc paměti, viz github.com/nette/nette/pull/1225 a 3v4l.org/e8psv
Jan Tvrdík
Profil
Jinak bych asi rovnou zmínil md5 a sha1 pro hashování hesel (pro jiné účely jsou většinou v pohodě). V době, kdy má endora i wedos k dispozici PHP 5.5 tak lze začít důrazněji doporučovat password_* funkce.
snazimse
Profil
Jan Tvrdík:

Co doporučuje, dneska používat? Bcrypt? Nebo jak jste uvedl doporučení funkce v php password_hash? Díky!
lionel messi
Profil
snazimse:
Nebo jak jste uvedl doporučení funkce v php password_hash?
Druhú menovanú funkciu. Bolo by vhodné sa v tomto vlákne striktne držať témy (vývoj učebnice PHP) a nerobiť z neho priestor na všeobecnú diskusiu.
Jan Tvrdík
Profil
snazimse:
password_hash ve výchozím používá bcrypt, je to vlastně jenom crypt s hezčím API.
snazimse
Profil
lionel messi
Jj určitě konec diskuze.

Jan Tvrdík:
Děkuji!

Vaše odpověď

Mohlo by se hodit

Ostrá verze učebnice běží na www.pehapko.cz.

Odkud se sem odkazuje


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: