Nette Documentation Preview

syntax
Szablony
********

.[perex]
Nette wykorzystuje system szablonów [Latte |latte:]. Po pierwsze dlatego, że jest to najbezpieczniejszy system szablonowania dla PHP, a także najbardziej intuicyjny. Nie musisz uczyć się wielu nowych rzeczy, wystarczy znajomość PHP i kilka tagów.

Często zdarza się, że strona składa się z szablonu układu + szablonu akcji. Na przykład tak może wyglądać szablon układu, zauważ bloki `{block}` i znacznik `{include}`:

```latte
<!DOCTYPE html>
<html>
<head>
	<title>{block title}My App{/block}</title>
</head>
<body>
	<header>...</header>
	{include content}
	<footer>...</footer>
</body>
</html>
```

I to będzie szablon akcji:

```latte
{block title}Homepage{/block}

{block content}
<h1>Homepage</h1>
...
{/block}
```

Definiuje blok `content`, który zostanie wstawiony w miejsce `{include content}` w układzie, a także redefiniuje blok `title`, który zastąpi `{block title}` w układzie. Spróbujcie sobie wyobrazić ten rezultat.


Wyszukiwanie szablonów .[#toc-template-lookup]
----------------------------------------------

W prezenterach nie trzeba określać, który szablon ma być renderowany; framework automatycznie określi ścieżkę, ułatwiając kodowanie.

Jeśli używasz struktury katalogów, w której każdy prezenter ma swój własny katalog, po prostu umieść szablon w tym katalogu pod nazwą akcji (tj. widoku). Na przykład dla akcji `default` należy użyć szablonu `default.latte`:

/--pre
app/
└── UI/
    └── Home/
        ├── HomePresenter.php
        └── <b>default.latte</b>
\--

Jeśli używasz struktury, w której prezenterzy znajdują się w jednym katalogu, a szablony w folderze `templates`, zapisz je w pliku `<Presenter>.<view>.latte` lub `<Presenter>/<view>.latte`:

/--pre
app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── <b>Home.default.latte</b>  ← 1st variant
        └── <b>Home/</b>
            └── <b>default.latte</b>   ← 2nd variant
\--

Katalog `templates` może być również umieszczony o jeden poziom wyżej, na tym samym poziomie co katalog z klasami prezenterów.

Jeśli szablon nie zostanie znaleziony, prezenter odpowie [błędem 404 - nie znaleziono strony |presenters#Error 404 etc].

Widok można zmienić za pomocą `$this->setView('anotherView')`. Możliwe jest również bezpośrednie określenie pliku szablonu za pomocą `$this->template->setFile('/path/to/template.latte')`.

.[note]
Pliki, w których wyszukiwane są szablony, można zmienić, nadpisując metodę [formatTemplateFiles() |api:Nette\Application\UI\Presenter::formatTemplateFiles()], która zwraca tablicę możliwych nazw plików.


Wyszukiwanie szablonu układu .[#toc-layout-template-lookup]
-----------------------------------------------------------

Nette automatycznie wyszukuje również plik układu.

Jeśli używasz struktury katalogów, w której każdy prezenter ma swój własny katalog, umieść układ w folderze z prezenterem, jeśli jest on specyficzny tylko dla niego, lub poziom wyżej, jeśli jest wspólny dla wielu prezenterów:

/--pre
app/
└── UI/
    ├── <b>@layout.latte</b>           ← common layout
    └── Home/
        ├── <b>@layout.latte</b>       ← only for Home presenter
        ├── HomePresenter.php
        └── default.latte
\--

Jeśli używasz struktury, w której prezenterzy są zgrupowani w jednym katalogu, a szablony znajdują się w folderze `templates`, layout będzie oczekiwany w następujących miejscach:

/--pre
app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── <b>@layout.latte</b>       ← common layout
        ├── <b>Home.@layout.latte</b>  ← only for Home, 1st variant
        └── <b>Home/</b>
            └── <b>@layout.latte</b>   ← only for Home, 2nd variant
\--

Jeśli prezenter znajduje się w [module |modules], będzie on również wyszukiwany dalej w drzewie katalogów zgodnie z zagnieżdżeniem modułu.

Nazwę layoutu można zmienić za pomocą `$this->setLayout('layoutAdmin')`, a następnie będzie ona oczekiwana w pliku `@layoutAdmin.latte`. Można również bezpośrednio określić plik szablonu układu za pomocą `$this->setLayout('/path/to/template.latte')`.

Użycie `$this->setLayout(false)` lub znacznika `{layout none}` wewnątrz szablonu wyłącza wyszukiwanie układu.

.[note]
Pliki, w których przeszukiwane są szablony układu, można zmienić, nadpisując metodę [formatLayoutTemplateFiles() |api:Nette\Application\UI\Presenter::formatLayoutTemplateFiles()], która zwraca tablicę możliwych nazw plików.


Zmienne w szablonie .[#toc-variables-in-the-template]
-----------------------------------------------------

Zmienne są przekazywane do szablonu poprzez zapisanie ich do `$this->template`, a następnie są dostępne w szablonie jako zmienne lokalne:

```php
$this->template->article = $this->articles->getById($id);
```

W ten sposób możemy łatwo przekazać dowolne zmienne do szablonów. Jednak podczas tworzenia solidnych aplikacji często bardziej przydatne jest ograniczenie się. Na przykład poprzez jawne zdefiniowanie listy zmiennych, których oczekuje szablon i ich typów. Pozwoli to PHP na sprawdzanie typu, IDE na prawidłowe szeptanie, a analiza statyczna na wykrywanie błędów.

A jak zdefiniować taką wyliczankę? Po prostu w postaci klasy i jej właściwości. Nazwiemy go jak presenter, ale z `Template` na końcu:

```php
/**
 * @property-read ArticleTemplate $template
 */
class ArticlePresenter extends Nette\Application\UI\Presenter
{
}

class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
{
	public Model\Article $article;
	public Nette\Security\User $user;

	// a další proměnné
}
```

Obiekt `$this->template` w prezenterze będzie teraz instancją klasy `ArticleTemplate`. Tak więc PHP będzie sprawdzać zadeklarowane typy podczas pisania. A począwszy od PHP 8.2, będzie również ostrzegać przy zapisie do nieistniejącej zmiennej; w poprzednich wersjach to samo można osiągnąć za pomocą cechy [Nette\SmartObject |utils:smartobject].

Adnotacja `@property-read` jest dla IDE i analizy statycznej, sprawi, że szeptanie będzie działać, zobacz "PhpStorm i uzupełnianie kodu dla $this->template":https://blog.nette.org/en/phpstorm-and-code-completion-for-this-template.

[* phpstorm-completion.webp *]

Możesz również mieć luksus szeptania w szablonach, wystarczy zainstalować wtyczkę Latte w PhpStorm i umieścić nazwę klasy na początku szablonu, zobacz artykuł "Latte: jak wpisać system":https://blog.nette.org/pl/latte-jak-korzystac-z-systemu-typow, aby uzyskać więcej informacji:

```latte
{templateType App\UI\Article\ArticleTemplate}
...
```

W ten sam sposób działają szablony w komponentach, wystarczy zastosować konwencję nazewnictwa i stworzyć klasę szablonu `FifteenTemplate` dla komponentu np. `FifteenControl`.

Jeśli potrzebujesz stworzyć `$template` jako instancję innej klasy, użyj metody `createTemplate()`:

```php
public function renderDefault(): void
{
	$template = $this->createTemplate(SpecialTemplate::class);
	$template->foo = 123;
	// ...
	$this->sendTemplate($template);
}
```


Zmienne domyślne .[#toc-default-variables]
------------------------------------------

Prezentery i komponenty przekazują automatycznie kilka przydatnych zmiennych do szablonów:

- `$basePath` to bezwzględna ścieżka URL do katalogu głównego (np. `/eshop`)
- `$baseUrl` to bezwzględny adres URL do katalogu głównego (np. `http://localhost/eshop`)
- `$user` jest obiektem [reprezentującym użytkownika |security:authentication]
- `$presenter` jest obecnym prezenterem
- `$control` jest bieżącym elementem lub prezenterem
- `$flashes` jest tablicą [wiadomości |presenters#Flash-Messages] wysyłanych przez funkcje `flashMessage()`

Jeśli używasz niestandardowej klasy szablonu, te zmienne zostaną przekazane, jeśli utworzysz dla nich właściwość.


Tworzenie linków .[#toc-creating-links]
---------------------------------------

Szablon tworzy w ten sposób linki do innych prezenterów & wydarzeń:

```latte
<a n:href="Product:show">detail produktu</a>
```

Atrybut `n:href` jest bardzo przydatny dla znaczników HTML `<a>`. Jeśli chcemy wymienić link w innym miejscu, na przykład w tekście, używamy `{link}`:

```latte
Adresa je: {link Home:default}
```

Aby uzyskać więcej informacji, zobacz [Tworzenie linków URL |creating-links].


Niestandardowe filtry, tagi, itp. .[#toc-custom-filters-tags-etc]
-----------------------------------------------------------------

System szablonów Latte może być rozszerzony o własne filtry, funkcje, tagi, itp. Można to zrobić bezpośrednio w metodzie `render<View>` lub `beforeRender()`:

```php
public function beforeRender(): void
{
	// dodaj filtr
	$this->template->addFilter('foo', /* ... */);

	// lub skonfigurować bezpośrednio obiekt Latte\Engine
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}
```

Latte w wersji 3 oferuje bardziej zaawansowany sposób tworzenia [rozszerzenia |latte:creating-extension] dla każdego projektu internetowego. Oto krótki przykład takiej klasy:

```php
namespace App\UI\Accessory;

final class LatteExtension extends Latte\Extension
{
	public function __construct(
		private App\Model\Facade $facade,
		private Nette\Security\User $user,
		// ...
	) {
	}

	public function getFilters(): array
	{
		return [
			'timeAgoInWords' => $this->filterTimeAgoInWords(...),
			'money' => $this->filterMoney(...),
			// ...
		];
	}

	public function getFunctions(): array
	{
		return [
			'canEditArticle' =>
				fn($article) => $this->facade->canEditArticle($article, $this->user->getId()),
			// ...
		];
	}

	// ...
}
```

Rejestrujemy go za pomocą [konfiguracji |configuration#Latte]:

```neon
latte:
	extensions:
		- App\UI\Accessory\LatteExtension
```


Tłumaczenie .[#toc-translating]
-------------------------------

Jeśli programujesz wielojęzyczną aplikację, prawdopodobnie będziesz musiał wyprowadzić część tekstu w szablonie w różnych językach. Aby to zrobić, Nette Framework definiuje interfejs tłumaczący [api:Nette\Localization\Translator], który posiada pojedynczą metodę `translate()`. Przyjmuje ona komunikat `$message`, który zwykle jest ciągiem znaków, oraz dowolne inne parametry. Jej zadaniem jest zwrócenie przetłumaczonego ciągu znaków.
W Nette nie ma domyślnej implementacji, można wybrać według własnych potrzeb spośród kilku gotowych rozwiązań, które można znaleźć na [Componette |https://componette.org/search/localization]. Ich dokumentacja podpowiada, jak skonfigurować translator.

Szablony można skonfigurować z tłumaczem, którego [będziemy mieli przekazanego |dependency-injection:passing-dependencies], za pomocą metody `setTranslator()`:

```php
protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator);
}
```

Alternatywnie, tłumacz może być ustawiony za pomocą [konfiguracji |configuration#Latte]:

```neon
latte:
	extensions:
		- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
```

Translator może być wtedy użyty np. jako filtr `|translate`, z dodatkowymi parametrami przekazywanymi do metody `translate()` (patrz `foo, bar`):

```latte
<a href="basket">{='Basket'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>
```

Albo jako znacznik podkreślenia:

```latte
<a href="basket">{_'Basket'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>
```

Do tłumaczenia sekcji szablonu służy sparowany tag `{translate}` (od wersji Latte 2.11, wcześniej używany był tag `{_}` ):

```latte
<a href="order">{translate}Order{/translate}</a>
<a href="order">{translate foo, bar}Order{/translate}</a>
```

Translator jest domyślnie wywoływany w trybie runtime podczas renderowania szablonu. Latte w wersji 3 może jednak tłumaczyć cały tekst statyczny podczas kompilacji szablonu. Oszczędza to wydajność, ponieważ każdy ciąg jest tłumaczony tylko raz, a wynikowe tłumaczenie jest zapisywane do skompilowanej postaci. Tworzy to wiele skompilowanych wersji szablonu w katalogu cache, po jednej dla każdego języka. Aby to zrobić, musisz tylko określić język jako drugi parametr:

```php
protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator, $lang);
}
```

Przez tekst statyczny rozumiemy na przykład `{_'hello'}` lub `{translate}hello{/translate}`. Tekst niestatyczny, taki jak `{_$foo}`, będzie nadal kompilowany w locie.

Szablony

Nette wykorzystuje system szablonów Latte. Po pierwsze dlatego, że jest to najbezpieczniejszy system szablonowania dla PHP, a także najbardziej intuicyjny. Nie musisz uczyć się wielu nowych rzeczy, wystarczy znajomość PHP i kilka tagów.

Często zdarza się, że strona składa się z szablonu układu + szablonu akcji. Na przykład tak może wyglądać szablon układu, zauważ bloki {block} i znacznik {include}:

<!DOCTYPE html>
<html>
<head>
	<title>{block title}My App{/block}</title>
</head>
<body>
	<header>...</header>
	{include content}
	<footer>...</footer>
</body>
</html>

I to będzie szablon akcji:

{block title}Homepage{/block}

{block content}
<h1>Homepage</h1>
...
{/block}

Definiuje blok content, który zostanie wstawiony w miejsce {include content} w układzie, a także redefiniuje blok title, który zastąpi {block title} w układzie. Spróbujcie sobie wyobrazić ten rezultat.

Wyszukiwanie szablonów

W prezenterach nie trzeba określać, który szablon ma być renderowany; framework automatycznie określi ścieżkę, ułatwiając kodowanie.

Jeśli używasz struktury katalogów, w której każdy prezenter ma swój własny katalog, po prostu umieść szablon w tym katalogu pod nazwą akcji (tj. widoku). Na przykład dla akcji default należy użyć szablonu default.latte:

app/
└── UI/
    └── Home/
        ├── HomePresenter.php
        └── default.latte

Jeśli używasz struktury, w której prezenterzy znajdują się w jednym katalogu, a szablony w folderze templates, zapisz je w pliku <Presenter>.<view>.latte lub <Presenter>/<view>.latte:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── Home.default.latte  ← 1st variant
        └── Home/
            └── default.latte   ← 2nd variant

Katalog templates może być również umieszczony o jeden poziom wyżej, na tym samym poziomie co katalog z klasami prezenterów.

Jeśli szablon nie zostanie znaleziony, prezenter odpowie błędem 404 – nie znaleziono strony.

Widok można zmienić za pomocą $this->setView('anotherView'). Możliwe jest również bezpośrednie określenie pliku szablonu za pomocą $this->template->setFile('/path/to/template.latte').

Pliki, w których wyszukiwane są szablony, można zmienić, nadpisując metodę formatTemplateFiles(), która zwraca tablicę możliwych nazw plików.

Wyszukiwanie szablonu układu

Nette automatycznie wyszukuje również plik układu.

Jeśli używasz struktury katalogów, w której każdy prezenter ma swój własny katalog, umieść układ w folderze z prezenterem, jeśli jest on specyficzny tylko dla niego, lub poziom wyżej, jeśli jest wspólny dla wielu prezenterów:

app/
└── UI/
    ├── @layout.latte           ← common layout
    └── Home/
        ├── @layout.latte       ← only for Home presenter
        ├── HomePresenter.php
        └── default.latte

Jeśli używasz struktury, w której prezenterzy są zgrupowani w jednym katalogu, a szablony znajdują się w folderze templates, layout będzie oczekiwany w następujących miejscach:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── @layout.latte       ← common layout
        ├── Home.@layout.latte  ← only for Home, 1st variant
        └── Home/
            └── @layout.latte   ← only for Home, 2nd variant

Jeśli prezenter znajduje się w module, będzie on również wyszukiwany dalej w drzewie katalogów zgodnie z zagnieżdżeniem modułu.

Nazwę layoutu można zmienić za pomocą $this->setLayout('layoutAdmin'), a następnie będzie ona oczekiwana w pliku @layoutAdmin.latte. Można również bezpośrednio określić plik szablonu układu za pomocą $this->setLayout('/path/to/template.latte').

Użycie $this->setLayout(false) lub znacznika {layout none} wewnątrz szablonu wyłącza wyszukiwanie układu.

Pliki, w których przeszukiwane są szablony układu, można zmienić, nadpisując metodę formatLayoutTemplateFiles(), która zwraca tablicę możliwych nazw plików.

Zmienne w szablonie

Zmienne są przekazywane do szablonu poprzez zapisanie ich do $this->template, a następnie są dostępne w szablonie jako zmienne lokalne:

$this->template->article = $this->articles->getById($id);

W ten sposób możemy łatwo przekazać dowolne zmienne do szablonów. Jednak podczas tworzenia solidnych aplikacji często bardziej przydatne jest ograniczenie się. Na przykład poprzez jawne zdefiniowanie listy zmiennych, których oczekuje szablon i ich typów. Pozwoli to PHP na sprawdzanie typu, IDE na prawidłowe szeptanie, a analiza statyczna na wykrywanie błędów.

A jak zdefiniować taką wyliczankę? Po prostu w postaci klasy i jej właściwości. Nazwiemy go jak presenter, ale z Template na końcu:

/**
 * @property-read ArticleTemplate $template
 */
class ArticlePresenter extends Nette\Application\UI\Presenter
{
}

class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
{
	public Model\Article $article;
	public Nette\Security\User $user;

	// a další proměnné
}

Obiekt $this->template w prezenterze będzie teraz instancją klasy ArticleTemplate. Tak więc PHP będzie sprawdzać zadeklarowane typy podczas pisania. A począwszy od PHP 8.2, będzie również ostrzegać przy zapisie do nieistniejącej zmiennej; w poprzednich wersjach to samo można osiągnąć za pomocą cechy Nette\SmartObject.

Adnotacja @property-read jest dla IDE i analizy statycznej, sprawi, że szeptanie będzie działać, zobacz PhpStorm i uzupełnianie kodu dla $this->template.

Możesz również mieć luksus szeptania w szablonach, wystarczy zainstalować wtyczkę Latte w PhpStorm i umieścić nazwę klasy na początku szablonu, zobacz artykuł Latte: jak wpisać system, aby uzyskać więcej informacji:

{templateType App\UI\Article\ArticleTemplate}
...

W ten sam sposób działają szablony w komponentach, wystarczy zastosować konwencję nazewnictwa i stworzyć klasę szablonu FifteenTemplate dla komponentu np. FifteenControl.

Jeśli potrzebujesz stworzyć $template jako instancję innej klasy, użyj metody createTemplate():

public function renderDefault(): void
{
	$template = $this->createTemplate(SpecialTemplate::class);
	$template->foo = 123;
	// ...
	$this->sendTemplate($template);
}

Zmienne domyślne

Prezentery i komponenty przekazują automatycznie kilka przydatnych zmiennych do szablonów:

  • $basePath to bezwzględna ścieżka URL do katalogu głównego (np. /eshop)
  • $baseUrl to bezwzględny adres URL do katalogu głównego (np. http://localhost/eshop)
  • $user jest obiektem reprezentującym użytkownika
  • $presenter jest obecnym prezenterem
  • $control jest bieżącym elementem lub prezenterem
  • $flashes jest tablicą wiadomości wysyłanych przez funkcje flashMessage()

Jeśli używasz niestandardowej klasy szablonu, te zmienne zostaną przekazane, jeśli utworzysz dla nich właściwość.

Szablon tworzy w ten sposób linki do innych prezenterów & wydarzeń:

<a n:href="Product:show">detail produktu</a>

Atrybut n:href jest bardzo przydatny dla znaczników HTML <a>. Jeśli chcemy wymienić link w innym miejscu, na przykład w tekście, używamy {link}:

Adresa je: {link Home:default}

Aby uzyskać więcej informacji, zobacz Tworzenie linków URL.

Niestandardowe filtry, tagi, itp.

System szablonów Latte może być rozszerzony o własne filtry, funkcje, tagi, itp. Można to zrobić bezpośrednio w metodzie render<View> lub beforeRender():

public function beforeRender(): void
{
	// dodaj filtr
	$this->template->addFilter('foo', /* ... */);

	// lub skonfigurować bezpośrednio obiekt Latte\Engine
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}

Latte w wersji 3 oferuje bardziej zaawansowany sposób tworzenia rozszerzenia dla każdego projektu internetowego. Oto krótki przykład takiej klasy:

namespace App\UI\Accessory;

final class LatteExtension extends Latte\Extension
{
	public function __construct(
		private App\Model\Facade $facade,
		private Nette\Security\User $user,
		// ...
	) {
	}

	public function getFilters(): array
	{
		return [
			'timeAgoInWords' => $this->filterTimeAgoInWords(...),
			'money' => $this->filterMoney(...),
			// ...
		];
	}

	public function getFunctions(): array
	{
		return [
			'canEditArticle' =>
				fn($article) => $this->facade->canEditArticle($article, $this->user->getId()),
			// ...
		];
	}

	// ...
}

Rejestrujemy go za pomocą konfiguracji:

latte:
	extensions:
		- App\UI\Accessory\LatteExtension

Tłumaczenie

Jeśli programujesz wielojęzyczną aplikację, prawdopodobnie będziesz musiał wyprowadzić część tekstu w szablonie w różnych językach. Aby to zrobić, Nette Framework definiuje interfejs tłumaczący Nette\Localization\Translator, który posiada pojedynczą metodę translate(). Przyjmuje ona komunikat $message, który zwykle jest ciągiem znaków, oraz dowolne inne parametry. Jej zadaniem jest zwrócenie przetłumaczonego ciągu znaków. W Nette nie ma domyślnej implementacji, można wybrać według własnych potrzeb spośród kilku gotowych rozwiązań, które można znaleźć na Componette. Ich dokumentacja podpowiada, jak skonfigurować translator.

Szablony można skonfigurować z tłumaczem, którego będziemy mieli przekazanego, za pomocą metody setTranslator():

protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator);
}

Alternatywnie, tłumacz może być ustawiony za pomocą konfiguracji:

latte:
	extensions:
		- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)

Translator może być wtedy użyty np. jako filtr |translate, z dodatkowymi parametrami przekazywanymi do metody translate() (patrz foo, bar):

<a href="basket">{='Basket'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>

Albo jako znacznik podkreślenia:

<a href="basket">{_'Basket'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>

Do tłumaczenia sekcji szablonu służy sparowany tag {translate} (od wersji Latte 2.11, wcześniej używany był tag {_} ):

<a href="order">{translate}Order{/translate}</a>
<a href="order">{translate foo, bar}Order{/translate}</a>

Translator jest domyślnie wywoływany w trybie runtime podczas renderowania szablonu. Latte w wersji 3 może jednak tłumaczyć cały tekst statyczny podczas kompilacji szablonu. Oszczędza to wydajność, ponieważ każdy ciąg jest tłumaczony tylko raz, a wynikowe tłumaczenie jest zapisywane do skompilowanej postaci. Tworzy to wiele skompilowanych wersji szablonu w katalogu cache, po jednej dla każdego języka. Aby to zrobić, musisz tylko określić język jako drugi parametr:

protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator, $lang);
}

Przez tekst statyczny rozumiemy na przykład {_'hello'} lub {translate}hello{/translate}. Tekst niestatyczny, taki jak {_$foo}, będzie nadal kompilowany w locie.