Nette Documentation Preview

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

.[perex]
O SmartObject costumava corrigir o comportamento dos objetos de várias maneiras, mas o PHP atual já inclui a maioria desses aprimoramentos nativamente. No entanto, ele ainda adiciona suporte para *property*.


Instalação:

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


Propriedades, Getters e Setters .[#toc-properties-getters-and-setters]
======================================================================

Em linguagens modernas orientadas a objetos (por exemplo, C#, Python, Ruby, JavaScript), o termo *propriedade* se refere a [membros especiais de classes |https://en.wikipedia.org/wiki/Property_(programming)] que se parecem com variáveis, mas que na verdade são representadas por métodos. Quando o valor desta "variável" é atribuído ou lido, o método correspondente (chamado getter ou setter) é chamado. Isto é uma coisa muito útil de se fazer, nos dá total controle sobre o acesso às variáveis. Podemos validar a entrada ou gerar resultados somente quando a propriedade é lida.

As propriedades do PHP não são suportadas, mas o traço `Nette\SmartObject` pode imitá-las. Como utilizá-lo?

- Acrescentar uma anotação à classe no formulário `@property <type> $xyz`
- Crie um getter chamado `getXyz()` ou `isXyz()`, um setter chamado `setXyz()`
- O getter e o setter devem ser *públicos* ou *protegidos* e são opcionais, portanto pode haver uma propriedade *somente de leitura* ou *somente de escrita*.

Utilizaremos a propriedade da classe Circle para garantir que somente números não-negativos sejam colocados na variável `$radius`. Substituir `public $radius` por propriedade:

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

	private float $radius = 0.0; // não público

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

	// setter para propriedade $radius
	protected function setRadius(float $radius): void
	{
		// valor higienizante antes de salvá-lo
		$this->radius = max(0.0, $radius);
	}

	// adquirente por propriedade $visível
	protected function isVisible(): bool
	{
		return $this->radius > 0;
	}
}

$circle = new Circle;
$circle->radius = 10; // na verdade chama setRadius(10)
echo $circle->radius; // chama getRadius()
echo $circle->visible; // chamadas isVisible()
```

As propriedades são principalmente o "açúcar sintático"((açúcar sintático)), que se destina a tornar a vida do programador mais doce, simplificando o código. Se você não os quer, não precisa usá-los.


Um vislumbre da história .[#toc-a-glimpse-into-history]
=======================================================

O SmartObject costumava refinar o comportamento dos objetos de várias maneiras, mas o PHP atual já incorpora a maioria desses aprimoramentos de forma nativa. O texto a seguir é um olhar nostálgico para a história, lembrando-nos de como as coisas evoluíram.

Desde seu início, o modelo de objeto do PHP sofria de uma infinidade de falhas e deficiências graves. Isso levou à criação da classe `Nette\Object` (em 2007), cujo objetivo era corrigir esses problemas e aumentar o conforto do uso do PHP. Tudo o que era necessário era que outras classes herdassem essa classe e obtivessem os benefícios que ela oferecia. Quando o PHP 5.4 introduziu o suporte a características, a classe `Nette\Object` foi substituída pela característica `Nette\SmartObject`. Isso eliminou a necessidade de herdar de um ancestral comum. Além disso, a característica poderia ser usada em classes que já herdavam de outra classe. O fim definitivo de `Nette\Object` veio com o lançamento do PHP 7.2, que proibiu que as classes fossem nomeadas `Object`.

À medida que o desenvolvimento do PHP continuava, seu modelo de objeto e os recursos da linguagem foram aprimorados. Várias funções da classe `SmartObject` tornaram-se redundantes. Desde o lançamento do PHP 8.2, resta apenas um recurso não suportado diretamente no PHP: a capacidade de usar as chamadas [propriedades |#Properties, getters, and setters].

Quais recursos o `Nette\Object` e, por extensão, o `Nette\SmartObject` ofereceram? Aqui está uma visão geral. (Nos exemplos, a classe `Nette\Object` é usada, mas a maioria dos recursos também se aplica à característica `Nette\SmartObject` ).


Erros Inconsistentes .[#toc-inconsistent-errors]
------------------------------------------------
O PHP tinha um comportamento inconsistente ao acessar membros não declarados. O estado, na época de `Nette\Object`, era o seguinte:

```php
echo $obj->undeclared; // E_NOTICE, mais tarde E_WARNING
$obj->undeclared = 1; // passa silenciosamente sem informar
$obj->unknownMethod(); // Erro fatal (não detectável por tentativa/captura)
```

O erro fatal terminou a aplicação sem qualquer possibilidade de reação. Escrever silenciosamente a membros inexistentes sem aviso prévio poderia levar a erros graves que eram difíceis de detectar. `Nette\Object` Todos estes casos foram pegos e uma exceção `MemberAccessException` foi lançada.

```php
echo $obj->undeclared; // jogue Nette\MemberAccessException
$obj->undeclared = 1; // jogar Nette\MemberAccessException
$obj->unknownMethod(); // jogar Nette\MemberAccessException
```
Desde o PHP 7.0, o PHP não causa mais erros fatais não detectáveis, e acessar membros não declarados tem sido um erro desde o PHP 8.2.


Você quis dizer? .[#toc-did-you-mean]
-------------------------------------
Se um erro `Nette\MemberAccessException` foi lançado, talvez devido a um erro de digitação ao acessar uma variável de objeto ou ao chamar um método, `Nette\Object` tentou dar uma dica na mensagem de erro sobre como corrigir o erro, na forma do icônico adendo "você quis dizer...".

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

Embora o PHP atual não tenha um recurso "você quis dizer?", essa frase pode ser adicionada aos erros pela [Tracy |tracy:]. Ele pode até mesmo [corrigir automaticamente esses erros |tracy:open-files-in-ide#toc-demos].


Métodos de extensão .[#toc-extension-methods]
---------------------------------------------
Inspirado pelos métodos de extensão da C#. Eles deram a possibilidade de adicionar novos métodos às classes existentes. Por exemplo, você poderia adicionar o método `addDateTime()` a um formulário para adicionar seu próprio DateTimePicker.

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

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

Os métodos de extensão se mostraram impraticáveis porque seus nomes não foram autocompletados pelos editores, em vez disso, eles relataram que o método não existia. Portanto, seu apoio foi descontinuado.


Como obter o nome da classe .[#toc-getting-the-class-name]
----------------------------------------------------------

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


Acesso à Reflexão e Anotações .[#toc-access-to-reflection-and-annotations]
--------------------------------------------------------------------------

`Nette\Object` ofereceu acesso à reflexão e à anotação utilizando os métodos `getReflection()` e `getAnnotation()`:

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

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

A partir do PHP 8.0, é possível acessar meta-informação sob a forma de atributos:

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

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


Método Getters .[#toc-method-getters]
-------------------------------------

`Nette\Object` oferecia uma maneira elegante de lidar com métodos como se fossem variáveis:

```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 partir do PHP 8.1, você pode usar a chamada "sintaxe de primeira classe que pode ser chamada":https://www.php.net/manual/en/functions.first_class_callable_syntax:

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


Eventos .[#toc-events]
----------------------

`Nette\Object` ofereceu açúcar sintáctico para acionar o [evento |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;
	}
}
```

O código `$this->onChange($this, $radius)` é equivalente ao seguinte:

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

Por uma questão de clareza, recomendamos evitar o método mágico `$this->onChange()`. Um substituto prático é a [Arrays Nette\Utils::invoke |arrays#invoke] function:

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

SmartObject

O SmartObject costumava corrigir o comportamento dos objetos de várias maneiras, mas o PHP atual já inclui a maioria desses aprimoramentos nativamente. No entanto, ele ainda adiciona suporte para property.

Instalação:

composer require nette/utils

Propriedades, Getters e Setters

Em linguagens modernas orientadas a objetos (por exemplo, C#, Python, Ruby, JavaScript), o termo propriedade se refere a membros especiais de classes que se parecem com variáveis, mas que na verdade são representadas por métodos. Quando o valor desta „variável“ é atribuído ou lido, o método correspondente (chamado getter ou setter) é chamado. Isto é uma coisa muito útil de se fazer, nos dá total controle sobre o acesso às variáveis. Podemos validar a entrada ou gerar resultados somente quando a propriedade é lida.

As propriedades do PHP não são suportadas, mas o traço Nette\SmartObject pode imitá-las. Como utilizá-lo?

  • Acrescentar uma anotação à classe no formulário @property <type> $xyz
  • Crie um getter chamado getXyz() ou isXyz(), um setter chamado setXyz()
  • O getter e o setter devem ser públicos ou protegidos e são opcionais, portanto pode haver uma propriedade somente de leitura ou somente de escrita.

Utilizaremos a propriedade da classe Circle para garantir que somente números não-negativos sejam colocados na variável $radius. Substituir public $radius por propriedade:

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

	private float $radius = 0.0; // não público

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

	// setter para propriedade $radius
	protected function setRadius(float $radius): void
	{
		// valor higienizante antes de salvá-lo
		$this->radius = max(0.0, $radius);
	}

	// adquirente por propriedade $visível
	protected function isVisible(): bool
	{
		return $this->radius > 0;
	}
}

$circle = new Circle;
$circle->radius = 10; // na verdade chama setRadius(10)
echo $circle->radius; // chama getRadius()
echo $circle->visible; // chamadas isVisible()

As propriedades são principalmente o açúcar sintático, que se destina a tornar a vida do programador mais doce, simplificando o código. Se você não os quer, não precisa usá-los.

Um vislumbre da história

O SmartObject costumava refinar o comportamento dos objetos de várias maneiras, mas o PHP atual já incorpora a maioria desses aprimoramentos de forma nativa. O texto a seguir é um olhar nostálgico para a história, lembrando-nos de como as coisas evoluíram.

Desde seu início, o modelo de objeto do PHP sofria de uma infinidade de falhas e deficiências graves. Isso levou à criação da classe Nette\Object (em 2007), cujo objetivo era corrigir esses problemas e aumentar o conforto do uso do PHP. Tudo o que era necessário era que outras classes herdassem essa classe e obtivessem os benefícios que ela oferecia. Quando o PHP 5.4 introduziu o suporte a características, a classe Nette\Object foi substituída pela característica Nette\SmartObject. Isso eliminou a necessidade de herdar de um ancestral comum. Além disso, a característica poderia ser usada em classes que já herdavam de outra classe. O fim definitivo de Nette\Object veio com o lançamento do PHP 7.2, que proibiu que as classes fossem nomeadas Object.

À medida que o desenvolvimento do PHP continuava, seu modelo de objeto e os recursos da linguagem foram aprimorados. Várias funções da classe SmartObject tornaram-se redundantes. Desde o lançamento do PHP 8.2, resta apenas um recurso não suportado diretamente no PHP: a capacidade de usar as chamadas propriedades.

Quais recursos o Nette\Object e, por extensão, o Nette\SmartObject ofereceram? Aqui está uma visão geral. (Nos exemplos, a classe Nette\Object é usada, mas a maioria dos recursos também se aplica à característica Nette\SmartObject ).

Erros Inconsistentes

O PHP tinha um comportamento inconsistente ao acessar membros não declarados. O estado, na época de Nette\Object, era o seguinte:

echo $obj->undeclared; // E_NOTICE, mais tarde E_WARNING
$obj->undeclared = 1; // passa silenciosamente sem informar
$obj->unknownMethod(); // Erro fatal (não detectável por tentativa/captura)

O erro fatal terminou a aplicação sem qualquer possibilidade de reação. Escrever silenciosamente a membros inexistentes sem aviso prévio poderia levar a erros graves que eram difíceis de detectar. Nette\Object Todos estes casos foram pegos e uma exceção MemberAccessException foi lançada.

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

Desde o PHP 7.0, o PHP não causa mais erros fatais não detectáveis, e acessar membros não declarados tem sido um erro desde o PHP 8.2.

Você quis dizer?

Se um erro Nette\MemberAccessException foi lançado, talvez devido a um erro de digitação ao acessar uma variável de objeto ou ao chamar um método, Nette\Object tentou dar uma dica na mensagem de erro sobre como corrigir o erro, na forma do icônico adendo „você quis dizer…“.

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

Embora o PHP atual não tenha um recurso „você quis dizer?“, essa frase pode ser adicionada aos erros pela Tracy. Ele pode até mesmo corrigir automaticamente esses erros.

Métodos de extensão

Inspirado pelos métodos de extensão da C#. Eles deram a possibilidade de adicionar novos métodos às classes existentes. Por exemplo, você poderia adicionar o método addDateTime() a um formulário para adicionar seu próprio DateTimePicker.

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

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

Os métodos de extensão se mostraram impraticáveis porque seus nomes não foram autocompletados pelos editores, em vez disso, eles relataram que o método não existia. Portanto, seu apoio foi descontinuado.

Como obter o nome da classe

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

Acesso à Reflexão e Anotações

Nette\Object ofereceu acesso à reflexão e à anotação utilizando os métodos getReflection() e getAnnotation():

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

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

A partir do PHP 8.0, é possível acessar meta-informação sob a forma de atributos:

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

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

Método Getters

Nette\Object oferecia uma maneira elegante de lidar com métodos como se fossem variáveis:

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 partir do PHP 8.1, você pode usar a chamada sintaxe de primeira classe que pode ser chamada:

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

Eventos

Nette\Object ofereceu açúcar sintáctico para acionar o evento:

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

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

O código $this->onChange($this, $radius) é equivalente ao seguinte:

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

Por uma questão de clareza, recomendamos evitar o método mágico $this->onChange(). Um substituto prático é a Arrays Nette\Utils::invoke function:

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