Nette Documentation Preview

syntax
NEON pro PHP
************

<div class=perex>

Balíček `nette/neon` čte a zapisuje NEON v PHP. Díky němu:

- dekódujete konfigurační soubor do hodnot PHP a hodnoty zakódujete zpět do NEONu
- [upravíte soubor |editing] tak, že každý komentář a mezera, na které nesáhnete, zůstanou na svém místě
- dáte některým hodnotám vlastní význam, třeba konstantám nebo parametrům
- zkontrolujete soubory pomocí `neon-lint` v editoru, v CI nebo po AI agentovi

</div>


Instalace
=========

Knihovnu stáhnete a nainstalujete pomocí nástroje [Composer|best-practices:composer]:

```shell
composer require nette/neon
```

Vyžaduje PHP 8.4 až 8.6 s rozšířeními `json` a `mbstring`.


Dekódování
==========

Dejme tomu, že vaše aplikace má nastavení v souboru `config.neon`:

```neon
# database connection
database:
	host: localhost
	port: 3306

services:
	- App\Model\ArticleFacade
```

Statická třída [api:Nette\Neon\Neon] ho načte do obyčejného pole PHP:

```php
use Nette\Neon\Neon;

$config = Neon::decodeFile('config.neon');
// ['database' => ['host' => 'localhost', 'port' => 3306], 'services' => ['App\Model\ArticleFacade']]
```

Metoda `decode()` dělá totéž s řetězcem:

```php
$value = Neon::decode('hello: world'); // ['hello' => 'world']
```

Hodnoty [formátu |format] se převedou na tyto hodnoty PHP:

| NEON | PHP
|-----------------------------------------
| `null`, `true`, `yes` … | `null`, `bool`
| celé číslo | `int`; nad `PHP_INT_MAX` řetězec `string` z jeho číslic
| desetinné číslo | `float`
| řetězec | `string`
| [datum |format#Datum] | `DateTimeImmutable`
| [entita |format#Entity], řetěz | [api:Nette\Neon\Entity]
| sekvence, mapování | `array`

PHP ukládá sekvence i mapování do polí, takže prázdné `[]` i prázdné `{}` se stanou `[]`. Datum bez časové zóny dostane výchozí časovou zónu PHP.


Kódování
========

Metoda `encode()` zapíše hodnotu PHP jako NEON. Ve výchozím stavu zapíše vše na jeden řádek; pokud jako druhý argument předáte `true`, dostanete bloky:

```php
Neon::encode(['roles' => ['guest', 'admin']]);
// '{roles: [guest, admin]}'

Neon::encode(['roles' => ['guest', 'admin']], true);
// "roles:\n\t- guest\n\t- admin\n\n"
```

Místo `true` můžete předat funkci, která rozhodne u každého pole zvlášť. Dostane pole a seznam klíčů, které k němu vedou, a pro blok vrátí `true`. Tahle zapíše bloky jen v první úrovni:

```php
Neon::encode($config, fn($value, $path) => !$path);
// "database: {host: localhost, port: 3306}\nservices: [App\\Model\\ArticleFacade]\n"
```

Třetí argument určuje odsazení, výchozí je tabulátor. Řetězec dostane uvozovky, jen když je potřebuje, a víceřádkový řetězec se zapíše jako [víceřádkový řetězec |format#Víceřádkové řetězce].

Některé hodnoty jako NEON bez ztráty zapsat nejde: řetězec, který není platné UTF-8, `INF` a `NAN`, resource, closure nebo struktura, která obsahuje sama sebe. Pro ně `encode()` vyhodí `Nette\Neon\Exception`.


Chyby
=====

Když vstup není platný NEON, `decode()` a `decodeFile()` vyhodí `Nette\Neon\Exception` s pozicí problému:

```php
try {
	$config = Neon::decode("database:\n\thost: localhost\n\thost: db");
} catch (Nette\Neon\Exception $e) {
	echo $e->getMessage();     // Duplicated key 'host' on line 3 at column 2
	echo $e->getDescription(); // Duplicated key 'host'
}
```

Sloupec se počítá ve znacích, ne v bajtech. Když chybu zobrazujete po svém, třeba u pole formuláře, metoda `getDescription()` vrátí hlášku bez pozice.


Vlastní hodnoty: Evaluator
==========================

`Neon::decode()` převádí [syntaktický strom |editing#Strom] na hodnoty pomocí třídy `Nette\Neon\Evaluator`. Její potomek může některým hodnotám dát vlastní význam. Tento převádí názvy konstant PHP na jejich hodnoty:

```php
use Nette\Neon\Evaluator;
use Nette\Neon\Neon;
use Nette\Neon\Nodes\LiteralNode;
use Nette\Neon\Place;

final class ConstantEvaluator extends Evaluator
{
	protected function evaluateLiteral(LiteralNode $node, Place $place): mixed
	{
		$value = parent::evaluateLiteral($node, $place);
		return $place === Place::Value && is_string($value) && defined($value)
			? constant($value)
			: $value;
	}
}

$document = Neon::parse('errorLevel: E_ALL');
$config = new ConstantEvaluator()->evaluate($document->value);
// ['errorLevel' => 30719]
```

Metody, které můžete přepsat, jsou `evaluateLiteral()`, `evaluateString()`, `evaluateEntity()`, `evaluateChain()` a `evaluateItem()`. `Place` říká, kde hodnota stojí: jako hodnota, jako klíč, jako jméno entity nebo jako článek řetězu. Díky tomu příklad výše nechá na pokoji klíč, který se jmenuje jako konstanta. Takto čte svou konfiguraci [DI kontejner |dependency-injection:] Nette.


Kontrola souborů: neon-lint
===========================

Balíček obsahuje příkaz `neon-lint`. Předáte mu soubory nebo adresáře a on vypíše jeden řádek pro každý problém:

```shell
vendor/bin/neon-lint config/
```

```
config/local.neon:3:2: Duplicated key 'user'
```

Když jsou všechny soubory platné, nevypíše nic. V adresáři hledá soubory `*.neon` a vynechá `vendor`, `node_modules` a skryté adresáře, `-` čte standardní vstup. Jeho chování mění tyto volby:

| Volba | Účinek
|-----------------------------------------
| `--check` | ověří navíc, že se soubor načte zpět bajt po bajtu
| `--json` | vypíše nálezy jako JSON, pro nástroje
| `--quiet` | nevypíše nic, jen nastaví návratový kód
| `--verbose` | vypíše počet souborů, když jsou všechny platné

Návratový kód je 0, když jsou všechny soubory platné, 1 při nálezu a 2 při chybném volání. Příkaz, jeho výstup i návratové kódy jsou stejné v knihovnách pro [JavaScript |javascript] a [Python |python], takže cokoliv, co ho spouští, funguje se všemi.

Proto se hodí i pro AI agenty: když ho agent spustí po každé změně souboru `.neon`, dozví se o rozbitém souboru hned a opraví ho dřív, než pokračuje. [Pluginy Nette pro AI agenty |ai:agent-plugins] to dělají za vás.


Co dál
======

- [Formát NEON |format] - syntaxe formátu
- [Úpravy souborů |editing] - změna souboru se zachováním formátování a syntaktický strom
- [Upgrade |upgrading] - co se změnilo ve verzi 4.0

{{maintitle: NEON pro PHP: čtení, zápis a úpravy souborů NEON}}
{{description: Balíček nette/neon dekóduje a kóduje NEON v PHP, upravuje konfigurační soubory bez ztráty formátování a kontroluje je příkazem neon-lint.}}

NEON pro PHP

Balíček nette/neon čte a zapisuje NEON v PHP. Díky němu:

  • dekódujete konfigurační soubor do hodnot PHP a hodnoty zakódujete zpět do NEONu
  • upravíte soubor tak, že každý komentář a mezera, na které nesáhnete, zůstanou na svém místě
  • dáte některým hodnotám vlastní význam, třeba konstantám nebo parametrům
  • zkontrolujete soubory pomocí neon-lint v editoru, v CI nebo po AI agentovi

Instalace

Knihovnu stáhnete a nainstalujete pomocí nástroje Composer:

composer require nette/neon

Vyžaduje PHP 8.4 až 8.6 s rozšířeními json a mbstring.

Dekódování

Dejme tomu, že vaše aplikace má nastavení v souboru config.neon:

# database connection
database:
	host: localhost
	port: 3306

services:
	- App\Model\ArticleFacade

Statická třída Nette\Neon\Neon ho načte do obyčejného pole PHP:

use Nette\Neon\Neon;

$config = Neon::decodeFile('config.neon');
// ['database' => ['host' => 'localhost', 'port' => 3306], 'services' => ['App\Model\ArticleFacade']]

Metoda decode() dělá totéž s řetězcem:

$value = Neon::decode('hello: world'); // ['hello' => 'world']

Hodnoty formátu se převedou na tyto hodnoty PHP:

NEON PHP
null, true, yes … null, bool
celé číslo int; nad PHP_INT_MAX řetězec string z jeho číslic
desetinné číslo float
řetězec string
datum DateTimeImmutable
entita, řetěz Nette\Neon\Entity
sekvence, mapování array

PHP ukládá sekvence i mapování do polí, takže prázdné [] i prázdné {} se stanou []. Datum bez časové zóny dostane výchozí časovou zónu PHP.

Kódování

Metoda encode() zapíše hodnotu PHP jako NEON. Ve výchozím stavu zapíše vše na jeden řádek; pokud jako druhý argument předáte true, dostanete bloky:

Neon::encode(['roles' => ['guest', 'admin']]);
// '{roles: [guest, admin]}'

Neon::encode(['roles' => ['guest', 'admin']], true);
// "roles:\n\t- guest\n\t- admin\n\n"

Místo true můžete předat funkci, která rozhodne u každého pole zvlášť. Dostane pole a seznam klíčů, které k němu vedou, a pro blok vrátí true. Tahle zapíše bloky jen v první úrovni:

Neon::encode($config, fn($value, $path) => !$path);
// "database: {host: localhost, port: 3306}\nservices: [App\\Model\\ArticleFacade]\n"

Třetí argument určuje odsazení, výchozí je tabulátor. Řetězec dostane uvozovky, jen když je potřebuje, a víceřádkový řetězec se zapíše jako víceřádkový řetězec.

Některé hodnoty jako NEON bez ztráty zapsat nejde: řetězec, který není platné UTF-8, INF a NAN, resource, closure nebo struktura, která obsahuje sama sebe. Pro ně encode() vyhodí Nette\Neon\Exception.

Chyby

Když vstup není platný NEON, decode() a decodeFile() vyhodí Nette\Neon\Exception s pozicí problému:

try {
	$config = Neon::decode("database:\n\thost: localhost\n\thost: db");
} catch (Nette\Neon\Exception $e) {
	echo $e->getMessage();     // Duplicated key 'host' on line 3 at column 2
	echo $e->getDescription(); // Duplicated key 'host'
}

Sloupec se počítá ve znacích, ne v bajtech. Když chybu zobrazujete po svém, třeba u pole formuláře, metoda getDescription() vrátí hlášku bez pozice.

Vlastní hodnoty: Evaluator

Neon::decode() převádí syntaktický strom na hodnoty pomocí třídy Nette\Neon\Evaluator. Její potomek může některým hodnotám dát vlastní význam. Tento převádí názvy konstant PHP na jejich hodnoty:

use Nette\Neon\Evaluator;
use Nette\Neon\Neon;
use Nette\Neon\Nodes\LiteralNode;
use Nette\Neon\Place;

final class ConstantEvaluator extends Evaluator
{
	protected function evaluateLiteral(LiteralNode $node, Place $place): mixed
	{
		$value = parent::evaluateLiteral($node, $place);
		return $place === Place::Value && is_string($value) && defined($value)
			? constant($value)
			: $value;
	}
}

$document = Neon::parse('errorLevel: E_ALL');
$config = new ConstantEvaluator()->evaluate($document->value);
// ['errorLevel' => 30719]

Metody, které můžete přepsat, jsou evaluateLiteral(), evaluateString(), evaluateEntity(), evaluateChain() a evaluateItem(). Place říká, kde hodnota stojí: jako hodnota, jako klíč, jako jméno entity nebo jako článek řetězu. Díky tomu příklad výše nechá na pokoji klíč, který se jmenuje jako konstanta. Takto čte svou konfiguraci DI kontejner Nette.

Kontrola souborů: neon-lint

Balíček obsahuje příkaz neon-lint. Předáte mu soubory nebo adresáře a on vypíše jeden řádek pro každý problém:

vendor/bin/neon-lint config/
config/local.neon:3:2: Duplicated key 'user'

Když jsou všechny soubory platné, nevypíše nic. V adresáři hledá soubory *.neon a vynechá vendor, node_modules a skryté adresáře, - čte standardní vstup. Jeho chování mění tyto volby:

Volba Účinek
--check ověří navíc, že se soubor načte zpět bajt po bajtu
--json vypíše nálezy jako JSON, pro nástroje
--quiet nevypíše nic, jen nastaví návratový kód
--verbose vypíše počet souborů, když jsou všechny platné

Návratový kód je 0, když jsou všechny soubory platné, 1 při nálezu a 2 při chybném volání. Příkaz, jeho výstup i návratové kódy jsou stejné v knihovnách pro JavaScript a Python, takže cokoliv, co ho spouští, funguje se všemi.

Proto se hodí i pro AI agenty: když ho agent spustí po každé změně souboru .neon, dozví se o rozbitém souboru hned a opraví ho dřív, než pokračuje. Pluginy Nette pro AI agenty to dělají za vás.

Co dál