Vyřešení circular reference a registrace eventu

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
akadlec
Člen | 1326
+
0
-

Na radu Filipa (tímto mu velice děkuji za jeho čas) jsem si v DI extension upravil přidání události kterou chci aby se spustila a nevyvolala circular reference error.

$builder->getDefinition('application')
	->addSetup('$self = $this; $service->onRequest[] = function () use ($self) { $self->getService(?); }', array($this->prefix('onRequestHandler')))

Služba onRequest handler pak obsahuje magickou metodu __invoke která provede požadované operace. Problém je ale v tom že se to při téhle definici nespustí. Aby se provedl invoke musím to upravit takhle:

$builder->getDefinition('application')
	->addSetup('$self = $this; $service->onRequest[] = function () use ($self) { call_user_func($self->getService(?)); }', array($this->prefix('onRequestHandler')))

a když to samo volám takto tak se nepředají ty parametry co by se normálně předaly (Nette\Application\Application atd.) ale to mě až tak nevadí.

Co dělám špatně? Je to postaveno na nette 2.1.dev

Filip Procházka
Moderator | 4668
+
0
-

Ty přece tu metodu __invoke() na té službě vůbec nevoláš. Máš problém na úrovni PHP, né Nette.

Když přidáváš event ručně, v Compiler Extension

$builder->getDefinition('application')
    ->addSetup('$self = $this; $service->onRequest[] = ...', array($this->prefix('onRequestHandler')))

tak by tohle fungovalo, ale vzniká ti možnost circural reference. Všimni si že předáváš do eventu instaci objektu, který je sám callable. Když se tedy zavolá event, tak event spouští tento tvůj callback.

$self->getService(?);

Ty jsi ale udělal jen to, že jsi službu vytáhl, ale už jsi nijak neinvokoval, tedy se __invoke() nemohlo zavolat, protože invoke v ObjectMixin se aplikoval na closuru, nikoliv na tvoji službu

function () use ($self) { $self->getService(?); }

Naproti tomu, když tam přidáš ještě to call_user_func, tak už se tvoje služba nejen vytvoří, ale i se invokne, protože když do call_user_func předáš objekt, tak ti nad ním zkouší zavolat __invoke

function () use ($self) { call_user_func($self->getService(?)); }

Jenže tomu pořád něco chybí, nepředávají se argumenty :) Takhle už je to kompletní:

function () use ($self) { call_user_func_array($self->getService(?), func_get_args()); }

No a nebo, pokud si chceš tohle všechno ušetřit můžeš prostě použít kdyby/events :)

akadlec
Člen | 1326
+
0
-

Mno to sem tak nějak pochopil, proto jsem si tam hodil to call_user_func aby se ten call provedl ;) no ale právě jak si mě v issue nasměroval tímto směrem tak sem čekal nějakou magii na straně nette ;)

Jinak kdyby/events zvažuji že tam asi hodím pro ušetření zbytečných problémů ;)

Filip Procházka
Moderator | 4668
+
0
-

No call_user_func, jak jsme si ukázali ale nestačí. S tím func_get_args to už ale funguje, ne? :)

V Nette na to žádná magie není, nejspíš proto že to nikdo moc často nepotřebuje.

akadlec
Člen | 1326
+
0
-

no mělo by to fungovat ;) jako já tam ty argumenty nepotřebuju ;)
chapu, dostal jsem se do sfér kde málo kdo něco takového kutí no ;)