Nette Documentation Preview

syntax
SmartObject und StaticClass
***************************

.[perex]
SmartObject fügt PHP-Klassen Unterstützung für *Eigenschaften* hinzu. StaticClass wird verwendet, um statische Klassen zu bezeichnen.


Installation:

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


Eigenschaften, Getters und Setters .[#toc-properties-getters-and-setters]
=========================================================================

In modernen objektorientierten Sprachen (z. B. C#, Python, Ruby, JavaScript) bezieht sich der Begriff *Eigenschaft* auf [spezielle Mitglieder von Klassen |https://en.wikipedia.org/wiki/Property_(programming)], die wie Variablen aussehen, aber eigentlich durch Methoden repräsentiert werden. Wenn der Wert dieser "Variablen" zugewiesen oder gelesen wird, wird die entsprechende Methode (Getter oder Setter genannt) aufgerufen. Das ist sehr praktisch, denn es gibt uns die volle Kontrolle über den Zugriff auf Variablen. Wir können die Eingabe validieren oder Ergebnisse nur dann erzeugen, wenn die Eigenschaft gelesen wird.

PHP-Eigenschaften werden nicht unterstützt, aber Trait `Nette\SmartObject` kann sie imitieren. Wie verwendet man es?

- Fügen Sie der Klasse eine Annotation in der Form `@property <type> $xyz`
- Erstellen Sie einen Getter namens `getXyz()` oder `isXyz()`, einen Setter namens `setXyz()`
- Die Getter und Setter müssen *public* oder *protected* sein und sind optional, d.h. es kann eine *read-only* oder *write-only* Eigenschaft geben

Wir werden die Eigenschaft für die Klasse Circle verwenden, um sicherzustellen, dass nur nicht-negative Zahlen in die Variable `$radius` eingegeben werden. Ersetzen Sie `public $radius` durch property:

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

	private float $radius = 0.0; // not public

	// getter for property $radius
	protected function getRadius(): float
	{
		return $this->radius;
	}

	// setter for property $radius
	protected function setRadius(float $radius): void
	{
		// sanitizing value before saving it
		$this->radius = max(0.0, $radius);
	}

	// getter for property $visible
	protected function isVisible(): bool
	{
		return $this->radius > 0;
	}
}

$circle = new Circle;
$circle->radius = 10;  // actually calls setRadius(10)
echo $circle->radius;  // calls getRadius()
echo $circle->visible; // calls isVisible()
```

Eigenschaften sind in erster Linie "syntaktischer Zucker" ((syntactic sugar)), der das Leben des Programmierers durch Vereinfachung des Codes versüßen soll. Wenn Sie sie nicht wollen, müssen Sie sie nicht verwenden.


Statische Klassen .[#toc-static-classes]
========================================

Statische Klassen, d.h. Klassen, die nicht instanziiert werden sollen, können mit dem Trait `Nette\StaticClass` gekennzeichnet werden:

```php
class Strings
{
	use Nette\StaticClass;
}
```

Wenn Sie versuchen, eine Instanz zu erstellen, wird die Ausnahme `Error` ausgelöst, die angibt, dass die Klasse statisch ist.


Ein Blick in die Geschichte .[#toc-a-look-into-the-history]
===========================================================

SmartObject hat das Verhalten von Klassen in vielerlei Hinsicht verbessert und korrigiert, aber die Entwicklung von PHP hat die meisten der ursprünglichen Funktionen überflüssig gemacht. Im Folgenden werfen wir einen Blick in die Geschichte, wie sich die Dinge entwickelt haben.

Von Anfang an litt das PHP-Objektmodell unter einer Reihe von schwerwiegenden Mängeln und Ineffizienzen. Dies war der Grund für die Schaffung der Klasse `Nette\Object` (im Jahr 2007), mit der versucht wurde, diese zu beheben und die Benutzung von PHP zu verbessern. Es reichte aus, dass andere Klassen von ihr erbten und von den Vorteilen profitierten, die sie mit sich brachte. Als PHP 5.4 mit Trait-Unterstützung kam, wurde die Klasse `Nette\Object` durch `Nette\SmartObject` ersetzt. Somit war es nicht mehr notwendig, von einem gemeinsamen Vorfahren zu erben. Darüber hinaus konnte Trait in Klassen verwendet werden, die bereits von einer anderen Klasse geerbt hatten. Das endgültige Ende von `Nette\Object` kam mit der Veröffentlichung von PHP 7.2, die es verbot, Klassen den Namen `Object` zu geben.

Im weiteren Verlauf der PHP-Entwicklung wurden das Objektmodell und die Sprachfähigkeiten verbessert. Die einzelnen Funktionen der Klasse `SmartObject` wurden überflüssig. Seit der Veröffentlichung von PHP 8.2 ist die einzige Funktion, die noch nicht direkt in PHP unterstützt wird, die Möglichkeit, sogenannte [Properties |#Properties, getters and setters] zu verwenden.

Welche Funktionen boten `Nette\Object` und `Nette\Object` früher? Hier ist ein Überblick. (Die Beispiele verwenden die Klasse `Nette\Object`, aber die meisten der Eigenschaften gelten auch für die Eigenschaft `Nette\SmartObject` ).


Inkonsistente Fehler .[#toc-inconsistent-errors]
------------------------------------------------
PHP hatte ein inkonsistentes Verhalten beim Zugriff auf nicht deklarierte Mitglieder. Der Zustand zum Zeitpunkt von `Nette\Object` war wie folgt:

```php
echo $obj->undeclared; // E_NOTICE, later E_WARNING
$obj->undeclared = 1;  // passes silently without reporting
$obj->unknownMethod(); // Fatal error (not catchable by try/catch)
```

Ein schwerwiegender Fehler beendete die Anwendung, ohne dass eine Möglichkeit zur Reaktion bestand. Das stille Schreiben auf nicht existierende Mitglieder ohne Warnung konnte zu schwerwiegenden Fehlern führen, die schwer zu erkennen waren. `Nette\Object` Alle diese Fälle wurden abgefangen und eine Ausnahme `MemberAccessException` wurde ausgelöst.

```php
echo $obj->undeclared;   // throw Nette\MemberAccessException
$obj->undeclared = 1;    // throw Nette\MemberAccessException
$obj->unknownMethod();   // throw Nette\MemberAccessException
```
Seit PHP 7.0 verursacht PHP keine nicht abfangbaren fatalen Fehler mehr, und der Zugriff auf nicht deklarierte Member ist seit PHP 8.2 ein Fehler.


Haben Sie gemeint? .[#toc-did-you-mean]
---------------------------------------
Wenn ein `Nette\MemberAccessException` -Fehler ausgelöst wurde, etwa aufgrund eines Tippfehlers beim Zugriff auf eine Objektvariable oder beim Aufruf einer Methode, versuchte `Nette\Object`, in der Fehlermeldung einen Hinweis darauf zu geben, wie der Fehler zu beheben ist, und zwar in Form des ikonischen Zusatzes "Meinten Sie?".

```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()?"
```

Das heutige PHP hat zwar keine Form von "did you mean?", aber [Tracy |tracy:] fügt diesen Zusatz zu Fehlern hinzu. Und es kann solche Fehler sogar selbst [beheben |tracy:open-files-in-ide#toc-demos].


Erweiterungsmethoden .[#toc-extension-methods]
----------------------------------------------
Inspiriert von Erweiterungsmethoden aus C#. Sie bieten die Möglichkeit, neue Methoden zu bestehenden Klassen hinzuzufügen. Zum Beispiel könnten Sie die Methode `addDateTime()` zu einem Formular hinzufügen, um Ihren eigenen DateTimePicker hinzuzufügen.

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

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

Erweiterungsmethoden erwiesen sich als unpraktisch, da ihre Namen von Editoren nicht automatisch vervollständigt wurden, sondern sie meldeten, dass die Methode nicht existierte. Daher wurde ihre Unterstützung eingestellt.


Ermitteln des Klassennamens .[#toc-getting-the-class-name]
----------------------------------------------------------

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


Zugang zu Reflexion und Anmerkungen .[#toc-access-to-reflection-and-annotations]
--------------------------------------------------------------------------------

`Nette\Object` bietet den Zugang zu Reflexion und Kommentaren mit den Methoden `getReflection()` und `getAnnotation()`:

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

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

Seit PHP 8.0 ist es möglich, auf Metainformationen in Form von Attributen zuzugreifen:

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

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


Methoden-Getter .[#toc-method-getters]
--------------------------------------

`Nette\Object` boten eine elegante Möglichkeit, mit Methoden so umzugehen, als wären sie Variablen:

```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
```

Seit PHP 8.1 können Sie die sogenannte "First-Class-Callable-Syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax verwenden:

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


Ereignisse .[#toc-events]
-------------------------

`Nette\Object` bietet syntaktischen Zucker zum Auslösen des [Ereignisses |nette:glossary#events]:

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

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

Der Code `$this->onChange($this, $radius)` entspricht folgendem:

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

Aus Gründen der Übersichtlichkeit wird empfohlen, die magische Methode `$this->onChange()` zu vermeiden. Ein guter Ersatz ist [Nette\Utils\Arrays::invoke |arrays#invoke]:

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

SmartObject und StaticClass

SmartObject fügt PHP-Klassen Unterstützung für Eigenschaften hinzu. StaticClass wird verwendet, um statische Klassen zu bezeichnen.

Installation:

composer require nette/utils

Eigenschaften, Getters und Setters

In modernen objektorientierten Sprachen (z. B. C#, Python, Ruby, JavaScript) bezieht sich der Begriff Eigenschaft auf spezielle Mitglieder von Klassen, die wie Variablen aussehen, aber eigentlich durch Methoden repräsentiert werden. Wenn der Wert dieser „Variablen“ zugewiesen oder gelesen wird, wird die entsprechende Methode (Getter oder Setter genannt) aufgerufen. Das ist sehr praktisch, denn es gibt uns die volle Kontrolle über den Zugriff auf Variablen. Wir können die Eingabe validieren oder Ergebnisse nur dann erzeugen, wenn die Eigenschaft gelesen wird.

PHP-Eigenschaften werden nicht unterstützt, aber Trait Nette\SmartObject kann sie imitieren. Wie verwendet man es?

  • Fügen Sie der Klasse eine Annotation in der Form @property <type> $xyz
  • Erstellen Sie einen Getter namens getXyz() oder isXyz(), einen Setter namens setXyz()
  • Die Getter und Setter müssen public oder protected sein und sind optional, d.h. es kann eine read-only oder write-only Eigenschaft geben

Wir werden die Eigenschaft für die Klasse Circle verwenden, um sicherzustellen, dass nur nicht-negative Zahlen in die Variable $radius eingegeben werden. Ersetzen Sie public $radius durch property:

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

	private float $radius = 0.0; // not public

	// getter for property $radius
	protected function getRadius(): float
	{
		return $this->radius;
	}

	// setter for property $radius
	protected function setRadius(float $radius): void
	{
		// sanitizing value before saving it
		$this->radius = max(0.0, $radius);
	}

	// getter for property $visible
	protected function isVisible(): bool
	{
		return $this->radius > 0;
	}
}

$circle = new Circle;
$circle->radius = 10;  // actually calls setRadius(10)
echo $circle->radius;  // calls getRadius()
echo $circle->visible; // calls isVisible()

Eigenschaften sind in erster Linie „syntaktischer Zucker“ ((syntactic sugar)), der das Leben des Programmierers durch Vereinfachung des Codes versüßen soll. Wenn Sie sie nicht wollen, müssen Sie sie nicht verwenden.

Statische Klassen

Statische Klassen, d.h. Klassen, die nicht instanziiert werden sollen, können mit dem Trait Nette\StaticClass gekennzeichnet werden:

class Strings
{
	use Nette\StaticClass;
}

Wenn Sie versuchen, eine Instanz zu erstellen, wird die Ausnahme Error ausgelöst, die angibt, dass die Klasse statisch ist.

Ein Blick in die Geschichte

SmartObject hat das Verhalten von Klassen in vielerlei Hinsicht verbessert und korrigiert, aber die Entwicklung von PHP hat die meisten der ursprünglichen Funktionen überflüssig gemacht. Im Folgenden werfen wir einen Blick in die Geschichte, wie sich die Dinge entwickelt haben.

Von Anfang an litt das PHP-Objektmodell unter einer Reihe von schwerwiegenden Mängeln und Ineffizienzen. Dies war der Grund für die Schaffung der Klasse Nette\Object (im Jahr 2007), mit der versucht wurde, diese zu beheben und die Benutzung von PHP zu verbessern. Es reichte aus, dass andere Klassen von ihr erbten und von den Vorteilen profitierten, die sie mit sich brachte. Als PHP 5.4 mit Trait-Unterstützung kam, wurde die Klasse Nette\Object durch Nette\SmartObject ersetzt. Somit war es nicht mehr notwendig, von einem gemeinsamen Vorfahren zu erben. Darüber hinaus konnte Trait in Klassen verwendet werden, die bereits von einer anderen Klasse geerbt hatten. Das endgültige Ende von Nette\Object kam mit der Veröffentlichung von PHP 7.2, die es verbot, Klassen den Namen Object zu geben.

Im weiteren Verlauf der PHP-Entwicklung wurden das Objektmodell und die Sprachfähigkeiten verbessert. Die einzelnen Funktionen der Klasse SmartObject wurden überflüssig. Seit der Veröffentlichung von PHP 8.2 ist die einzige Funktion, die noch nicht direkt in PHP unterstützt wird, die Möglichkeit, sogenannte Properties zu verwenden.

Welche Funktionen boten Nette\Object und Nette\Object früher? Hier ist ein Überblick. (Die Beispiele verwenden die Klasse Nette\Object, aber die meisten der Eigenschaften gelten auch für die Eigenschaft Nette\SmartObject ).

Inkonsistente Fehler

PHP hatte ein inkonsistentes Verhalten beim Zugriff auf nicht deklarierte Mitglieder. Der Zustand zum Zeitpunkt von Nette\Object war wie folgt:

echo $obj->undeclared; // E_NOTICE, later E_WARNING
$obj->undeclared = 1;  // passes silently without reporting
$obj->unknownMethod(); // Fatal error (not catchable by try/catch)

Ein schwerwiegender Fehler beendete die Anwendung, ohne dass eine Möglichkeit zur Reaktion bestand. Das stille Schreiben auf nicht existierende Mitglieder ohne Warnung konnte zu schwerwiegenden Fehlern führen, die schwer zu erkennen waren. Nette\Object Alle diese Fälle wurden abgefangen und eine Ausnahme MemberAccessException wurde ausgelöst.

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

Seit PHP 7.0 verursacht PHP keine nicht abfangbaren fatalen Fehler mehr, und der Zugriff auf nicht deklarierte Member ist seit PHP 8.2 ein Fehler.

Haben Sie gemeint?

Wenn ein Nette\MemberAccessException -Fehler ausgelöst wurde, etwa aufgrund eines Tippfehlers beim Zugriff auf eine Objektvariable oder beim Aufruf einer Methode, versuchte Nette\Object, in der Fehlermeldung einen Hinweis darauf zu geben, wie der Fehler zu beheben ist, und zwar in Form des ikonischen Zusatzes „Meinten Sie?“.

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()?"

Das heutige PHP hat zwar keine Form von „did you mean?“, aber Tracy fügt diesen Zusatz zu Fehlern hinzu. Und es kann solche Fehler sogar selbst beheben.

Erweiterungsmethoden

Inspiriert von Erweiterungsmethoden aus C#. Sie bieten die Möglichkeit, neue Methoden zu bestehenden Klassen hinzuzufügen. Zum Beispiel könnten Sie die Methode addDateTime() zu einem Formular hinzufügen, um Ihren eigenen DateTimePicker hinzuzufügen.

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

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

Erweiterungsmethoden erwiesen sich als unpraktisch, da ihre Namen von Editoren nicht automatisch vervollständigt wurden, sondern sie meldeten, dass die Methode nicht existierte. Daher wurde ihre Unterstützung eingestellt.

Ermitteln des Klassennamens

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

Zugang zu Reflexion und Anmerkungen

Nette\Object bietet den Zugang zu Reflexion und Kommentaren mit den Methoden getReflection() und getAnnotation():

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

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

Seit PHP 8.0 ist es möglich, auf Metainformationen in Form von Attributen zuzugreifen:

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

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

Methoden-Getter

Nette\Object boten eine elegante Möglichkeit, mit Methoden so umzugehen, als wären sie Variablen:

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

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

Seit PHP 8.1 können Sie die sogenannte First-Class-Callable-Syntax verwenden:

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

Ereignisse

Nette\Object bietet syntaktischen Zucker zum Auslösen des Ereignisses:

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

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

Der Code $this->onChange($this, $radius) entspricht folgendem:

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

Aus Gründen der Übersichtlichkeit wird empfohlen, die magische Methode $this->onChange() zu vermeiden. Ein guter Ersatz ist Nette\Utils\Arrays::invoke:

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