Vyhodnocení Nette\DI\Statement v rozšíření
- TomasG
- Člen | 23
Ahoj,
narážíme na problém, který se týká validace konfigurací v Nette
rozšířeních.
Problém spočívá jednoduše v tom, že dané rozšíření validuje, jestli
předaná hodnota je nějakého typu. Nicméně my potřebujeme danou hodnotu
nějakým způsobem získat (například z ENVu), či upravit (například
přetypovat) tak použijeme v neonu zápis pro funkci:
fooExtension:
bar: ::getenv(BAR)
V tento moment nám aplikace při generování DI containeru spadne, pokud rozšíření foo obsahuje takovouto validaci:
class FooExtension extends CompilerExtension {
public function loadConfiguration() {
$config = $this->>getConfig();
Validators::assertField($config, 'bar', 'string');
}
}
… jelikož hodnota bar obsahuje instanci objektu Nette\DI\Statement . Nabízí se námitka, že ENV proměnné by se měly nasetovat jako parametry, respektive dynamické parametry v Configuratoru a následně je takto používat. Nicméně se nám zde stejně objevuje problém s přetypováním. Pokud by rozšíření validovalo hodnotu bar jako boolean , tak by při pouhém předání parametru aplikace znova spadla, jelikož ENV proměnné obsahují pouze stringy (0|1), takže je potřeba hodnotu přetypovat na boolean. Například takto:
fooExtension:
bar: ::boolval(%env.BAR%)
A zde se opět dostáváme k tomu, že se nám do bar dostane instance Nette\DI\Statement . Jak řešit tento problém obecně, když spousta rozšíření jsou od třetí strany a obsahují tyto validace?
Nabízí se možnost “naučit” object Statement se vyhodnotit. Samozřejmě vyhodnotit by se dalo jen to, co je možné. Pokud by například Statement obsahoval volání nějaké služby, tak by to mělo vyhodit výjimku, protože není možné volat službu v compile time. A nějakým specifickým zápisem v neonu by se “řeklo”, že právě tento Statement se má po zkompilování rovnou vyhodnotit. Tedy například něco takového:
fooExtension:
bar: ::boolval('1') # vytvoří Nette\DI\Statement
baz: !::boolval('1') # vytvoří Nette\DI\Statement, který se ihned vyhodnotí a výsledek se použije jakožto hodnota
Samozřejmě ten zápis může být jiný, vykřičník jsem použil jen jako příklad. Teoreticky by to mohlo fungovat bez BC breaku, jelikož výchozí chování by bylo zachováno. Je toto dobrý/správný způsob, jak by se tento problém dal řešit, či se nabízí nějaký lepší a čistější způsob?
- David Grudl
- Nette Core | 8205
Řekl bych, že v reálu se tímto způsobem použijí asi jen funkce getenv, constant, defined a přetypování. Nebo ne?
Pokud by šlo o tyto funkce, klidně bych jim dal nativní obdoby, které by
se vykonaly už při kompilaci. Něco jako je not()
.
Koneckonců zrovna funkce boolval, intval atd. nejsou úplně ideální, bylo
by lepší mít jejich alternativy, které ověří, že hodnotu vůbec lze
přetypovat (např. abc
nebo 10.3
nejde na int, naopak
12
jde na int, atd.)
- TomasG
- Člen | 23
Většinou si asi člověk vystačí s těmito funkcemi a přetypováním. Ale napadá mě například použití, jaké máme my:
http:
proxy: ::explode(',', ::getenv(TRUSTED_PROXIES))
Tady to zrovna hezky projde a ve výsledném Containeru je:
$service->setProxy(explode(',', getenv('TRUSTED_PROXIES')));
Ale kdyby tam někde byla nějaká validace, že se musí jednat o pole, či
pole stringů, tak už to neprojde. Navíc my třeba nepoužíváme nativní
getenv()
funkci, ale vlastní, která to hledá a vrací z pole
$_ENV
. Při načítaní ENVů totiž nepoužíváme
putenv()
.
Jinak mimo to, původně jsme se chtěli dostat k tomu, aby ve výsledném
Containeru nebyly už finální hodnoty, ale aby tam bylo přímo volání
getenv()
(té naší) a i to přetypování etc.
Abychom mohli změnit ENV proměnné a nemazat cache Containeru… ale pak to
zase padá na těch různých validacích napříč rozšířeními.
- David Grudl
- Nette Core | 8205
Přidal jsem do masteru podporu pro bool()
, int()
,
float()
, string()
a společně s not()
se
vykonají v compile-time, pokud je to možné.
- David Grudl
- Nette Core | 8205
TomasG napsal(a):
Většinou si asi člověk vystačí s těmito funkcemi a přetypováním. Ale napadá mě například použití, jaké máme my:
Ale kdyby tam někde byla nějaká validace, že se musí jednat o pole, či pole stringů, tak už to neprojde. Navíc my třeba nepoužíváme nativní
getenv()
funkci, ale vlastní, která to hledá a vrací z pole$_ENV
. Při načítaní ENVů totiž nepoužívámeputenv()
.
Ale ona tam validace je. Pomocí schématu,
konkrétně proxy
je
Expect::anyOf(
Expect::arrayOf('string'),
Expect::string()->castTo('array')
)->default([])
->dynamic()
a právě to dynamic()
říká, že to může být parametr
vyhodnocený až za běhu a pak se také validuje. Zkus to udělat tímto
způsobem, je to fakt lepší.
- TomasG
- Člen | 23
David Grudl napsal(a):
Přidal jsem do masteru podporu pro
bool()
,int()
,float()
,string()
a společně snot()
se vykonají v compile-time, pokud je to možné.
Super, (y)
Aha, ono to totiž běží na Nette 2.4, ne 3, takže to co jsem psal nebylo
možná úplně relevantní. Pokud mě tedy validace pustí pomocí
dynamic()
dál, když se bude jednat o Statement
a ne
žádný primitivní typ, tak potom není co řešit a vyřeší se to budoucím
přechodem na Nette 3 :)
Díky moc!