phpFashion

Na navigaci | Klávesové zkratky

Rubrika Nette

Nette je 3. nejpopulárnější framework!

Nette se v anketě Best PHP Framework for 2015 pořádané magazínem SitePoint umístilo na úžasném 3. místě. Moc děkuji všem za hlasy, tak skvělý výsledek jsem opravdu nečekal.

Těší mě na tom, že uživatelé jsou s Nette nejspíš spokojení, jinak by asi hlasy neposílali. A také samozřejmě fakt, že Nette tím na sebe upozornilo ve světě, kde se o něm kvůli jazykové bariéře tolik neví.

Na výsledcích je zajímavé i to, že si člověk uvědomí, jak velké množství PHP frameworků se reálně používá, že existují i další populární „lokální frameworky“ a že je tu stále dost těch, kteří žádný framework nepoužívají.

před 6 lety v rubrice Nette


Nabušené DI srdce pro vaše aplikace

Jednou z nejzajímavějších částí Nette, kterou vychvalují i uživatelé jiných frameworků, je Dependency Injection Container (dále Nette DI). Podívejte se, jak snadno jej můžete použít kdekoliv, i mimo Nette.

Mějme aplikaci pro rozesílání newsletterů. Kód jednotlivých tříd jsem zjednodušil na dřeň. Máme tu objekt představující email:

class Mail
{
	public $subject;
	public $message;
}

Někoho, kdo ho umí odeslat:

interface Mailer
{
	function send(Mail $mail, $to);
}

Přidáme podporu pro logování:

interface Logger
{
	function log($message);
}

A nakonec třídu, která rozesílání newsletterů zajišťuje:

class NewsletterManager
{
	private $mailer;
	private $logger;

	function __construct(Mailer $mailer, Logger $logger)
	{
		$this->mailer = $mailer;
		$this->logger = $logger;
	}

	function distribute(array $recipients)
	{
		$mail = new Mail;
		...
		foreach ($recipients as $recipient) {
			$this->mailer->send($mail, $recipient);
		}
		$this->logger->log(...);
	}
}

Kód respektuje Dependency Injection, tj. že každá třída pracuje pouze s proměnnými, které jsme jí předali. Také máme možnost si Mailer i Logger implementovat po svém, třeba takto:

class SendMailMailer implements Mailer
{
	function send(Mail $mail, $to)
	{
		mail($to, $mail->subject, $mail->message);
	}
}

class FileLogger implements Logger
{
	private $file;

	function __construct($file)
	{
		$this->file = $file;
	}

	function log($message)
	{
		file_put_contents($this->file, $message . "\n", FILE_APPEND);
	}
}

DI kontejner je nejvyšší architekt, který umí stvořit jednotlivé objekty (v terminologii DI označované jako služby) a poskládat a nakonfigurovat je přesně podle naší potřeby.

Kontejner pro naši aplikaci by mohl vypadat třeba takto:

class Container
{
	private $logger;
	private $mailer;

	function getLogger()
	{
		if (!$this->logger) {
			$this->logger = new FileLogger('log.txt');
		}
		return $this->logger;
	}

	function getMailer()
	{
		if (!$this->mailer) {
			$this->mailer = new SendMailMailer;
		}
		return $this->mailer;
	}

	function createNewsletterManager()
	{
		return new NewsletterManager($this->getMailer(), $this->getLogger());
	}
}

Implementace vypadá takto, aby:

  • se jednotlivé služby vytvářely, až když je potřeba (lazy)
  • dvojí volání createNewsletterManager využívalo stále stejný objekt loggeru a maileru

Vytvoříme instanci Container, necháme ji vyrobit managera a můžeme se pustit do spamování uživatelů newslettery:

$container = new Container;
$manager = $container->createNewsletterManager();
$manager->distribute(...);

Podstatné na Dependency Injection je, že žádná třída nemá závislost na kontejneru. Tudíž jej můžeme klidně nahradit za jiný. Třeba za kontejner, který nám vygeneruje Nette DI.

Nette DI

Nette DI je totiž generátor kontejnerů. Instruujeme ho (zpravidla) pomocí konfiguračních souborů a třeba tato konfigurace vygeneruje cca totéž, jako byla třída Container:

services:
	- FileLogger( log.txt )
	- SendMailMailer
	- NewsletterManager

Zásadní výhodou je stručnost zápisu. Navíc jednotlivým třídám můžeme přidávat další a další závislosti často bez nutnosti do konfigurace zasahovat.

Nette DI vygeneruje skutečně PHP kód kontejneru. Ten je proto extrémně rychlý, programátor přesně ví, co dělá, a může ho třeba i krokovat.

Kontejner může mít v případě velkých aplikací desetitisíce řádků a udržovat něco takového ručně by už nejspíš ani nebylo možné.

Nasazení Nette DI do naší aplikace je velmi snadné. Nejprve jej nainstalujeme Composerem (protože stahování zipů je tááák zastaralé):

composer require nette/di

Výše uvedenou konfiguraci uložíme do souboru config.neon a pomocí třídy Nette\DI\ContainerLoader vytvoříme kontejner:

$loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp');
$class = $loader->load(function($compiler) {
	$compiler->loadConfig(__DIR__ . '/config.neon');
});
$container = new $class;

a pak jej opět necháme vytvořit objekt NewsletterManager a můžeme rozesílat emaily:

$manager = $container->getByType('NewsletterManager');
$manager->distribute(['john@example.com', ...]);

Ale ještě na chvíli zpět ke ContainerLoader. Uvedený zápis je podřízen jediné věci: rychlosti. Kontejner se vygeneruje jednou, jeho kód se zapíše do cache (adresář __DIR__ . '/temp') a při dalších požadavcích se už jen odsud načítá. Proto je načítání konfigurace umístěno do closure v metodě $loader->load().

Během vývoje je užitečné aktivovat auto-refresh mód, kdy se kontejner automaticky přegeneruje, pokud dojde ke změně jakékoliv třídy nebo konfiguračního souboru. Stačí v konstruktoru ContainerLoader uvést jako druhý argument true.

Jak vidíte, použití Nette DI rozhodně není limitované na aplikace psané v Nette, můžete jej pomocí pouhých 3 řádků kódu nasadit kdekoliv. Zkuste si s ním pohrát, celý příklad je dostupný na GitHubu.


Generování odkazů kupříkladu v emailech a Nette

V Nette je k dispozici nástroj LinkGenerator pro vytváření odkazů bez nutnosti použití presenterů a přitom stejně pohodlně. Jak ho použít?

Ukážeme si to na příkladu třídy, která odesílá emaily. Kód emailu může vypadat nějak takto: (všechny příklady jsou pro Nette 2.4 nebo vyšší)

<title>Subject of message</title>

<p>Hello {$name} <a n:href="Homepage:">click here</a></p>

Třídě předám LinkGenerator a ta si jej uloží do proměnné $this->linkGenerator.

class MailSender
{
	/** @var Nette\Application\LinkGenerator */
	private $linkGenerator;

	function __construct(Nette\Application\LinkGenerator $generator)
	{
		$this->linkGenerator = $generator;
	}

Samotné odesílání emailů bude provádět metoda sendEmail, která si vytvoří objekt Latte:

	function sendEmail()
	{
		$latte = new Latte\Engine;
		$latte->setTempDirectory(...);
		...
	}

U Latte je vhodné nastavit temp directory , aby se šablona s každým emailem nemusela znovu kompilovat, nicméně pokud jich posíláte jen několik denně, nebo třeba hodně, ale v jednom requestu (tj. pomocí jednoho objektu $latte), není to nutné, kompilace je blesková. Kde cestu k temp složce vzít? Můžeme si ji opět předat, ale chytřejší řešení je místo toho si předat objekt (tzv. továrničku), který dovede nakonfigurované Latte vyrobit:

	/** @var Nette\Bridges\ApplicationLatte\ILatteFactory */
	private $latteFactory;

	// $latteFactory opět předáme přes konstruktor

	function sendEmail()
	{
		$latte = $this->latteFactory->create();

		// nainstalujeme do $latte makra {link} a n:href
		Nette\Bridges\ApplicationLatte\UIMacros::install($latte->getCompiler());

		$params = [  // proměnné do šablony
			'name' => $order->getName(),
			...
		];
		// a vygenerujeme HTML email
		$html = $latte->renderToString(__DIR__ . '/email.latte', $params);

		// a odešleme jej, viz dále
	}

Tady ještě jednou přeruším, protože musím zmínit ještě jednu alternativu, a to nechat si vygenerovat přímo známý objekt $template, který bude obsahovat například proměnné $basePath apod, už s nakonfigurovaným Latte. Vyměním tedy latteFactory za templateFactory:

	/** @var Nette\Application\UI\ITemplateFactory */
	private $templateFactory;

	// $templateFactory si opět předáme přes konstruktor

	function sendEmail()
	{
		$template = $this->templateFactory->createTemplate();
		$template->name = $order->getName();
		$template->setFile(__DIR__ . '/email.latte');
	}

Jde o obdobu použití $this->createTemplate() uvnitř presenteru či komponenty.

Zbytek metody sendEmail bude vypadat takto:

		...
		$mail = new Nette\Mail\Message;
		$mail->addTo($order->email);
		$mail->setHtmlBody($html); // nebo setHtmlBody($template)

		$this->mailer->send($mail);	// $mailer si opět předáme konstruktorem
	}

A teď zbývá poslední krok! Zapojit do toho LinkGenerator, ke kterému se konečně dostávám. Je to snadné:

Tedy buď (pokud si předáváme latteFactory):

		$latte->addProvider('uiControl', $this->linkGenerator);

nebo (pokud jdeme cestou templateFactory):

		$template->getLatte()->addProvider('uiControl', $this->linkGenerator);

O té chvíle můžete používat makro {link} nebo n:href.

LinkGenerator generuje všechny URL absolutní, tedy včetně http://example.com. Taktéž na moduly pohlíží absolutně, tudíž v odkazech neuvádějte na začátku dvojtečku ani //, není to potřeba a vlastně tomu ani nebude rozumět.

Pokud v routeru používáte relativní cesty, což je velmi časné, generátor bere doménu z aktuálního HTTP requestu. Ale v CLI žádný HTTP request pochopitelně neexistuje. Lze jej však podstrčit třeba v config.neon:

services:
	application.linkGenerator:
		arguments:
			refUrl: Nette\Http\UrlScript('http://example.com')

Celý kód by vypadal tedy nějak takto:

<?php

use Nette\Application\LinkGenerator;
use Nette\Bridges\ApplicationLatte\ILatteFactory;
use Nette\Mail\IMailer;

class MailSender
{
	/** @var LinkGenerator */
	private $linkGenerator;

	/** @var ILatteFactory */
	private $latteFactory;

	/** @var IMailer */
	private $mailer;


	function __construct(LinkGenerator $generator, ILatteFactory $latteFactory, IMailer $mailer)
	{
		$this->linkGenerator = $generator;
		$this->latteFactory = $latteFactory;
		$this->mailer = $mailer;
	}

	function sendEmail()
	{
		$latte = $this->latteFactory->create();

		// nainstalujeme do $latte makra {link} a n:href
		Nette\Bridges\ApplicationLatte\UIMacros::install($latte->getCompiler());
		$latte->addProvider('uiControl', $this->linkGenerator);

		//$order = ...;
		$params = [
			'name' => $order->getName(),
			// ...
		];
		$html = $latte->renderToString(__DIR__ . '/email.latte', $params);

		$mail = new Nette\Mail\Message;
		$mail->addTo($order->email);
		$mail->setHtmlBody($html);

		$this->mailer->send($mail);
	}
}

Nette 2.3 bude trošku citlivka

Nette ve verzi 2.3 bude case sensitive. Proč a co to konkrétně znamená?

Case sensitivity, anglicky „citlivost na velikost písmen“, znamená, že například Homepage a homepage jsou dva naprosto rozdílné řetězce. PHP je citlivé na velikost písmen u proměnných, ignoruje ji u tříd a metod. JavaScript, C#, Ruby nebo XML jsou citlivé u všech identifikátorů, naopak HTML je case insensitive. Souborový systém na Linuxu je case sensitive, zatímco na Windows a Macu nikoliv. Atd.

Začínal jsem na jazycích, které nebyly na velikost písmen citlivé, a připadalo mi šílené, že některé jiné jsou. „To si jako u každého identifikátoru musím pamatovat, které písmenko je malé a které velké?“. Tohle byla lichá obava, funguje to stejně jako v přirozeném jazyce, prostě stačí znát, že vlastní jména se píší s velkým počátečním písmenem, nikoliv to, že jeden konkrétní člověk se jmenuje Franta a ne franta.

Nicméně mám rád pravidlo „buď velkorysý v tom, co přijímáš, a striktní v tom, co odesíláš“ a protože striktní lpění na citlivosti může být až komické (Nabla je automat), Nette bylo vůči velikosti písmen tolerantní.

Konkrétně šlo o

  • jména presenterů
  • některé parametry v cestě URL
  • názvy tříd u autoloadingu a DI kontejneru

Být tolerantní v prostředí, které je na velikost písmen citlivé, ba co hůř, které je citlivé v závislosti na operačním systému apod, přináší řadu úskalí. Například URL lišící se jen velikostí písmen mohou mít zcela odlišný obsah, atd. A přitom vlastně jen tolerujete, že někdo píše jak motejlek.

Proto od verze 2.3 bude Nette na velikost písmen citlivé.

A protože se Nette vždy snaží o co nejbezproblémovější přechod, bude chyby ve velikosti písmen detekovat a upozorní na ně.


Jak je to s release managementem Nette

Občas dostávám otázky, proč Nette nemá přesný harmonogram vydávání verzí, kde by bylo uvedeno, kdy přesně vyjde příští verze a jak dlouho bude podporovaná. Pokusím se vysvětlit, jak to v Nette funguje.

Podle mého je ideální vydávat ročně jednu až dvě nové větší verze. Rychlejší a zejména pomalejší vydávání způsobuje různé komplikace, a ony 1–2 verze ročně se ukázaly jako ideál.

Zároveň přibližně každých 6 týdnů vydávám opravné setinkové verze. Někdy je interval kratší, protože se objeví chyba, kterou je třeba opravit co nejdřív, jindy je delší, protože k vydání verze není důvod.

Podporu pro každou verzi se snažím držet dlouho:

  • Nette 0.9 vyšlo 8/2009, poslední 0.9.7 vyšla 1/2012 (2,5 roku)
  • Nette 2.0 vyšlo 2/2012, poslední 2.0.18 vyšla 12/2014 (2,5 roku)
  • Nette 2.1 vyšlo 12/2013, aktuální 2.1.9 uvádí, že podpora bude trvat do 1/2016 (2,1 roku)

Podpora tedy vždy trvá minimálně 2 roky. Pravdou je, že tato informace nebyla explicitně na webu zmíněna, proto jsem ji doplnil na stránku download.

Související otázkou je i podpora konkrétní verze PHP. Před nějakou dobou jsem sliboval, že PHP 5.3 bude podporované ještě pár let, a skutečně nadcházející Nette 2.3 (vyjde asi na přelomu února a března) je stále s PHP 5.3 kompatibilní, tedy minimálně dva roky ještě bude 5.3 podporované. (Nicméně téměř jistě příští verze Nette už 5.3 opustí.)

Zpátky k harmonogramu vydávání nových verzí. Předvídatelné a pravidelné vydání nových verzí je z mého pohledu ideální, na rozdíl od kalendáře s přesnými daty, kdy která verze v budoucnu vyjde. Koneckonců ho téměř žádný jiný podobný open source projekt nemá.

Samozřejmě chápu, že je prima pocit vědět, kdy která verze vyjde, také bych rád věděl, kdy třeba vyjde PHP 7.0, ale zároveň rozumím, že nikdo z PHP mi přesné datum nedá.

Vydávání nové verze je poměrně náročný proces, do kterého zasahuje hodně faktorů, a tím hlavním je být s výsledkem spokojený a stát si za ním. Kdyby měl do toho ještě zasahovat faktor pevného data, tak to bude jen na úkor něčeho jiného. A to nechci.

Dovedu si představit typy projektů, kde naopak přesný harmonogram je výhodou, ale co funguje jinde, není nutně vhodné i pro Nette.

Doplnění: aby nedošlo k nedorozumění, je samozřejmě potřebné, aby se tým vývojářů domluvil na harmonogramu vydání příští verze, buď mezi sebou interně, nebo veřejně jako v případě PHP timeline, což je při koordinaci velkého počtu vývojářů nutnost. Článek se týkal něčeho jiného, tedy veřejných příslibů, kdy přesně v následujících letech vyjde která verze.)


Sbohem a šáteček, Nette 2.0 & PHP 5.2

Jsou tomu 3 roky, kdy vyšlo Nette 2.0.0. Šlo o přelomovou verzi. Uzavřela pár let trvající vývoj a přinesla novinky, bez kterých si dnes vůbec nelze vývoj v Nette představit.

  • Dependency Injection
  • formát NEON
  • Debug Bar rozšiřitelný o vlastní panely
  • unobtrusive JavaScript validation ve formulářích
  • nové API pro rozšiřování Latte
  • novou strukturu jmenných prostorů a tříd
  • představila databázovou vrstvu Nette Database a NDBT
  • a také úplně nově sepsanou dokumentaci

Shodou okolností v té době byly vydány i přelomové dvojkové verze významných frameworků Zend a Symfony. Nedá mi to nepřipomenout, že na rozdíl od nich Nette nehodilo uživatele svých předchozích verzí přes palubu, tj. neudělalo mezi verzemi tlustou čáru, ale naopak se snažilo o zachování kompatibility, jak to jen bylo možné. Uživatelé také například dostali nástroj, který jim ve zdrojových kódech nahradil původní názvy tříd za nové atd.

PHP 5.2

Řada 2.0 stále držela podporu PHP 5.2, včetně verze PHP 5.2.0, což bylo skutečně bolestivé. Šlo totiž o jednu z méně povedených verzí PHP, jenže Debian ji měl předinstalovanou a konzervativní správci ji odmítali aktualizovat.

Zajímavostí je, že Nette bylo již od roku 2010 psáno čistě v PHP 5.3 se všemi jeho vymoženostmi, jako jsou jmenné prostory nebo anonymní funkce. (Dvě) verze pro PHP 5.2 byly vytvářeny strojově převodníkem. Ten nejen zaměňoval názvy tříd na varianty bez jmenných prostorů, ale poradil si i s přepisem anonymních funkcí a řadou dalších odlišností, jako například nemožností použít func_get_args() jako parametr funkce atd.

Příklad kódu v PHP 5.3:

	/**
	 * Caches results of function/method calls.
	 * @param  mixed
	 * @param  array  dependencies
	 * @return Closure
	 */
	public function wrap($function, array $dependencies = null)
	{
		$cache = $this;
		return function() use ($cache, $function, $dependencies) {
			$key = array($function, func_get_args());
			$data = $cache->load($key);
			if ($data === null) {
				$data = $cache->save($key, Nette\Callback::create($function)->invokeArgs($key[1]), $dependencies);
			}
			return $data;
		};
	}

A převedeného kódu pro PHP 5.2:

	/**
	 * Caches results of function/method calls.
	 * @param  mixed
	 * @param  array  dependencies
	 * @return NClosure
	 */
	public function wrap($function, array $dependencies = null)
	{
		$cache = $this;
		return create_function('',
			'extract($GLOBALS[0]['.array_push($GLOBALS[0], array('cache'=>$cache,'function'=> $function,'dependencies'=> $dependencies)).'-1], EXTR_REFS);
			$_args=func_get_args(); $key = array($function, $_args);
			$data = $cache->load($key);
			if ($data === null) {
				$data = $cache->save($key, NCallback::create($function)->invokeArgs($key[1]), $dependencies);
			}
			return $data;
		');
	}

Dependency Injection

Co zpětně vidím jako nejdůležitější přínos Nette 2.0? Dependency Injection. Ale slovy klasika:

To není jednoduchá věc to Dependency Injection. Není. To je věc, ve které se ne každý dost dobře vyzná.

DI nahradilo do té doby používaný objektový Service Locator a jeho statickou variantu, třídu Environment. Čímž naprosto převrátilo způsoby, jak aplikace navrhovat. Přineslo kvalitativní posun na novou úroveň. Proto také přepsat aplikaci používající Environment na Dependency Injection je nesmírně náročné, protože to vlastně znamená ji navrhnout znovu a lépe.

End of Life

První den roku 2014 vyšlo Nette 2.0.14. Ano, tak hezky to vyšlo :-) Tím byl ukončen vývoj řady 2.0 a série vstoupila do roční fáze critical issues only, kdy byly opravovány jen závažné chyby. Dnes tato fáze končí. Před pár dny vyšlo Nette 2.0.18, definitivně poslední verze této řady a také poslední verze pro PHP 5.2.

Tak sbohem a šáteček!

(Do fáze critical issues only nyní vstupuje řada 2.1. Po dobu roku 2015 budou opravovány jen závažné chyby.)


Školení, na kterém už vaše konkurence byla

Tři roky slýchávám otázku: „Budeš mít školení Nette i pro pokročilé?“ – „Budu,“ odpovídal jsem, ale zdaleka netušil, že přípravy zaberou tolik času. Konečně je to tady!

Pokročilé školení Mistrovství v Nette volně navazuje na základní kurz Vývoj aplikací v Nette a je určeno všem, kteří už framework používají a chtějí proniknout do podstaty jeho fungování. Vědět vše o Dependency Injection a co jim přinese psaní tzv. rozšíření. Jak tvořit vlastní makra do Latte. Jak správně navrhovat komponenty. Nebo vytvářet vlastní formulářové prvky. A tak dále.

Příprava trvala tak dlouho hlavně kvůli mé permanentní nespokojenosti s frameworkem ;-) Nechci školit oblasti, dokud mi nepřipadají perfektní. Ano, je to asi hloupost, ale jelikož vždycky na školeních otevřeně říkám i to, co se mi na Nette nelíbí, tak ten dobrý vnitřní pocit je tuze důležitý.

S verzí Nette 2.2 jsem konečně začal být spokojený.

V říjnu jsem vypsal beta-verzi kurzu, na kterou se přihlásilo 3× více lidí, než jaká byla kapacita. Mezi nimi možná i ti vaši konkurenti :-) Udělal jsem proto testovací školení tři, odladil pořadí i náročnost témat a všem účastníkům děkuji za feedback.

Teď už jedeme naostro.

Přihlaste se na školení Mistrovství v Nette a staňte se Nette guru!


Pět důvodů upgradovat na Nette 2.2.3

Mám skvělý pocit z právě vydané verze Nette 2.2.3, protože se tam podařilo vychytat řadu drobností, počínaje chytřejším rozpoznání chyb u nativních funkcí, přes výstižnější chybové hlášky DI kontejneru, až po různé novinky (viz release notes). Už jsem ji nasadil na všechny své weby a běží výborně.

Rád bych vypíchl 3 užitečné novinky. První se týká Latte a jde o funkci invokeFilter(), kterou můžete volat filtr i mimo šablonu:

$latte = new Latte\Engine;
$latte->addFilter('upper', 'strtoupper');
$upper = $latte->invokeFilter('upper', array('abc')));
// obdoba {'abc'|upper} v šabloně

Druhá novinka se týká Tracy. Ta nyní dokáže logovat chyby jako je E_NOTICE v plné náloži (tj. s HTML souborem), jako když loguje výjimky. Které chyby má takto logovat nastavíte do proměnné $logSeverity:

Tracy\Debugger::$logSeverity = E_NOTICE | E_WARNING;

Třetí novinka souvisí s bezpečností. Třída Configurator má metodu setDebugMode(), pomocí které určujete, zda aplikace poběží v produkčním nebo vývojářském režimu. Raději jí nikdy nepředávejte argument true, může se pak snadno stát, že to deploynete na ostrý server a máte hned bezpečnostní kráter. Správné je jako argument předat IP adresy, pro které chcete vývojářský režim na ostrém serveru povolit:

$configurator->setDebugMode('23.75.345.200');

Jenže IP adresy se mohou měnit a dostane ji někdo jiný. Proto je nově možné přidat ještě pojistku v podobně cookie. Do cookie nazvané nette-debug si uložíte tajný řetězec (buď funkcí setcookie nebo pomocí vývojářského nástroje prohlížeče, každopádně nezapomeňte na příznak httpOnly), například mysecret a necháte Configurator, aby ověřoval i jej. Teprve sedí-li IP adresa i hodnota v cookie, bude aktivován vývojářský režim:

$configurator->setDebugMode('mysecret@23.75.345.200');

Čtvrtá novinka ze tří se týká DI kontejneru a je velmi dobře skrytá. Dovoluje nastavit, které třídy vynechat z autowiringu. Typickým kandidátem je Nette\Application\UI\Control, kterého vám může DI kontejner cpát třeba do konstruktoru formuláře. Seznam ignorovaných tříd předáte metodě ContainerBuilder::addExcludedClasses(). K té se dostanete například v bootstrap.php:

$configurator->onCompile[] = function($configurator, $compiler) {
	$compiler->getContainerBuilder()->addExcludedClasses(array(
		'stdClass',
		'Nette\Application\UI\Control',
	));
};

A do pětice všeho dobrého: při vývoji můžete narazit na upozornění Possible problem: you are sending a HTTP header while already having some data in output buffer. Try OutputDebugger or start session earlier. To se objeví, když se snažíte odeslat HTTP hlavičky, a ono to sice ještě jde, nicméně aplikace už nějaký výstup předtím odeslala, jen ho zachytil output buffer. V takové situaci je nejlepší nastartovat OutputDebugger a zjistit, odkud se výstup posílal a předejít tomu. Od verze 2.2.3 máte také možnost toto dobře míněné upozornění potlačit pomocí proměnné Nette\Http\Response::$warnOnBuffer. Třeba opět z bootstrapu:

$container->getByType('Nette\Http\Response')->warnOnBuffer = false;

Jak předávat závislosti v Nette

Jaké jsou best practice pro předávání závislostí presenterům, komponentám a jiným službám v Nette?

Nejprve: nezávisle na frameworku nebo typu tříd platí vždy tohle:

  • povinné závislosti předávat konstruktorem
  • volitelné buď samostatnou metodou, nebo též konstruktorem

Tečka.


Nicméně existuje případ, kdy je předávání závislostí konstruktorem problematické, tzv. constructor hell, tedy situace, kdy předáváme závislosti konstruktorem určité třídě a zároveň jejímu potomkovi. Tohle nastává často u presenterů, kde je právě zvykem mít nějaký BasePresenter. Jako workaround dává DI kontejner v Nette možnost použít v BasePresenteru místo konstruktoru metodu nazvanou injectBase() (případně jakkoliv jinak, jen musí začínat na inject).

Jde o workaround pro base presentery, takže v jiných situacích metody inject nepoužívejte.

Dále předávání závislostí přes konstruktory nebo jiné metody je otravné v tom, že musíte napsat nějaký rutinní kód. Nutnost psát rutinní kód vždy ukazuje na slabé místo samotného jazyka, jedna ze šancí na zjednodušení byla zamítnuta, nicméně některé IDE ho umí vygenerovat a taktéž DI kontejner ve frameworku nabízí zkratku v podobě @inject anotací. Public (!) proměnnou doplníte anotací např. /** @var Model\UserFacade @inject */ a framework sám do ní vloží službu a vyhnete se psaní konstruktoru nebo inject metody.

Protože to není čisté řešení, funguje opět jen pro presentery. Používání anotací rozhodně není best practice, ale je to sakra pohodlné.


Doporučuji podívat se na přednášku Filipa Procházky a přečíst článek Vojty Dobeše Tvorba komponent s využitím autowiringu.


Nette Revolution 2.2

Asi největší revoluce v dějinách Nette, která se vás nijak nedotkne.

Poměrně krátce po vydání velké verze 2.1, která přinesla řadu vylepšení nejen ve formulářích, je tu další velká verze 2.2, která přichází s úplně novou infrastrukturou. Totiž původní repozitář Nette byl smazán rozdělen do 19 nových samostatných komponent: Application, Bootstrap, Caching, ComponentModel, Nette Database, DI, Finder, Forms, Http, Latte, Mail, Neon, PhpGenerator, Reflection, RobotLoader, SafeStream, Security, Tokenizer, Tracy a Utils.

(Podívejte se na video Kvadratura Nette.)

Co se tím mění pro vás, uživatele Nette Frameworku?

Takřka nic. Stále si můžete stáhnout celý framework nebo jej nainstalovat příkazem composer require nette/nette.

Ale zároveň máte možnost používat zcela samostatně šablonovací systém Latte, Laděnku (neboli Tracy), databázovou vrstvu a vlastně cokoliv.

Rozdělení frameworku do menších částí je trend, který už započalo například Symfony nebo Zend Framework 2. Nutnou podmínkou byl vznik respektovaného nástroje pro správu závislostí v PHP, kterým se stal Composer. Pokud si s ním zatím netykáte, neváhejte a seznamte se, není daleko doba, kdy se v PHP knihovny nebudou distribuovat jinak.

Nette se na rozdíl od Symfony nebo Zendu rozdělilo i fyzicky, což znamená, že každá komponenta má vlastní repozitář, issue tracker a číslování verzí. Zároveň každý repozitář obsahuje testy a všechny související soubory. Podívejte se například na Forms, kde najdete příklady použití v examples, v adresáři src/assets JavaScripty a v adresáři src/Bridges kód, který propojuje různé Nette komponenty. V případě formulářů jde o makra pro Latte.

Stejným způsobem jsou pak strukturovány všechny repozitáře.

Rozdělení frameworku je završením dvouleté práce, s cílem zachovat maximální kompatibilitu. Vlastně i proto zůstáváme u čísla verze 2. Jediným výrazným zásekem bylo oddělení Latte, což si vyžádalo přepracovat systém šablon, který v Nette existuje v takřka původní podobně od verze 0.8. Ale postupně:

Tracy

Laděnka byla přejmenována na Tracy, nemusíte se tak trápit s psaním šíleného Nette\Diagnostics\Debugger, nyní stačí Tracy\Debugger. Původní třída je z důvodu zpětné kompatibility stále funkční.

Pokud píšete doplňky, prefix názvů CSS tříd se změnil z nette- na tracy- a třída nette-toggle-collapsed na dvojici tracy-toggle tracy-collapsed. Původní třídy jsou u starých doplňku změněny na nové automaticky.

Latte

Šablonovací systém byl vždy poměrně úzce provázán s dalšími částmi frameworku, zejména třídami z jmenného prostoru Nette\Templating. Aby bylo Latte samostatně použitelné, bylo potřeba mu vymyslet nové snadno použitelné API, které se obejde bez těchto pomocných tříd. A vypadá takto:

$latte = new Latte\Engine; // nikoliv Nette\Latte\Engine
$latte->setTempDirectory('/path/to/cache');

$latte->addFilter('money', function($val) { return ...; }); // dříve registerHelper()

$latte->onCompile[] = function($latte) {
	$latte->addMacro(...); // when you want add some own macros, see http://goo.gl/d5A1u2
};

$latte->render('template.latte', $parameters);
// or $html = $latte->renderToString('template.latte', $parameters);

Jak vidíte, Latte si řeší samo načítání šablon a jejich kešování, čímž pádem původní FileTemplate a vlastně celý Nette\Templating z velké míry pozbývá na smyslu existence. Tyto třídy i nadále fungují a snaží se zajistit kompatibilitu s novým Latte, nicméně jsou zavržené. Ze stejného důvodu jsou zavržené i třídy Nette\Utils\LimitedScope, Nette\Caching\Storages\PhpFileStorage a služba templateCacheStorage.

Naopak Application přináší vlastní třídu Template (nahrazující FileTemplate a zajišťují kompatibilitu) a továrnu TemplateFactory, která rozšiřuje možnosti, jak v presenterech a komponentách pracovat se šablonami.

Ostatní

Třídy Nette\ArrayHash, ArrayList, DateTime, Image a ObjectMixin jsou nyní součástí balíčku Utils, proto i jejich namespace byl změněn z Nette na Nette\Utils. Obdobně Nette\Utils\Neon se stalo součástí balíčku Neon a bude mít namespace Nette\Neon\Neon. Aby změna byla transparentní, vytváří se pro tyto a některé další třídy tiše aliasy. Aliasy ostatních tříd vytváří Nette Loader spuštěný v loader.php a vypisuje přitom varování (abyste mohli svůj kód upravit). Pokud loader.php nepoužíváte (tj. instalujete Nette přes Composer), můžete Nette Loader načíst ručně třeba v bootstrap.php. Od RC4 se Nette Loader spouští vždy.

Alternativně je možné vytvořit v bootstrap.php rovnou všechny aliasy a obejít tak varování. Ale jelikož PHP při vytváření aliasů načítá zdrojový kód tříd, může to vést k mírnému snížení výkonu:

@array_walk(Nette\Loaders\NetteLoader::getInstance()->renamed, 'class_alias');

Zavržena (tj. stále funguje, jen vyhodí E_USER_DEPRECATED) je třída Nette\Utils\MimeTypeDetector, která od PHP 5.3 není potřeba, neboť ji plně nahrazuje rozšíření Fileinfo (pod Windows jej nezapomeňte zapnout v php.ini).

Byla zrušena podpora anotace @serializationVersion a dohledávání tříd pro vlastní anotace – tyto věci nebyly známé ani používané, ale měly negativní vliv na výkon.

A nakonec, chybné odkazy v šabloně nyní začínají hashem, tj. místo error:... se vypisuje #error:, aby když na takový odkaz omylem kliknete, browser nevypsal strašidelnou hlášku. Upravte si proto CSS.

Novinky

Verze 2.2.0 přichází i s řadou novinek.

Formuláře nyní přenášejí parametr do v POST datech, takže nebude strašit v URL. Přibyly nové validátory Form::MIN a Form::MAX. A do funkcí obsluhující událost onSuccess se nyní jako druhý parametr předávají hodnoty formuláře, takže ušetříte psaní $values = $form->getValues().

V databázi přibyla nová funkce fetchAssoc(). Můžete se podívat na pár příkladů použití v testech.

Anotace @inject nyní respektují aliasy definované pomocí use.

Pokud do data- atributů třídy Html vložíte pole, bude se serializovat do JSONu.

V souboru config.neon můžete jednotlivým uživatelů definovat také jejich role.

A přibyla nová třída Nette\Security\Passwords, která řeší hashování hesel.

Tip: nástroj na automatické přejmenování tříd.


phpFashion © 2004, 2020 David Grudl | o blogu

Ukázky zdrojových kódů smíte používat s uvedením autora a URL tohoto webu bez dalších omezení.