Smějeme se s Delphi for PHP
Že je Delphi for PHP absolutní propadák, se už nějakou dobu ví. Prohra je to naprosto zasloužená. Borland (respektive jeho pobočka CodeGear) totiž vypustil produkt, kde prakticky nic nefungovalo. Zatímco Arthur už dal Delphi sbohem u první verze, já neodolal svodům CodeGearu a zkusil si nainstalovat docela čerstvou verzi 1.5. Mohu tak potvrdit, že ani v ní si aplikaci nespustíte! (to si nedělám srandu)
Je mi upřímně smutno z toho, kam to společnost, ke které jsem vzhlížel už jako dítě, kam to dotáhla. Byl to Borland, kdo dal světu IDE, kdo přišel s revolučním Turbo Vision a ustál i smrtící přechod na Windows. Sbohem a šáteček.
Ještě než jsem Delphi for PHP odinstaloval, zlákal mou pozornost zdrojový kód frameworku VCL for PHP (pro PHP 5), který má webové aplikace pohánět. Kdyby mě někdo v tu chvíli natáčel, zaznamenal by desetiminutový plynulý morphing od němého úžasu k hlasitému smíchu.
Ať se pobavíte taky:
- autoři neví, že PHP má konstrukci
foreacha zásadně iterují přeswhile(list($key, $val)=each($arr)(používalo se v PHP 3) - neví nic o statických metodách
- slabě tuší o privátních metodách
- naopak klíčové slovo
globalpoužívají s úspěchem (více než 70 výskytů) - autoři neznají konstanty tříd, vše deklarují na globální úrovni
- kód tříd je důkladně prošpikován HTML fragmenty, dosolen JavaScriptem a špetkou CSS
- tyto framenty mají občas syntax HTML, občas XHTML
- názvy CSS vlastností jsou psány někde velkými písmeny, jinde malými
- zdrojáky se jen hemží zakomentovaným kódem a různými poznámkami,
z nichž některé až zarážení svou triviálností:
//calls inherited constructor parent::__construct($aowner);Programátor nejspíš dlouho googlil, co ten tajemný kód znamená, že si to pak samou radostí poznamenal
- v kódu je použito
$HTTP_SERVER_VARS(zavrženo v PHP 4.1)
Fakt by mě zajímalo, který zoufalec by na tom vybudoval aplikaci
p.s. CodeGear neusnul na vavřínech a už ohlásil IDE pro Ruby on Rails nazvané 3rdRail. Připouštím, dělal jsem si z Rubystů občas srandu, ale tohle bych jim nikdy nepřál!
PHP triky: generování JavaScriptu
Výstupem PHP skriptu bývá nejčastěji HTML stránka. S jejím generováním na úrovni značek může pomoci třeba knihovna NHtml. Ale ať už výstup generujete jakkoliv, je třeba mít na paměti, že každý řetězec se musí ošetřit funkcí htmlSpecialChars. To ji staví do pozice téměř nejdůležitější funkce, škoda proto, že nemá kratší název. Ačkoliv i to se dá napravit:
function h($s) {
return htmlSpecialChars($s, ENT_QUOTES);
}
Nojo, ale co když potřebujeme ve stránce vygenerovat JavaScriptový kód? Jakou funkcí v něm ošetříme řetězce?
Je to s podivem, ale až do verze PHP 5.2.0 s touto eventualitou tvůrci
nepočítali. Žádnou funkci pro „escapování“ řetězců jazyk
nenabízel. Jako náhrada se občas používá addSlashes, jenže ta nevkládá lomítka
na všechna potřebná místa a navíc je závislá na nastavení direktivy
magic_quotes_sybase. Teprve s příchodem verze 5.2 přišla
spása nazvaná json_encode.
$foo = ...; // libovolný řetězec v UTF-8
echo '<script type="text/javascript">';
echo 'var foo = ', json_encode((string) $foo), ';';
echo 'alert(foo);';
echo '</script>';
Poznámka: pokud píšete v XHTML a obsah skriptu uzavíráte mezi značky
<![CDATA[ a ]]>, je třeba ještě zajistit, aby
se v textu skriptu neobjevila koncová značka. Ale lepší spíš bude se
těmto Xvěcem vyhnout.
Sice za několik dní už žádného správného vývojáře verze nižší
než 5.2.0 zajímat nebudou
, než k tomu však dojde, podělím se s vámi
o alternativní řešení. To umí nejen sanitizovat řetězce, ale je
plnohodnotnou náhradu funkce json_encode (potřebujete-li ošetřit pouze
řetězce, stačí vám část za if (is_string($val))):
if (!function_exists('json_encode')) { // since PHP 5.2.0
/**
* JSON encode
*
* @author David Grudl
* @copyright Copyright (c) 2007 David Grudl
*/
function json_encode($val)
{
// indexed array
if (is_array($val) && (!$val
|| array_keys($val) === range(0, count($val) - 1))) {
return '[' . implode(',', array_map('json_encode', $val)) . ']';
}
// associative array
if (is_array($val) || is_object($val)) {
$tmp = array();
foreach ($val as $k => $v) {
$tmp[] = json_encode((string) $k) . ':' . json_encode($v);
}
return '{' . implode(',', $tmp) . '}';
}
if (is_string($val)) {
$val = addslashes($val); // ' " \ NUL - due to bug #40915
return '"' . addcslashes($val, "\x8..\xA\xC\xD/") . '"';
}
if (is_int($val) || is_float($val)) {
return (string) $val;
}
if (is_bool($val)) {
return $val ? 'true' : 'false';
}
return 'null';
}
}
Tato funkce se oproti předloze liší v tom, že neodhalí rekurzivně zanořené pole nebo objekty. V případě polí by se jednalo o docela netriviální úkol a tudíž nejlepším řešením je ho neřešit vůbec.
PHP triky: standardní výjimky
Kromě základní třídy Exception disponuje PHP počínaje verzí 5.1.0 celou škálou dalších předpřipravených výjimek. Než si tedy vytvoříte novou třídu, zkuste se podívat, jestli by vám nevyhovovala některá z těchto:
- LogicException – předvídatelné již při návrhu programu
- BadFunctionCallException – chyba při volání funkce; funkce
nenalezena; volání nepovoleno
- BadMethodCallException – totéž pro metody
- InvalidArgumentException – špatný argument předaný funkci
- OutOfRangeException – index mimo rozsah pole či kolekce
- LengthException – hodnota překračuje povolenou délku
- DomainException – hodnota nespadá do požadované domény, rozsahu
- BadFunctionCallException – chyba při volání funkce; funkce
nenalezena; volání nepovoleno
- RuntimeException – zjistitelné pouze za běhu programu
- OverflowException – přetečení bufferu či aritmetické operace; více dat než očekáváno
- UnderflowException – podtečení bufferu či aritmetické operace; méně dat než očekáváno
- OutOfBoundsException – index mimo rozsah pole či kolekce
- RangeException – hodnota nespadá do požadovaného rozsahu
- UnexpectedValueException – neočekávaná hodnota (např. návratová hodnota funkce)
PHP triky: zjišťování ini_get
Interpretovat nastavení konfiguračních direktiv
nemusí být vůbec snadné. V případě logických hodnot sice dokumentace tvrdí, že se vždy vrací
1, 0 nebo prázdný řetězec, ale není tomu tak
v případě, že hodnotu nastavíme souborem .htaccess či httpd.conf a
direktivou php_value:
php_value magic_quotes_gpc On
V takovém případě ini_get vrátí přímo řetězec On.
Konfiguraci je proto lepší zjišťovat účelovou funkcí, jako je třeba
get_magic_guotes_gpc(). V případě, že žádná funkce
k dispozici není, můžete použít tuto:
/**
* Gets the boolean value of a configuration option
* @param string configuration option name
* @return bool
*/
function getDirective($directive)
{
$val = strtolower(ini_get($directive));
return $val === 'on' || $val === 'true'
|| $val === 'yes' || $val % 256;
}
if (getDirective('zend.ze1_compatibility_mode')) {
// zend.ze1_compatibility_mode is ON
}
PHP triky: include, require a cesty
Vkládání skriptů přes include nebo require
není vždy transparentní, protože programátoři často neví, z jakého
adresáře se skript načte. Pravidla jsou následující:
- pokud je cesta absolutní, nic se nedohledává
- pokud cesta začíná na
./nebo../, hledá se vůči aktuálnímu adresáři (neplést s adresářem s právě prováděným skriptem!) - v ostatních případech:
- prohledají se všechny cesty z
include_pathrelativně vztažené k aktuálnímu adresáři - hledá se od adresáře s právě prováděným skriptem
- prohledají se všechny cesty z
„Na jistotu“ lze obecně vkládat pouze přes absolutní cesty. K tomu se užívá tato formule:
require_once dirname(__FILE__) . '/libs/TexyBase.php';
Následující trik ukazuje, jak se lze volání funkce dirname zbavit:
require_once __FILE__ . '/../libs/TexyBase.php';
Aktualizace: díval jsem se do zdrojových kódů PHP 5.3 a objevila se tam
nová magická konstanta __DIR__, která nahradí volání
dirname(__FILE__).
pokračování...