Vyšla první betaverze Nette DI 3.0 – pojďte ji otestovat!

David Grudl
Nette Core | 8215
+
+31
-

Právě vyšla první betaverze Nette DI 3.0. Je za ní měsíc velmi intenzivní práce, obrovský refaktoring, samozřejmě se snahou maximálně zachovat zpětnou kompatibilitu. A tu bych právě potřeboval otestovat.

  • Z pohledu uživatele by tato verze měla být 99.9% kompatibilní.
  • Z pohledu vývojáře CompilerExtension jsou drobné změny používáte-li generované továrny

Vždycky dávám enormní úsilí do zachování zpětné kompatibility. Tam, kde to nelze, generuje Nette co nejsmysluplnější chybové hlášky, aby přechod byl co nepříjemnější. Knihovny jsou velmi pečlivě pokryty testy. Nicméně i přes tohle všechno se stává a bude stávat, že něco přestane fungovat. Že vznikne nekompatibilita, která nebyla záměrná, nebo ji lze řešit lépe. Proto existují testovací verze. Od vás chci slyšet o každém problému. Takže je to o komunikaci, když místo ní budete nadávat, že „jsem něco rozbil“, nelze to brát na zřetel :-)

Nette DI prošlo obrovským a potřebným refactoringem, který otevírá úplně nové možnosti, jak lze DI kontejner vylepšovat. Obsahuje i řadu novinek, jako je autowiring polí služeb, multi-accessory a multi-továrny, apod, hodně jich je ještě v plánu, nicméně teď se chci věnovat spíše změnám v této betě.

Změny z pohledu uživatele

Z DI jsem odstranil několik věcí, které považuju za historické relikty, jako je podpora INI souborů, přímý zápis PHP kódu do konfigurace pomocí otazníků a možná něco dalšího. Pokud to potřebujete zachovat, dejte vědět. Při použití zápisu class: PDO(...) vás upozorní, že byste měli použít factory: PDO(...).

Změnou je, že nyní je vyžadována u každé služby znalost jejího typu – tj. oproti dřívějšku i u těch, co mají vypnuté autowired.

Změny z pohledu tvůrce Compiler Extensions

Tou hlavní je, že zatímco dosud interně popisoval každou službu objekt ServiceDefinition, dnes existuje definic vícero, tak například ImportedDefinition pro importované neboli dynamic služby, FactoryDefinition pro generované továrničky na základě inteface, AccessorDefinition pro generované accessory a LocatorDefinition pro multi-továrny a accessory.

Ukázka kódu pro DI 2.4:

		$builder->addDefinition($this->prefix('latteFactory'))
			->setImplement(Nette\Bridges\ApplicationLatte\ILatteFactory::class);
			->setFactory(Latte\Engine::class)
			->addSetup('setTempDirectory', [$this->tempDir])
			->addSetup('setAutoRefresh', [$this->debugMode]);

Jelikož $builder->addDefinition() vrací právě objekt ServiceDefinition, který nelze pro generované továrničky použít, zahlásí Nette chybu:

Nette\DI\Definitions\ServiceDefinition::setImplement() is deprecated, use $builder->addFactoryDefinition(...) or addAccessorDefinition(...)

případně

Nette\DI\Definitions\FactoryDefinition::setFactory() is deprecated, use ->getResultDefinition()->setFactory()

Řešením je mírně upravit kód extenze, tj. jednak nahradit addDefinition() za addFactoryDefinition() případně addAccessorDefinition(), a dále protože FactoryDefinition má trošku jiné API, ještě doplníme getResultDefinition() před definici vraceného objektu:

		$builder->addFactoryDefinition($this->prefix('latteFactory'))
			->setImplement(Nette\Bridges\ApplicationLatte\ILatteFactory::class)
			->getResultDefinition()
				->setFactory(Latte\Engine::class)
				->addSetup('setTempDirectory', [$this->tempDir])
				->addSetup('setAutoRefresh', [$this->debugMode]);

Jak vidíte, této změně neušla ani LatteFactory z nette/application.

Prosím o otestování

Díky!

hrach
Člen | 1838
+
+9
-

Už se snažím upgradovat Nextras Orm a Dbal, v podstatě nepoužívam nic krom ServiceDefinition, takže prakticky žádný problém, ale co mi přijde strašně nepohodlné je to, že metoda addDefinition je takový prasopes, který vrací jiný typ v PHP, jiný typ v PhpDoc, nemluvě o tom, že ještě něco vrací dynamicky na základě druhého argumentu.

Přimlouval bych se za to, aby se to udělalo nějak výrazně čistěji. Mj. kvůli tomu dost blbne PhpStorm, který pak neví, kterou definici metod na výsledném objektu člověk volá (Jestli na Definition, nebo na ServiceDefinition) a kvůli tomu taky zahodí deprecated příznak u metody setClass, byť je u obou.

Taky bych ocenil lepší PhpDoc – popis daných classes a metod, co je přidávají, co vlastně ty třídy dělají a umějí. U factory asi zůstal i omylem stejný popis ze service definition.

PS: najít tento thread chce taky vyšší skill. Chtělo by to někam připnout, přiznat si, že zrušené české sekce je nesmysl, …

Editoval hrach (6. 1. 2019 17:26)

Nick
Člen | 4
+
0
-

Chtěl bych se zeptat, jak to vypadá s vydáním Nette 3.0? Co jsem pochopil, tak 3.0 existuje teď pouze jako betaverze.
Nyní pracuji na 2.3 a chystám se na upgrade.
Vyplatí se teď upgradovat na 2.4 a potom později na 3.0, nebo si počkat?
Díky.

David Grudl
Nette Core | 8215
+
0
-

Určitě nejprve updatuj na 2.4.

David Grudl
Nette Core | 8215
+
0
-

hrach napsal(a):

Přimlouval bych se za to, aby se to udělalo nějak výrazně čistěji. Mj. kvůli tomu dost blbne PhpStorm, který pak neví, kterou definici metod na výsledném objektu člověk volá (Jestli na Definition, nebo na ServiceDefinition) a kvůli tomu taky zahodí deprecated příznak u metody setClass, byť je u obou.

Napadá tě, jak to řešit? Dynamické napovídání podle druhého parametru addDefinition() lze asi řešit přes .phpstorm.meta.php. Možnost je přidat metodu addServiceDefinition(), která vždycky vrátí ServiceDefinition (a odstranit @return u addDefinition). Ale v podstatě to znamená tlačit autory všech extensions, aby nahradili addDefinition() za addServiceDefinition()…

U factory asi zůstal i omylem stejný popis ze service definition.

Co konkrétně myslíš?

PS: najít tento thread chce taky vyšší skill. Chtělo by to někam připnout, přiznat si, že zrušené české sekce je nesmysl, …

Nějak to pořeším.

hrach
Člen | 1838
+
0
-

Cele to na mě působilo nepřehledně, no. Možná kdyby existovala addServiceDefinition(), nemusí se addDefinition nutně deprecatnout.

Co konkrétně myslíš?

Snažil jsem se přijít na rozdíl mezi jednotlivýma definitions, moc jsem to nechápal, o co jde. https://github.com/…finition.php#L18

David Grudl
Nette Core | 8215
+
0
-

Nemusí, jen pak zůstane stejná jako nyní, čímž se to co jsi psal neřeší. Nebo z ní odstraním ten @return a vznikne problém s napovídáním u současného kódu.

LukasV
Člen | 5
+
+1
-

David Grudl napsal(a):

Nemusí, jen pak zůstane stejná jako nyní, čímž se to co jsi psal neřeší. Nebo z ní odstraním ten @return a vznikne problém s napovídáním u současného kódu.

A co tam nechat tu původní addDefinition(), která by se chovala pořád stejně a jen přidat novou addServiceDefinition(). addDefinition() by byla deprecated a v nette 4 nebo nějaké jiné velké verzi by se odstranila?

A existuje nějaké doporučení, jak psát rozšíření kompatibilní jak s nette 2.4, tak s nette 3? Vyhodnocovat to třeba podle toho, jestli existuje metoda addFactoryDefinition() nebo je nějaký elegantnější způsob?

Václav Pávek
Backer | 100
+
0
-

Je znám termín kdy bude DI ve verzi RC?

Editoval Václav Pávek (17. 2. 2019 23:11)

David Grudl
Nette Core | 8215
+
+6
-

Měla by vyjít zítra.

h4kuna
Backer | 740
+
0
-

Je nějaká rozumná možnost podstrčit vlastní třídu, aby si to pamatovalo původní parametry, protože jsem nezměnil konstruktor?

Například chci upravit PresenterFactory. Třidu si podědím, nesáhnu na konstruktor, ale už mi nejde lehce nastavit parametry které se generují v rozšíření ApplicationExtension.

application.presenterFactory:
	create: My\Application\PresenterFactory
#	class: My\Application\PresenterFactory

Editoval h4kuna (19. 3. 2019 15:01)

Mysteria
Člen | 797
+
0
-

Jak se prosím v trojkové verzi dá zapsat registrace Latte filtrů přímo v configu? Na Nette 2.4 jsem používal tohle, ale to momentálně nefunguje:

services:
    nette.latteFactory:
        setup:
            - addFilter(NULL, [@App\Model\Utils\TemplateFilters(%wwwDir%), 'loader'])
Argument 1 passed to Nette\DI\Config\Processor::updateServiceDefinition() must be an instance of Nette\DI\Definitions\ServiceDefinition, instance of Nette\DI\Definitions\FactoryDefinition given, called in /var/www/project/vendor/nette/di/src/DI/Config/Processor.php on line 75

Zkoušel jsem všechno možné, ale nepodařilo se mi vyřešit tu nekompatibilitu ServiceDefinition vs FactoryDefinition…

Matey
Člen | 142
+
0
-

Ahoj, akurát sa to riešilo na slacku. Je to bug. David Grudl už pripravuje fix.

forgie
Bronze Partner | 18
+
0
-

Ahoj, V Nette 2.4. fungovalo napríklad toto:

public function loadConfiguration()
{
	$this->compiler->addConfig([ ... parameters ....]);

	$this->compiler->processParameters();

Jak toto dosáhnout pomocí DI 3.0? Není vůbec potřeba po $this->compiler->addConfig() volat $this->compiler->processParameters()? Nebo pokud ano, tak co to nahrazuje?

Nebo to nebylo potřeba už ani v Nette 2.4. :) ? Nakolik tam se to volalo i v \Nette\DI\Compiler::compile.

Díky

David Grudl
Nette Core | 8215
+
0
-

Co je cílem toho kódu?

forgie
Bronze Partner | 18
+
0
-

Účelem je přidat složky s databázovýma migracema z různých extenzí do \Nette\DI\Container::$parameters:

V Nette2.4 to bylo:

	private function loadMigrations()
	{
		$this->compiler->addConfig([
			'parameters' => [
				'migrations' => [
					$this->name => [
						__DIR__ . '/../migrations',
					],
				],
			],
		]);

		$this->compiler->processParameters();
	}

a to se pak načetlo v extenzi určené pro migrace (hodně zjednodušený kód ale pro představu stačí):

public function loadConfiguration()
{
	 ...
	$params['groups'] += $this->getContainerBuilder()->parameters['migrations']
	$builder->addDefinition($this->prefix('continueCommand'))
			->setClass(Etten\Migrations\Bridges\SymfonyConsole\ContinueCommand::class)
			->setArguments($params)
			->addTag('kdyby.console.command');
	...

Ale co jsem teď zkoušel v Nette 2.4. to funguje i bez volání $this->compiler->processParameters(), takže to $this->compiler->processParameters() asi v DI 3.0 stačí jen odmazat?

Editoval forgie (17. 4. 2019 12:40)

sorm
Člen | 8
+
0
-

Pokud došlo ke zrušení otazníků, jak teď přiřadím událost?

services:
  sessionHandler: App\Handlers\Sessionhandler
  user:
    setup:
      - "?->onLoggedIn[] = [?, 'onLoggedIn']"(@self,@sessionHandler)
      - "?->onLoggedOut[] = [?, 'onLoggedOut']"(@self,@sessionHandler)

Musím si udělat do příslušné třídy nějakou metodu, která bude sloužit pro nastavování událostí a tu volat místo přiřazení?

Zatím jsem to tedy pojal takto:

services:
  sessionHandler: App\Handlers\SessionHandler
  user:
      class: App\Security\User
      setup:
          - associateSessionHandler( @sessionHandler )

A uvedená metoda v přetížené třídě na uživatele prostě nastaví ty události, ale to předtím mi umožnilo nedělat třídu jen kvůli těm dvěma asociacím.

Editoval sorm (29. 4. 2019 22:27)

stanley89
Člen | 6
+
0
-

@sorm funguje mi toto:

services:
	someService: App\Services\SomeService
	user:
		setup:
			- '$onLoggedIn[]' = [@someService, onLoggedIn]

Je to vhodné řešení?

Marek Bartoš
Nette Blogger | 1260
+
0
-

Tyhle komplikované konfigurace s otazníky nemají v neonu co dělat, mělo by to být v CompilerExtension

Felix
Nette Core | 1195
+
0
-

Mabar napsal(a):

Tyhle komplikované konfigurace s otazníky nemají v neonu co dělat, mělo by to být v CompilerExtension

Osobne bych to nezatracoval. Za me uplne v poradku zapis.