Nette Documentation Preview

syntax
SmartObject
***********

.[perex]
A SmartObject korábban sokféleképpen javította az objektumok viselkedését, de a mai PHP már natívan tartalmazza ezen fejlesztések nagy részét. Azonban még mindig hozzáadja a *tulajdonságok* támogatását.


Telepítés:

```shell
composer require nette/utils
```


Tulajdonságok, Getterek és Setterek .[#toc-properties-getters-and-setters]
==========================================================================

A modern objektumorientált nyelvekben (pl. C#, Python, Ruby, JavaScript) a *tulajdonság* kifejezés az [osztályok speciális tagjaira |https://en.wikipedia.org/wiki/Property_(programming)] utal, amelyek változóknak tűnnek, de valójában metódusok képviselik őket. Amikor ennek a "változónak" az értékét hozzárendeljük vagy kiolvassuk, a megfelelő metódust (az úgynevezett gettert vagy settert) hívjuk meg. Ez egy nagyon praktikus dolog, teljes kontrollt biztosít számunkra a változókhoz való hozzáférés felett. Csak akkor tudjuk érvényesíteni a bemenetet, vagy csak akkor generálhatunk eredményt, ha a tulajdonságot kiolvassuk.

A PHP tulajdonságok nem támogatottak, de a `Nette\SmartObject` trait képes utánozni őket. Hogyan használjuk?

- Adjunk hozzá egy megjegyzést az osztályhoz a következő formában `@property <type> $xyz`
- Hozzon létre egy `getXyz()` vagy `isXyz()` nevű gettert, egy settert, amelynek a neve `setXyz()`
- A getter és a setter *nyilvános* vagy *védett* kell, hogy legyen, és opcionális, tehát lehet *read-only* vagy *write-only* tulajdonság.

A Circle osztály tulajdonságát fogjuk használni annak biztosítására, hogy a `$radius` változóba csak nem negatív számok kerüljenek. Helyettesítsük a `public $radius` címet a tulajdonsággal:

```php
/**
 * @property float $radius
 * @property-read bool $visible
 */
class Circle
{
	use Nette\SmartObject;

	private float $radius = 0.0; // nem nyilvános

	// getter a $radius tulajdonsághoz
	protected function getRadius(): float
	{
		return $this->radius;
	}

	// setter a $radius tulajdonsághoz
	protected function setRadius(float $radius): void
	{
		// az érték mentés előtti szanálása
		$this->radius = max(0.0, $radius);
	}

	// getter a $visible tulajdonsághoz
	protected function isVisible(): bool
	{
		return $this->radius > 0;
	}
}

$circle = new Circle;
$circle->radius = 10; // valójában a setRadius(10) hívása.
echo $circle->radius; // meghívja a getRadius() függvényt.
echo $circle->visible; // hívja az isVisible() függvényt
```

A tulajdonságok elsősorban "szintaktikai cukor"((syntactic sugar)), amelynek célja, hogy a kód egyszerűsítésével megédesítse a programozó életét. Ha nem akarod őket, nem kell használni őket.


Pillantás a történelembe .[#toc-a-glimpse-into-history]
=======================================================

A SmartObject korábban számos módon finomította az objektumok viselkedését, de a mai PHP már natívan tartalmazza ezen fejlesztések nagy részét. A következő szöveg egy nosztalgikus visszatekintés a történelembe, emlékeztetve minket a dolgok fejlődésére.

A PHP objektummodellje a kezdetektől fogva számtalan komoly hiányosságtól és hiányosságtól szenvedett. Ez vezetett a `Nette\Object` osztály létrehozásához (2007-ben), amelynek célja ezen problémák orvoslása és a PHP használatának kényelmesebbé tétele volt. Mindössze arra volt szükség, hogy más osztályok is örököljenek belőle, és máris élvezhették az általa kínált előnyöket. Amikor a PHP 5.4 bevezette a tulajdonságok támogatását, a `Nette\Object` osztály helyébe a `Nette\SmartObject` tulajdonság lépett. Ezzel megszűnt a közös őstől való öröklés szükségessége. Sőt, a tulajdonságot olyan osztályokban is lehetett használni, amelyek már örököltek egy másik osztálytól. A `Nette\Object` végleges megszűnését a PHP 7.2 kiadása jelentette, amely megtiltotta, hogy az osztályok neve `Object` legyen.

A PHP fejlesztésének előrehaladtával az objektummodell és a nyelvi képességek tovább fejlődtek. A `SmartObject` osztály különböző funkciói feleslegessé váltak. A PHP 8.2 kiadása óta csak egyetlen olyan funkció maradt, amelyet a PHP közvetlenül nem támogat: az úgynevezett [tulajdonságok |#Properties, getters, and setters] használatának lehetősége.

Milyen funkciókat kínált a `Nette\Object` és ennek folytán a `Nette\SmartObject`? Íme egy áttekintés. (A példákban a `Nette\Object` osztályt használjuk, de a legtöbb funkció a `Nette\SmartObject` tulajdonságra is vonatkozik).


Inkonzisztens hibák .[#toc-inconsistent-errors]
-----------------------------------------------
A PHP következetlenül viselkedett a nem deklarált tagok elérésekor. Az állapot a `Nette\Object` idején a következő volt:

```php
echo $obj->undeclared; // E_NOTICE, később E_WARNING
$obj->undeclared = 1; // csendben átmegy jelentés nélkül.
$obj->unknownMethod(); // Végzetes hiba (try/catch-el nem fogható)
```

A végzetes hiba mindenféle reagálási lehetőség nélkül megszakította az alkalmazást. A nem létező tagokba való csendes írás figyelmeztetés nélkül súlyos, nehezen észlelhető hibákhoz vezethetett. `Nette\Object` Minden ilyen esetet elkaptunk, és a `MemberAccessException` címen egy kivételt dobtunk.

```php
echo $obj->undeclared; // throw Nette\MemberAccessException
$obj->undeclared = 1; // throw Nette\MemberAccessException
$obj->unknownMethod(); // throw Nette\MemberAccessException
```
A PHP 7.0 óta a PHP már nem okoz nem fogható halálos hibákat, a nem deklarált tagokhoz való hozzáférés pedig a PHP 8.2 óta hiba.


Úgy értetted? .[#toc-did-you-mean]
----------------------------------
Ha egy `Nette\MemberAccessException` hiba lépett fel, például egy objektumváltozó elérésekor vagy egy metódus hívásakor elírás miatt, a `Nette\Object` megpróbált a hibaüzenetben egy tippet adni a hiba kijavítására, az ikonikus "did you mean?" kiegészítés formájában.

```php
class Foo extends Nette\Object
{
	public static function from($var)
	{
	}
}

$foo = Foo::form($var);
// throw Nette\MemberAccessException
// "Call to undefined static method Foo::form(), did you mean from()?"
```

Bár a mai PHP nem rendelkezik "úgy értetted?" funkcióval, ez a mondat hozzáadható a hibákhoz a [Tracy |tracy:] által. Még az [ilyen hibákat |tracy:open-files-in-ide#toc-demos] is képes [automatikusan kijavítani |tracy:open-files-in-ide#toc-demos].


Bővítési módszerek .[#toc-extension-methods]
--------------------------------------------
A C# bővítési metódusok által inspirálva. Lehetőséget adtak új metódusok hozzáadására a meglévő osztályokhoz. Például a `addDateTime()` metódust hozzáadhatta egy űrlaphoz, hogy saját DateTimePickert adjon hozzá.

```php
Form::extensionMethod(
	'addDateTime',
	fn(Form $form, string $name) => $form[$name] = new DateTimePicker,
);

$form = new Form;
$form->addDateTime('date');
```

A kiterjesztési metódusok nem bizonyultak praktikusnak, mert a nevüket a szerkesztők nem töltötték ki automatikusan, hanem azt jelentették, hogy a metódus nem létezik. Ezért a támogatásuk megszűnt.


Az osztály nevének megadása .[#toc-getting-the-class-name]
----------------------------------------------------------

```php
$class = $obj->getClass(); // using Nette\Object
$class = $obj::class; // PHP 8.0 óta
```


Hozzáférés a reflexióhoz és a megjegyzésekhez .[#toc-access-to-reflection-and-annotations]
------------------------------------------------------------------------------------------

`Nette\Object` a `getReflection()` és a `getAnnotation()` metódusok segítségével kínált hozzáférést a reflexióhoz és a megjegyzésekhez:

```php
/**
 * @author John Doe
 */
class Foo extends Nette\Object
{
}

$obj = new Foo;
$reflection = $obj->getReflection();
$reflection->getAnnotation('author'); // visszaadja 'John Doe'
```

A PHP 8.0-tól kezdve a metainformációkat attribútumok formájában is elérhetjük:

```php
#[Author('John Doe')]
class Foo
{
}

$obj = new Foo;
$reflection = new ReflectionObject($obj);
$reflection->getAttributes(Author::class)[0];
```


Metódus Getterek .[#toc-method-getters]
---------------------------------------

`Nette\Object` elegáns módot kínált arra, hogy a módszereket úgy kezeljük, mintha változók lennének:

```php
class Foo extends Nette\Object
{
	public function adder($a, $b)
	{
		return $a + $b;
	}
}

$obj = new Foo;
$method = $obj->adder;
echo $method(2, 3); // 5
```

A PHP 8.1-től kezdve használhatod az úgynevezett "első osztályú hívható szintaxist":https://www.php.net/manual/en/functions.first_class_callable_syntax:

```php
$obj = new Foo;
$method = $obj->adder(...);
echo $method(2, 3); // 5
```


Események .[#toc-events]
------------------------

`Nette\Object` szintaktikai cukrot kínált az [esemény |nette:glossary#events] kiváltásához:

```php
class Circle extends Nette\Object
{
	public array $onChange = [];

	public function setRadius(float $radius): void
	{
		$this->onChange($this, $radius);
		$this->radius = $radius;
	}
}
```

A `$this->onChange($this, $radius)` kód a következővel egyenértékű:

```php
foreach ($this->onChange as $callback) {
	$callback($this, $radius);
}
```

Az áttekinthetőség kedvéért javasoljuk, hogy kerüljük a `$this->onChange()` varázsmódszert. Ennek praktikus helyettesítője a [Nette\Utils\Arrays::invoke |arrays#invoke] függvény:

```php
Nette\Utils\Arrays::invoke($this->onChange, $this, $radius);
```

SmartObject

A SmartObject korábban sokféleképpen javította az objektumok viselkedését, de a mai PHP már natívan tartalmazza ezen fejlesztések nagy részét. Azonban még mindig hozzáadja a tulajdonságok támogatását.

Telepítés:

composer require nette/utils

Tulajdonságok, Getterek és Setterek

A modern objektumorientált nyelvekben (pl. C#, Python, Ruby, JavaScript) a tulajdonság kifejezés az osztályok speciális tagjaira utal, amelyek változóknak tűnnek, de valójában metódusok képviselik őket. Amikor ennek a „változónak“ az értékét hozzárendeljük vagy kiolvassuk, a megfelelő metódust (az úgynevezett gettert vagy settert) hívjuk meg. Ez egy nagyon praktikus dolog, teljes kontrollt biztosít számunkra a változókhoz való hozzáférés felett. Csak akkor tudjuk érvényesíteni a bemenetet, vagy csak akkor generálhatunk eredményt, ha a tulajdonságot kiolvassuk.

A PHP tulajdonságok nem támogatottak, de a Nette\SmartObject trait képes utánozni őket. Hogyan használjuk?

  • Adjunk hozzá egy megjegyzést az osztályhoz a következő formában @property <type> $xyz
  • Hozzon létre egy getXyz() vagy isXyz() nevű gettert, egy settert, amelynek a neve setXyz()
  • A getter és a setter nyilvános vagy védett kell, hogy legyen, és opcionális, tehát lehet read-only vagy write-only tulajdonság.

A Circle osztály tulajdonságát fogjuk használni annak biztosítására, hogy a $radius változóba csak nem negatív számok kerüljenek. Helyettesítsük a public $radius címet a tulajdonsággal:

/**
 * @property float $radius
 * @property-read bool $visible
 */
class Circle
{
	use Nette\SmartObject;

	private float $radius = 0.0; // nem nyilvános

	// getter a $radius tulajdonsághoz
	protected function getRadius(): float
	{
		return $this->radius;
	}

	// setter a $radius tulajdonsághoz
	protected function setRadius(float $radius): void
	{
		// az érték mentés előtti szanálása
		$this->radius = max(0.0, $radius);
	}

	// getter a $visible tulajdonsághoz
	protected function isVisible(): bool
	{
		return $this->radius > 0;
	}
}

$circle = new Circle;
$circle->radius = 10; // valójában a setRadius(10) hívása.
echo $circle->radius; // meghívja a getRadius() függvényt.
echo $circle->visible; // hívja az isVisible() függvényt

A tulajdonságok elsősorban szintaktikai cukor, amelynek célja, hogy a kód egyszerűsítésével megédesítse a programozó életét. Ha nem akarod őket, nem kell használni őket.

Pillantás a történelembe

A SmartObject korábban számos módon finomította az objektumok viselkedését, de a mai PHP már natívan tartalmazza ezen fejlesztések nagy részét. A következő szöveg egy nosztalgikus visszatekintés a történelembe, emlékeztetve minket a dolgok fejlődésére.

A PHP objektummodellje a kezdetektől fogva számtalan komoly hiányosságtól és hiányosságtól szenvedett. Ez vezetett a Nette\Object osztály létrehozásához (2007-ben), amelynek célja ezen problémák orvoslása és a PHP használatának kényelmesebbé tétele volt. Mindössze arra volt szükség, hogy más osztályok is örököljenek belőle, és máris élvezhették az általa kínált előnyöket. Amikor a PHP 5.4 bevezette a tulajdonságok támogatását, a Nette\Object osztály helyébe a Nette\SmartObject tulajdonság lépett. Ezzel megszűnt a közös őstől való öröklés szükségessége. Sőt, a tulajdonságot olyan osztályokban is lehetett használni, amelyek már örököltek egy másik osztálytól. A Nette\Object végleges megszűnését a PHP 7.2 kiadása jelentette, amely megtiltotta, hogy az osztályok neve Object legyen.

A PHP fejlesztésének előrehaladtával az objektummodell és a nyelvi képességek tovább fejlődtek. A SmartObject osztály különböző funkciói feleslegessé váltak. A PHP 8.2 kiadása óta csak egyetlen olyan funkció maradt, amelyet a PHP közvetlenül nem támogat: az úgynevezett tulajdonságok használatának lehetősége.

Milyen funkciókat kínált a Nette\Object és ennek folytán a Nette\SmartObject? Íme egy áttekintés. (A példákban a Nette\Object osztályt használjuk, de a legtöbb funkció a Nette\SmartObject tulajdonságra is vonatkozik).

Inkonzisztens hibák

A PHP következetlenül viselkedett a nem deklarált tagok elérésekor. Az állapot a Nette\Object idején a következő volt:

echo $obj->undeclared; // E_NOTICE, később E_WARNING
$obj->undeclared = 1; // csendben átmegy jelentés nélkül.
$obj->unknownMethod(); // Végzetes hiba (try/catch-el nem fogható)

A végzetes hiba mindenféle reagálási lehetőség nélkül megszakította az alkalmazást. A nem létező tagokba való csendes írás figyelmeztetés nélkül súlyos, nehezen észlelhető hibákhoz vezethetett. Nette\Object Minden ilyen esetet elkaptunk, és a MemberAccessException címen egy kivételt dobtunk.

echo $obj->undeclared; // throw Nette\MemberAccessException
$obj->undeclared = 1; // throw Nette\MemberAccessException
$obj->unknownMethod(); // throw Nette\MemberAccessException

A PHP 7.0 óta a PHP már nem okoz nem fogható halálos hibákat, a nem deklarált tagokhoz való hozzáférés pedig a PHP 8.2 óta hiba.

Úgy értetted?

Ha egy Nette\MemberAccessException hiba lépett fel, például egy objektumváltozó elérésekor vagy egy metódus hívásakor elírás miatt, a Nette\Object megpróbált a hibaüzenetben egy tippet adni a hiba kijavítására, az ikonikus „did you mean?“ kiegészítés formájában.

class Foo extends Nette\Object
{
	public static function from($var)
	{
	}
}

$foo = Foo::form($var);
// throw Nette\MemberAccessException
// "Call to undefined static method Foo::form(), did you mean from()?"

Bár a mai PHP nem rendelkezik „úgy értetted?“ funkcióval, ez a mondat hozzáadható a hibákhoz a Tracy által. Még az ilyen hibákat is képes automatikusan kijavítani.

Bővítési módszerek

A C# bővítési metódusok által inspirálva. Lehetőséget adtak új metódusok hozzáadására a meglévő osztályokhoz. Például a addDateTime() metódust hozzáadhatta egy űrlaphoz, hogy saját DateTimePickert adjon hozzá.

Form::extensionMethod(
	'addDateTime',
	fn(Form $form, string $name) => $form[$name] = new DateTimePicker,
);

$form = new Form;
$form->addDateTime('date');

A kiterjesztési metódusok nem bizonyultak praktikusnak, mert a nevüket a szerkesztők nem töltötték ki automatikusan, hanem azt jelentették, hogy a metódus nem létezik. Ezért a támogatásuk megszűnt.

Az osztály nevének megadása

$class = $obj->getClass(); // using Nette\Object
$class = $obj::class; // PHP 8.0 óta

Hozzáférés a reflexióhoz és a megjegyzésekhez

Nette\Object a getReflection() és a getAnnotation() metódusok segítségével kínált hozzáférést a reflexióhoz és a megjegyzésekhez:

/**
 * @author John Doe
 */
class Foo extends Nette\Object
{
}

$obj = new Foo;
$reflection = $obj->getReflection();
$reflection->getAnnotation('author'); // visszaadja 'John Doe'

A PHP 8.0-tól kezdve a metainformációkat attribútumok formájában is elérhetjük:

#[Author('John Doe')]
class Foo
{
}

$obj = new Foo;
$reflection = new ReflectionObject($obj);
$reflection->getAttributes(Author::class)[0];

Metódus Getterek

Nette\Object elegáns módot kínált arra, hogy a módszereket úgy kezeljük, mintha változók lennének:

class Foo extends Nette\Object
{
	public function adder($a, $b)
	{
		return $a + $b;
	}
}

$obj = new Foo;
$method = $obj->adder;
echo $method(2, 3); // 5

A PHP 8.1-től kezdve használhatod az úgynevezett első osztályú hívható szintaxist:

$obj = new Foo;
$method = $obj->adder(...);
echo $method(2, 3); // 5

Események

Nette\Object szintaktikai cukrot kínált az esemény kiváltásához:

class Circle extends Nette\Object
{
	public array $onChange = [];

	public function setRadius(float $radius): void
	{
		$this->onChange($this, $radius);
		$this->radius = $radius;
	}
}

A $this->onChange($this, $radius) kód a következővel egyenértékű:

foreach ($this->onChange as $callback) {
	$callback($this, $radius);
}

Az áttekinthetőség kedvéért javasoljuk, hogy kerüljük a $this->onChange() varázsmódszert. Ennek praktikus helyettesítője a Nette\Utils\Arrays::invoke függvény:

Nette\Utils\Arrays::invoke($this->onChange, $this, $radius);