RobotLoader má podporu filtrů (a jaké triky se s tím dají dělat)
- David Grudl
- Nette Core | 8227
Přidal jsem RobotLoaderu podporu pro filtry: tedy funkce, skrz které je možné zdrojový soubor prohnat. Asi si říkáte, k čemu takový nesmysl je :-) Má to docela cool potenciál!
Příklad č.1:
Potřebujete v rámci testování mockovat objekty a narazíte na problém, že třída je buď final nebo má final metody. V takovém případě nelze plnohodnotný mock vůbec vytvořit. Dokud tedy nepoužijeme filtry:
$loader = new RobotLoader;
// pro použití filtrů je potřeba nastavit druhý storage
$loader->setCacheStorage(new FileStorage(...), new PhpFileStorage(...));
// filtr pro *.php soubory, co z kódu odstraní všechny klíčové slova "final"
$loader->filters['php'] = function($input) {
$code = '';
foreach (@token_get_all($input) as $token) {
if (!is_array($token)) {
$code .= $token;
} elseif ($token[0] !== T_FINAL) {
$code .= $token[1];
}
}
return $code;
};
$loader->register();
V tuto chvíli bude RobotLoader načítat všechny třídy tak, že nebudou mít finální modifikátory a půjdou mockovat.
Pokud bychom chtěli takto načítat třídy přímo z Nette, je nutné RobotLoader předřadit NetteLoaderu, což se udělá parametrem TRUE:
$loader->register(TRUE);
Příklad č.2
V PHP se oproti JavaScriptu zatím nerozmohly kompilované jazyky, jako je například CoffeeScript, ale dle mého je to jen otázkou času. Jejich nasazení maximálně ulehčí RobotLoader, díky němuž můžete zcela transparentně kombinovat soubory napsané v PHP se soubory v jiném jazyce.
Pokud bych třeba chtěl psát v LadyPHP, přidám si do RobotLoaderu filter:
$loader->filters['lady'] = function($input) {
return Lady::parse($input);
};
A kteroukoliv třídu v rámci projektu mohu mít v souboru *.lady a vše bude fungovat. Soubory se budou automaticky za běhu kompilovat a překompilují se i při změně zdrojového souboru.
- juzna.cz
- Člen | 248
Supr!! Prakticky to same jsme s HosipLanem resili v PhpEhnancer, kde jsme ale
narazili na neprivetivost RobotLoaderu a vrtat se nam v nem nechtelo (tak jsme
pouzili PSR-0 loader z Composeru).
PS: Najdete tam take dalsi ukazky filteru, se kterymi jsme si hrali.
- Jan Tvrdík
- Nette guru | 2595
Nice! Tak kdo první napíše filtr, který bude z něčeho jako
<?php
class Foo
{
/**
* @var MyService
* @inject
*/
public $myService;
}
generovat něco jako
<?php
class Foo
{
/**
* @var MyService
*/
private $myService;
public function injectMyService(MyService $service)
{
$this->myService = $service;
}
}
- David Ďurika
- Člen | 328
Jan Tvrdík napsal(a):
Nice! Tak kdo první napíše filtr, který …
no predsa ten kto sa pyta :)
- David Grudl
- Nette Core | 8227
A co třeba filtr konvertující on-the-fly Nette pro PHP 5.3 do verze PHP 5.2!
- David Ďurika
- Člen | 328
David Grudl napsal(a):
A co třeba filtr konvertující on-the-fly Nette pro PHP 5.3 do verze PHP 5.2!
zbytocna strata casu… 5.2 by som uz uplne zavrhol…
- Honza Marek
- Člen | 1664
David Grudl napsal(a):
A co třeba filtr konvertující on-the-fly Nette pro PHP 5.3 do verze PHP 5.2!
Jakože RobotLoadet nebude v žádném namespacu?
- Jan Tvrdík
- Nette guru | 2595
Pokud to David nebude řešit systémově, tak bych prozatím s klidným
srdcem přepsal formatTemplateFiles()
v
BasePresenteru
.
- unu
- Člen | 2
Elijen napsal(a):
Co podpora$array = [1, 2, 3];
pro PHP 5.3 a starsi?
Zrovna dneska jsem to přidal do LadyPHP, tady je zjednodušená verze pro PHP: https://gist.github.com/3724437
- jansfabik
- Člen | 193
vvoody napsal(a):
Napadlo mě řešení. RobotLoader by soubory neincludoval přímo, ale přes
stream wrapper. Pak by se includnulo filter://{filename}
. PHP
skripty by tento stream proháněl filtrem, ostatní soubory by nechal tak,
jak jsou.
Vlastně by se ta věc dala úplně vyčlenit z RobotLoaderu a mohla by dostat výstižnější název, třeba FilterStream. Pak by se to dalo používat takhle:
<?php // bootstrap.php
require LIBS_DIR . '/Nette/loader.php';
$stream = new FilterStream('filter');
$stream->setCacheStorage(new FileStorage(...));
$stream->filters['php'] = function($input) { ... };
$stream->registrer();
require 'filter://' . APP_DIR . '/real-bootstrap.php';
<?php // real-bootstrap.php
$loader = new RobotLoader;
$loader->setCacheStorage(new FileStorage(...));
$loader->addDirectory('filter://' . APP_DIR);
$loader->register();
// ...
Vyřešilo by to veškeré problémy s voláním šablon a umožnilo filtrovat nejen PHP ale i jiné formáty. Dále by to usnadnilo běh PHP 5.3 kódu na PHP 5.2, protože bych si mohl nechat filtrovat i bootstrap.
Jen doufám, že by to moc nezhoršilo výkon. Nevím, jestli v takovém případě PHP cachuje bytecode.
Co si o tomto nápadu myslíte?
Editoval jansfabik (15. 9. 2012 20:38)
- jansfabik
- Člen | 193
Problém by mohl nastat případě, že je předán parametr s jiným typem. Má se provést přetypování nebo vyhodit výjimka?
Napadlo mě, že by se takto mohl generovat kód podle PHP doc tagů.
/**
* @param int {@required}
* @param string {@convert}
*/
public function foo($bar, $baz)
{
if (!is_int($bar)) {
throw new InvalidArgumentException('Parameter $bar must be an integer, ' . gettype($bar) . ' given.');
}
$baz = (string) $baz;
// ...
}
Ale mnohem čistější mi připadá vytvořit si na tohle makro v editoru.
- jtousek
- Člen | 951
Připadá mi vhodnější vyhodit výjimku a případné přetypování vynutit už při volání.
Makro v editoru? Jakože aby tyhle kousky kódu generovalo? To snad ne, filtr by byl skvělý v tom, že bys ty kousky neviděl.
Mimochodem by bylo nutné ten filtr napsat tak aby neměnil čísla řádků kvůli laděnce.
- Filip Procházka
- Moderator | 4668
@pekelnik: my to s @juzna už řešili, tohle byl ovšem chybějící článek, protože spousta lidí robota používá.
@jansfabik: streamy máme taky vyřešené.
Editoval HosipLan (22. 9. 2012 11:35)
- jansfabik
- Člen | 193
@Hosiplan: Super. Ještě bych to chtělo pár změn a myslím, že by to bylo použitelné:
- Ten stream wrapper by měl umět i pracovat se složkami, viz
manuál. Potom by se RobotLoader vůbec nemusel hackovat, jenom by se mu
před cestu ke složce přidalo
enhance://
. - Soubory, které nejsou s příponou .php by se nechaly jak jsou, což vyřeší problém se šablonami, viz výše.
- Přidalo by se nějaké cachování.
- unu
- Člen | 2
@jansfabik: Pokud by se ten stream wrapper napsal tak
jak říkaš a fungovaly by s ním všechny operace jako s běžný
souborovým systémem, proč už rovnou nenahradit defaultní wrapper
file://
? Potom by se to dalo použít v jakémkoli projektu
pouhým přidáním dvou řádku a nemusel by se řešit žádný autoloader.
Jen nevím jak velký vliv by to mělo na rychlost načítání souborů.
- jansfabik
- Člen | 193
Tak jsem si to napsal (tady se můžete podívat) a zjistil jsem, že PHP je hrozně zabugované.
- Pokud uvnitř funkce
url_stat()
zavolámfopen
, tak to vyhodí RuntimeException a PHP na konci skriptu skončí se Segmentation Fault. - Funkce
glob()
,realpath()
neumí se stream wrappery pracovat.
RobotLoader mi fungoval, když jsem v něm zakomentoval funkci realpath tady.
Navíc to ani vůbec rychlé. Vytvořil jsem si filtr, který nedělá nic a zkusil jsem přečíst všechny soubory frameworku.
$iterator = new RecursiveDirectoryIterator($netteDir);
$iterator = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::LEAVES_ONLY);
foreach ($iterator as $file) {
if ($file->isFile()) {
file_get_contents($file->getPathname());
}
}
Bez filtru mi tohle zabere asi 13ms, s filtrem 250ms. Když jsem k tomu přidal ještě cachování, tak to trvalo dokonce 400ms!
Stream wrappery proto asi nebudou tou správnou cestou. Spíš mi připadá lepší si při každém nahrání na server ty skripty předzpracovat.
- Milo
- Nette Core | 1283
Dost často je typehint v PHP zbytečný nebo se předává objekt kde typehint lze. Ale někde by se mi to vysloveně hodilo :-)
public function printMe(array|Traversable $fields, int $flags)
{
}
jtousek napsal(a):
@Milo: Nebylo by to moc pomalé?
Určitě by to vzalo nějaký čas, než by se „přegeneroval“ PHP kód souboru, ale pak by to vzalo stejně času, jako by se typehint dělal ručně. Jen mě napadlo, že by to šlo.
Také se s tím dá obejít omezení v PHP, kdy lze proměnným objektu přiřadit jen „skalární“ defaultní hodnotu.
Také se s tím dá obejít celé PHP ;-)
- jtousek
- Člen | 951
Milo napsal(a):
jtousek napsal(a):
@Milo: Nebylo by to moc pomalé?Určitě by to vzalo nějaký čas, než by se „přegeneroval“ PHP kód souboru, ale pak by to vzalo stejně času, jako by se typehint dělal ručně. Jen mě napadlo, že by to šlo.
Nemyslím to generování, to se přegeneruje jednou a pak se loaduje z cache, to je v pohodě. Zajímá mne o kolik zpomalí aplikaci ty přidané podmínky na začátku téměř každé funkce. – Když už bych to používal, tak všude.
Editoval jtousek (23. 9. 2012 20:52)
- redhead
- Člen | 1313
Mě se to všechno hrozně líbí. Fakt! Jedinej obrovskej problém (pro mě) je, že NB nebo kterýkoliv jiný normální IDE bude svítit celý červeně a nebo pokud někdo alespoň udělá obarvovač, tak ztratím code completion, hinty a všechno ostatní. Opravdu nejsem masochista, abych používal PSPad nebo Sublime Text. Tohle pro mě bude vždy překážkou. Sakra, ale jak já bych tohle tak rád používal.