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 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ě.
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.)
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.)
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!
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é 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.
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.
„Přesvědč mě, proč bych měl používat Nette Tester, v čem je lepší než
PHPUnit?“ Tyhle otázky mě vždycky trošku uvádějí do rozpaků, neboť
nemám potřebu nikoho přesvědčovat, aby Tester používal. Sám bych se bez
něj ale neobešel.
Samotné testování je poněkud zakletá věc. Pět let se na všech
konferencích stále dokola mluví o testování a přitom skoro v žádné
firmě se kód „netestuje“. Píšu to v uvozovkách, protože ve
skutečnosti všichni programátoři dnes a denně testují, co ale nedělají
je, že nepíšou testy v PHPUnit, z mnoha důvodů. Pravda je, že sám jsem
v oné krátké době, kdy byl Nette Framework testován PHPUnitem, také
ztratil chuť testovat. Přičemž testování je pro vývoj frameworku natolik
zásadní prvek, jako třeba verzovací systém.
Ale popořadě. Proč všichni programátoři testují, i když
„netestují“?
Představte si, že naprogramujete funkci foobar()
function foobar($x, $y) {
// tady se počítají nějaké věci
return $val;
}
První věc, kterou každý programátor udělá, je, že vyzkouší, jestli
to funguje:
echo foobar(10, 20);
Spustí to, vypíše to 30, což je správně, vypadá to, že to funguje a
třeba ještě vyzkouší pár jiných vstupů.
Jinými slovy funkci otestuje. Tedy testuje!
Potom následně udělá to, že testovací skript smaže. A to je
právě průšvih! Všechny ty argumenty vývojářů, že na testování
nemají čas, v tento moment padají, protože realita je taková, že na
testování čas je, dokonce jsou napsané i testy, jenže pak je
programátoři smažou. Ironie.
Testem není pouze třída v PHPUnit, testem je i tento jednořádkový
skript. Přesněji řečeno, test musí obsahovat ještě informaci, jaká je
správná návratová hodnota, takže by vypadal takto:
assert( foobar(10, 20) === 30 );
assert( foobar(-1, 5) === 4 );
...
Až někdy v budoucnu upravím implementaci funkce foobar
, nebo
do ní sáhne kolega, stačí tento skript spustit, a pokud vyhodí warning,
hned vím, že je funkce rozbitá.
A to je vše. Tohle je testování. Tak je to jednoduché. Děláme to
všichni, bohužel hodně z vás si ty testy pak maže.
Časem se nám nahromadí obrovská spousta takovýchto testovacích
skriptíků a vznikne otázka, jak je spouštět hromadně. Dá se to řešit
nějakým shell skriptem, já jsem si na to napsal PHP skript. Jeho podstatnou
výhodou je, že umí testy pouštět paralelně (běžně spouštím
40 vláken), což otestování celé sady dramaticky zrychlí. A taky
přehledně vypisuje, ve kterém souboru kde přesně došlo k selhání.
Také místo PHP funkce assert
jsem si napsal vlastní funkce (třída Assert
),
které se liší především v tom, že při selhání hezky a čitelně
vypíšou, co funkce měla vrátit a co místo toho vrátila, abych rychle
věděl, kde je problém.
A ten spouštěč, třída Assert
a ještě dalších pár
věcí tvoří zmíněný Nette Tester. Čtyři roky spolehlivě testuje Nette
Framework.
Když mi někdo nahlásí, že ve funkci foobar
je chyba, že
pro vstupy 0
a -1
vrací prázdný řetězec místo
čísla -1
, tak začnu tím, že to nejprve ověřím:
Assert::same( -1, foobar(0, -1) );
Spustím to a skutečně, vypíše se:
Failed: '' should be -1
Tedy jsem napsal failující test. Neudělal jsem to proto, že
v příručkách o testování se píše, že test musí nejdřív failovat,
nebo proto, že bych se řídil TDD, dělám to proto, že mě nenapadá, co
jiného by se dalo dělat, než prostě napsat krátký kód, který bugreport
ověří, tedy failující test.
Chyba tam vážně je a je třeba ji opravit. V IDE začnu kód krokovat a
hledám místo, kde je zakopaný pes. (O programátorech, kteří píší kód
v poznámkovém bloku, ať už se jmenuje TextMate nebo Sublime, namísto
plnohodnotného IDE, a nemohou proto kód krokovat, napíšu článek někdy
příště.) Ano, kde je chyba bych asi zjistil i bez krokování, zíráním
do kódu a umisťováním var_dump
, echo
nebo
console.log
, ale trvalo by to mnohem déle. Chci zdůraznit, že
krokování a testování nejsou alternativy, ale zcela odlišné činnosti,
které je fajn používat dohromady.
Odhalím a opravím chybu, Assert::same
je spokojený a do
repozitáře komitnu nejen opravu funkce foobar
, ale také onen
soubor s testem. Díky tomu se tahle chyba už nikdy v budoucnu nevyskytne.
A věřte mi, že chyby mají tendenci se opakovat, dokonce pro tento jev
existuje název: regrese.
Tohle povídání vám asi muselo připadat strašně samozřejmé. A to je
dobře, ono je samozřejmé a já chci bourat předsudky a obavy z testování.
Ale stále jsem neodpověděl na úvodní otázku: proč nepoužívám PHPUnit?
Protože s ním takto přímočaře pracovat neumím.
Abych mohl otestovat foobar
, musel bych napsat celou třídu,
která dědí od jiné třídy, jejíž jméno si nejsem schopen zapamatovat. No
budiž, použiji šablonu. PHPUnit neumí testy spouštět paralelně, takže
otestování celé sady trvá násobně déle. V případě Nette Frameworku
jde o 35 sekund versus 2 minuty, což už saje. Ale především, testy
psané v PHPUnitu lze spouštět jen v PHPUnitu, nejde o samostatně
spustitelné skripty. Takže není způsob, jak napsat failující test a potom
ho úplně snadno krokovat a hledat výše uvedeným způsobem chybu.
Nejjednodušší řešení proto bylo napsat si vlastní triviální
testovací udělátko. Za ty čtyři roky velmi pozvolna vyspěl
v plnohodnotný nástroj, už ho nevyvíjím sám a díky klukům z Oracle má
dnes integrovanou podporu v NetBeans 8.0.
Jelikož generuje výstup ve formátu TAP, neměl by být problém ani
s integrací do jiných nástrojů.
Nebudu vás přesvědčovat, abyste používali Nette Tester, ale rád bych
vás přesvědčil, abyste si testy, které píšete, nemazali 
Otázky a stručné odpovědi pro všechny, kteří by rádi
přispěli do Nette Frameworku, ale netuší jak.
Rád
bych přispěl do Nette, ale nejsem moc dobrý programátor…
Kód je jen jedna ze šíleně mnoha částí, co tvoří framework. Do Nette
mohou přispívat lidé tím, že pomáhají vylepšit dokumentaci, píší
články, knihy, korigují naše pokusy o angličtinu, přednáší, točí
tutoriály, trpělivě odpovídají na fóru, dělí se o své nápady,
programují doplňky, testují nové verze, tvoří pro Nette nástroje,
pomáhají s Poslední sobotou, hledají prostory, natáčejí přednášky,
stříhají je, organizují a navštěvují hackathony a nebo prostě podporují
framework finančně. Určitě existuje ještě dalších 101 věcí, které
mě teď nenapadly.
Přispívání do kódu je vlastně jen jedna specifická záležitost,
o kterou mi dříve ani tolik nešlo a rozhodně bych nerad vyvolal pocit, že
každý by měl přispět řádkem
Podstatné je, že něco dělat chceš, čehož si vážím a
dodávám: možností jsou mraky.
Existuje
teda nějaký seznam úkolů, které je třeba udělat?
Žádný bodový seznam v tuto chvíli nemám, byť časem třeba něco
vznikne. O seznam totiž nejde, inspiraci máš kolem sebe. Třeba když
vyřešíš nějaký zajímavý úkol v Nette, máš příležitost o tom
blognout (jako to třeba dělá Filip a jiní). Určitě tě
mnohokrát napadlo, co by se v Nette mohlo vyřešit líp, netvrď mi, že ne,
a to je příležitost napsat
RFC (česky: „zlepšovací návrh“).
Ono stačí ve věcech místo problémů vidět příležitosti. Stěžovat
si, že Nette nemá zdaleka tolik tutoriálů, co třeba Laravel či Rails
versus uvědomit si příležitost a napsat třeba seriál a udělat
si tím jméno. Stěžovat si, že v issue trackeru visí
bugy versus uvědomit si, že můžu některé vyřešit. Vidět, že
mnoho lidí bylo frustrovaných
z quickstartu versus uvědomit si možnost s tím
něco udělat. A tak dále.
Spíš mám za to, že nevědět, co se žádá,
znamená mít klapky na očích.
Dobře,
ale pokud chci přímo programovat, musím znát road map
Nejnepraktičtější frameworky jsou ty, které navrhneš tzv. „od
stolu“. Nejlepší nápady totiž přicházejí z praxe, když se snažíš
něco zjednodušit. Nebo čistě náhodou. Tyhle myšlenky pak tvoří
road map.
Sepsal jsem seznam
svých idejí, které jsem plánoval do Nette přidat. Mohou tě zaujmout a
můžeš se pokusit je realizovat, mohou tě inspirovat k novým nápadům, ale
také si jich vůbec nemusíš všímat a můžeš přijít
s čímkoliv novým.
Prostě to normálně zkus.
A co když
něco naprogramuji a pak to nebude přijato?
To se pochopitelně stát může. Sám jsem naprogramoval stovky věcí,
které jsem nakonec do frameworku nedal. Proto je vhodné s nápadem přijít
v raném stádium na fórum a
sepsat RFC. Odargumentovat nebo si zkusit věc nahrubo naprogramovat často
pomůže utřídit myšlenky. Na fóru proběhne diskuse, někdy nelehká, jindy
se záměr takřka okamžitě schválí.
A domluvili jsme se s kolegy, že budeme RFC věnovat velkou pozornost, aby
nevyšuměly.
Někdy není ideální, aby se nápad stal přímo součástí frameworku,
ale může z něj být výborný doplněk.
A to musím kvůli všemu psát
RFC?
Pochopitelně ne, u jednodušších věcí stačí poslat Pull request (návod).
Přičemž kód a commit message musí respektovat coding standards, součástí
má být test a nejlépe i zápis do dokumentace.
…což
je dost práce. Existuje nějaký nástroj na ověření coding standards?
Poprvé to asi není úplně snadné, ale chceme přece všichni používat
framework, který má vysoké nároky na kvalitu, ne? Zvládnout základy Gitu a
Githubu se ti milionkrát vrátí. (Btw, dejte prosím Nette hvězdičku!)
Nástroj na ověření coding style zatím nemáme. Osobně mi nechybí:
když přispívám do jiných projektů, kód formátuji stejně, jako vypadá
ten okolní, v logu se podívám, jak hlavní vývojáři zapisují commit
message a je to řádově snazší, než studovat coding standards či
instalovat nějaký nástroj. Ale beru, že by se mohl hodit – opět je to
příležitost takovou věc udělat.
Jakým způsobem lze psát
do dokumentace?
Celá dokumentace je na
Githubu, takže se do ní dá přispívat stejně, jako do kódu, tedy
poslat pull request. Repozitář je rozdělen do větví, dokumentace pro Nette
2.0 je ve větvi doc-2.0
, pro verzi 2.1 v doc-2.1
.
Každou změnu je záhodno udělat v českém i anglickém jazyce. Pro náhled
použijte editor.
Pokud bys chtěl (v angličtině) představit nějaký doplněk nebo
publikovat návod, je ti k dispozici i nový Nette Blog.
Proč
anglicky? Není trapné mluvit anglicky, když jsme tu samí Češi?
Já vím, ale věc se má tak, že skoro nikdo na světě neumí česky 
Na rovinu, angličtina je pravděpodobně mnohem větší problém pro mě,
než pro tebe, protože jde o můj pátý jazyk, který se, když jsem byl na
základce, ani nevyučoval. Ale bojuji s tím! Psát anglicky má pár výhod:
lidi se v diskusích nemají tendenci moc rozkecávat, při psaní se
zdokonaluješ ve znalosti nejdůležitějšího jazyka a tvůj článek si
může přečíst tisíckrát více lidí.
Kde se dozvím, co se chystá a
tak?
Primárním místem je již zmiňovaný Nette Blog + Discussion on
development a RFC na fóru.
Můžeš sledovat také twitterový účet @NetteFramework či #nettefw
a samozřejmě číst tento blog.
Jinak každý měsíc pořádáme Poslední
soboty a komornější #NetteFwPivo, kde se pije převážně
kubánský rum.
A nemělo by zapadnout, že máme i Youtube kanál
s hromadou videí.
A kdo je to „my“?
Skupina samozvaných lidí, kteří prostě chtěli být u toho a chtějí
něco dělat. Přidej se!
Btw, pokud jsi napsal nějaký článek, seriál, natočil video apod, bylo
by fajn to dát na Planette. Stačí, když
mi pošleš odkaz do komentářů, s pár průvodními slovy.