mirror of
https://github.com/php/doc-pt_br.git
synced 2026-03-23 22:52:12 +01:00
990 lines
28 KiB
XML
990 lines
28 KiB
XML
<?xml version="1.0" encoding="utf-8"?> <!-- EN-Revision: 5744be5a4d239c18d4610581bd32b69a7d82947e Maintainer: leonardolara Status: ready --><!-- CREDITS: lhsazevedo,ae,ABDALAZARD,leonardolara -->
|
|
<chapter xml:id="language.enumerations" xmlns="http://docbook.org/ns/docbook">
|
|
<title>Enumerações</title>
|
|
<sect1 xml:id="language.enumerations.overview">
|
|
<title>Visão Geral das Enumerações</title>
|
|
<?phpdoc print-version-for="enumerations"?>
|
|
|
|
<para>
|
|
Enumerações, ou "Enums", permitem que um desenvolvedor defina um tipo personalizado que está limitado a um
|
|
número discreto de valores possíveis. Isso pode ser especialmente útil ao definir um
|
|
modelo de domínio, pois permite "tornar estados inválidos irrepresentáveis."
|
|
</para>
|
|
|
|
<para>
|
|
Enumerações aparecem em muitas linguagens com uma variedade de recursos diferentes. No PHP,
|
|
Enumerações são um tipo especial de objeto. A enumeração em si é uma classe, e seus possíveis
|
|
casos são todos objetos de instância única dessa classe. Isso significa que casos de enumeração são
|
|
objetos válidos e podem ser usados em qualquer lugar em que um objeto pode ser usado, incluindo verificações de tipo.
|
|
</para>
|
|
|
|
<para>
|
|
O exemplo mais popular de enumerações é o tipo embutido booleano, que é um
|
|
tipo enumerado com valores válidos &true; e &false;.
|
|
Enumerações permitem que os desenvolvedores definam suas próprias enumerações robustas.
|
|
</para>
|
|
</sect1>
|
|
<sect1 xml:id="language.enumerations.basics">
|
|
<title>Enumerações Básicas</title>
|
|
|
|
<para>
|
|
Enumerações são similares às classes e compartilham o mesmo namespaces que classes, interfaces, e traits.
|
|
Elas também podem ser carregadas automaticamente da mesma maneira. Uma Enumeração define um novo tipo, que possui um número fixo
|
|
e limitado de valores legais possíveis.
|
|
</para>
|
|
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum Naipe
|
|
{
|
|
case Copas;
|
|
case Ouros;
|
|
case Paus;
|
|
case Espadas;
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Essa declaração cria um novo tipo enumerado chamado <literal>Naipe</literal>, que possui
|
|
quatro e apenas quatro valores permitidos: <literal>Naipe::Copas</literal>, <literal>Naipe::Ouros</literal>,
|
|
<literal>Naipe::Paus</literal>, e <literal>Naipe::Espadas</literal>. Variáveis podem ser atribuídas
|
|
a um desses valores permitidos. Uma função pode ser tipada contra um tipo enumerado,
|
|
caso em que apenas os valores desse tipo podem ser passados.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
function pegar_uma_carta(Naipe $naipe)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
$val = Naipe::Ouros;
|
|
|
|
// OK
|
|
pegar_uma_carta($val);
|
|
|
|
// OK
|
|
pegar_uma_carta(Naipe::Paus);
|
|
|
|
// TypeError: pegar_uma_carta(): Argument #1 ($naipe) must be of type Naipe, string given
|
|
pegar_uma_carta('Espadas');
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Uma enumeração pode ter zero ou mais definições <literal>case</literal>, sem limite máximo.
|
|
Uma enumeração sem nenhum caso é sintaticamente válida, embora inútil.
|
|
</para>
|
|
|
|
<para>
|
|
Os nomes dos casos seguem as mesmas regras de sintaxe de qualquer nome do PHP, mais detalhados na seção sobre
|
|
<link linkend="language.constants">Constantes</link>.
|
|
</para>
|
|
|
|
<para>
|
|
Por padrão, os casos não são internamente associados por um valor escalar. Ou seja, <literal>Naipe::Copas</literal>
|
|
não é igual a <literal>"0"</literal>. Ao invés disso, cada caso é apoiado por um objeto único com esse nome. Isso significa que:
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
$a = Naipe::Espadas;
|
|
$b = Naipe::Espadas;
|
|
|
|
$a === $b; // true
|
|
|
|
$a instanceof Naipe; // true
|
|
|
|
$a !== 'Espadas'; // true
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Isso também significa que os valores de enumeração nunca são <literal><</literal> ou <literal>></literal> do que o outro,
|
|
uma vez que essas comparações não fazem sentido em objetos. Essas comparações sempre retornarão
|
|
&false; ao trabalhar com valores de enumeração.
|
|
</para>
|
|
|
|
<para>
|
|
Esse tipo de caso, sem dados relacionados, é chamado de "Caso Puro". Uma enumeração que contém
|
|
apenas Casos Puros é chamada de Enumeração Pura.
|
|
</para>
|
|
|
|
<para>
|
|
Todos os Casos Puros são implementados como instâncias de seus tipos 'enum'. O tipo 'enum' é representado internamente como uma classe.
|
|
</para>
|
|
|
|
<para>
|
|
Todos os Casos têm uma propriedade somente leitura, <literal>name</literal>, que é o nome (sensível a maiúsculas e minúsculas)
|
|
do próprio caso.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
print Naipe::Espadas->name;
|
|
// imprime "Espadas"
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
É possível também utilizar as funções <function>defined</function> e <function>constant</function>
|
|
para verificar a existência ou ler um item de enumeração se o nome for obtido dinamicamente.
|
|
Isto é desencorajado, já que utilizar <link linkend="language.enumerations.backed">Enumerações Apoiadas</link>
|
|
funciona para a maioria dos casos.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.backed">
|
|
<title>Enumerações Apoiadas</title>
|
|
|
|
<para>
|
|
Por padrão, os Casos não possuem um equivalente escalar. Eles são apenas objetos singleton. No entanto,
|
|
existem muitos casos em que um Caso Enumerado precisa ser capaz de fazer fazer uma conversão de ida e volta para um banco de dados ou
|
|
um armazenamento de dados semelhante, então ter um escalar embutido (e, portanto, trivialmente serializável) equivalente definido
|
|
intrinsecamente é útil.
|
|
</para>
|
|
|
|
<para>Para definir um equivalente escalar para uma Enumeração, a sintaxe é a seguinte:</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum Naipe: string
|
|
{
|
|
case Copas = 'C';
|
|
case Ouros = 'O';
|
|
case Paus = 'P';
|
|
case Espadas = 'E';
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Um caso que possui um equivalente escalar é chamado de Caso Apoiado, sendo "Apoiado" no sentido de lastreado, suportado,
|
|
por um valor mais simples. Uma enumeração que possui todos Casos Apoiados é chamada Enumeração Apoiada.
|
|
Uma Enumeração Apoiada pode conter apenas Casos Apoiados. Uma Enumeração Pura pode conter apenas Casos Puros.
|
|
</para>
|
|
|
|
<para>
|
|
Uma Enumeração Apoiada pode associar valores de tipos <literal>int</literal> ou <literal>string</literal>,
|
|
e uma determinada enumeração suporta apenas um único tipo de cada vez (isto é, sem união de <literal>int|string</literal>).
|
|
Se uma enumeração for marcada como tendo um equivalente escalar, então todos os casos devem possuir um escalar equivalente
|
|
único definido explicitamente. Não existem equivalentes escalares gerados automaticamente
|
|
(p. ex.: inteiros sequenciais). Casos apoiados devem ser únicos; dois casos apoiados não podem
|
|
ter o mesmo equivalente escalar. No entanto, uma constante pode se referir a um caso, efetivamente
|
|
criando um apelido. Veja as <link linkend="language.enumerations.constants">constantes de enumeração</link>.
|
|
</para>
|
|
|
|
<para>
|
|
Valores equivalentes podem ser uma expressão escalar constante.
|
|
Antes do PHP 8.2.0, os valores equivalentes deveriam ser literais ou expressões literais.
|
|
Isto significa que constantes e expressões constantes não eram suportadas.
|
|
Isto é, <code>1 + 1</code> era permitido, mas <code>1 + SOME_CONST</code> não era.
|
|
</para>
|
|
|
|
<para>
|
|
Casos Apoiados possuem uma propriedade somente leitura adicional, <literal>value</literal>, que é o valor
|
|
especificado na definição.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
print Naipe::Paus->value;
|
|
// Imprime "P"
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Para impor a propriedade <literal>value</literal> como somente leitura, uma variável não pode
|
|
ser atribuída como uma referência para ela. Isto é, o seguinte lança um erro:
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
$naipe = Naipe::Paus;
|
|
$ref = &$naipe->value;
|
|
// Error: Cannot acquire reference to property Naipe::$value
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Enumerações Apoiadas implementam uma interface interna <interfacename>BackedEnum</interfacename>,
|
|
que expõe dois métodos adicionais:
|
|
</para>
|
|
|
|
<simplelist>
|
|
<member>
|
|
<literal>from(int|string): self</literal> recebe um escalar e retornará o caso
|
|
correspondente da enumeração. Se não for encontrado, ela lançará um <classname>ValueError</classname>. Isso é útil
|
|
principalmente nas situações somente valores previstos são permitidos e um valor de enumeração ausente deve ser
|
|
considerado um erro de interrupção de aplicação.
|
|
</member>
|
|
<member>
|
|
<literal>tryFrom(int|string): ?self</literal> recebe uma escalar e retornará o
|
|
caso de enumeração correspondente. Se um não for encontrado, ela retornará <literal>null</literal>.
|
|
Isso é útil principalmente em casos onde o escalar de entrada não é confiável e o chamador quer
|
|
implementar sua própria lógica de manipulação de erros ou de valor padrão.
|
|
</member>
|
|
</simplelist>
|
|
|
|
<para>
|
|
Os métodos <literal>from()</literal> e <literal>tryFrom()</literal> seguem as regras
|
|
padrão de tipagem fraca/forte. No modo de tipagem fraca, passar um inteiro ou string é aceitável
|
|
e o sistema irá coagir o valor de acordo. Passar um float também irá funcionar e será
|
|
convertido. No modo de tipagem estrita, passar um inteiro para <literal>from()</literal> em uma
|
|
enumeração associada a strings (ou vice-versa) irá resultar em um <classname>TypeError</classname>,
|
|
assim como um float irá lançar um erro em todas as circunstâncias. Todos os outros tipos de parâmetro lançarão um TypeError
|
|
em ambos os modos.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
$registro = obter_coisas_do_banco_de_dados($id);
|
|
print $registro['naipe'];
|
|
|
|
$naipe = Naipe::from($registro['naipe']);
|
|
// Dados inválidos lançam um ValueError: "X" is not a valid scalar value for enum "Naipe"
|
|
print $naipe->value;
|
|
|
|
$naipe = Naipe::tryFrom('A') ?? Naipe::Espadas;
|
|
// Dados inválidos retornam null, então Naipe::Espadas em seu lugar.
|
|
print $naipe->value;
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>Definir manualmente um método <literal>from()</literal> ou <literal>tryFrom()</literal> em uma Enumeração Apoiada irá resultar em um erro fatal.</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.methods">
|
|
<title>Métodos de enumerações</title>
|
|
|
|
<para>
|
|
Enumerações (tanto Puras quanto Apoiadas) podem conter métodos e podem implementar interfaces.
|
|
Se uma enumeração implementa uma interface, qualquer verificação de tipo para aquela interface também aceitará
|
|
todos os casos daquela enumeração.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
interface Colorido
|
|
{
|
|
public function cor(): string;
|
|
}
|
|
|
|
enum Naipe implements Colorido
|
|
{
|
|
case Copas;
|
|
case Ouros;
|
|
case Paus;
|
|
case Espadas;
|
|
|
|
// Cumpre o contrato da interface.
|
|
public function cor(): string
|
|
{
|
|
return match($this) {
|
|
Naipe::Copas, Naipe::Ouros => 'Vermelho',
|
|
Naipe::Paus, Naipe::Espadas => 'Preto',
|
|
};
|
|
}
|
|
|
|
// Não faz parte de uma interface; tudo bem.
|
|
public function forma(): string
|
|
{
|
|
return "Retângulo";
|
|
}
|
|
}
|
|
|
|
function pintar(Colorido $c)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
pintar(Naipe::Paus); // Funciona
|
|
|
|
print Naipe::Ouros->shape(); // imprime "Retângulo"
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Nesse exemplo, todas as quatro instâncias de <literal>Naipe</literal> possuem dois métodos,
|
|
<literal>cor()</literal> e <literal>forma()</literal>. Até onde o código chamador
|
|
e as checagens de tipo sabem, elas se comportam exatamente da mesma forma que qualquer outra instância de objeto.
|
|
</para>
|
|
|
|
<para>
|
|
Em uma Enumeração Apoiada, a declaração de interface vai após a declaração do tipo de lastro.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
interface Colorido
|
|
{
|
|
public function cor(): string;
|
|
}
|
|
|
|
enum Naipe: string implements Colorido
|
|
{
|
|
case Copas = 'C';
|
|
case Ouros = 'O';
|
|
case Paus = 'P';
|
|
case Espadas = 'E';
|
|
|
|
// Cumpre o contrato da interface.
|
|
public function cor(): string
|
|
{
|
|
return match($this) {
|
|
Naipe::Copas, Naipe::Ouros => 'Vermelho',
|
|
Naipe::Paus, Naipe::Espadas => 'Preto',
|
|
};
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Dentro de um método, a variável <literal>$this</literal> é definida e se refere à instância do Caso.
|
|
</para>
|
|
|
|
<para>
|
|
Métodos podem ser arbitrariamente complexos, mas na prática geralmente retornam um valor estático ou
|
|
&match; no <literal>$this</literal> para fornecer
|
|
resultados diferentes para casos diferentes.
|
|
</para>
|
|
|
|
<para>
|
|
Note que nesse caso, uma prática melhor de modelagem de dados seria definir também um
|
|
Tipo 'enum' <literal>CorDeNaipe</literal> com valores Preto e Vermelho e retornar isso no seu lugar.
|
|
No entanto, isso complicaria esse exemplo.
|
|
</para>
|
|
|
|
<para>
|
|
A hierarquia acima é logicamente similar a seguinte estrutura de classes
|
|
(embora esse não seja o código real que é executado):
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
interface Colorido
|
|
{
|
|
public function cor(): string;
|
|
}
|
|
|
|
final class Naipe implements UnitEnum, Colorido
|
|
{
|
|
public const Copas = new self('Copas');
|
|
public const Ouros = new self('Ouros');
|
|
public const Paus = new self('Paus');
|
|
public const Espadas = new self('Espadas');
|
|
|
|
private function __construct(public readonly string $nome) {}
|
|
|
|
public function cor(): string
|
|
{
|
|
return match($this) {
|
|
Naipe::Copas, Naipe::Ouros => 'Vermelho',
|
|
Naipe::Paus, Naipe::Espadas => 'Preto',
|
|
};
|
|
}
|
|
|
|
public function forma(): string
|
|
{
|
|
return "Retângulo";
|
|
}
|
|
|
|
public static function cases(): array
|
|
{
|
|
// Método ilegal, porque definir manualmente um método cases() em uma enumeração não é permitido.
|
|
// Veja também a seção "Listagem de valores".
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Métodos podem ser públicos, privados ou protegidos, apesar dos privados e
|
|
protegidos serem equivalentes na prática, pois herança não é permitida.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.static-methods">
|
|
<title>Métodos estáticos de enumerações</title>
|
|
|
|
<para>
|
|
Enumerações também podem ter métodos estáticos. O uso para métodos estáticos na
|
|
própria enumeração é primariamente para construtores alternativos. Por exemplo:
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum Tamanho
|
|
{
|
|
case Pequeno;
|
|
case Medio;
|
|
case Grande;
|
|
|
|
public static function deComprimento(int $cm): self
|
|
{
|
|
return match(true) {
|
|
$cm < 50 => self::Pequeno,
|
|
$cm < 100 => self::Medio,
|
|
default => self::Grande,
|
|
};
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Métodos estáticos podem ser públicos, privados ou protegidos apesar dos privados e
|
|
protegidos serem equivalentes na prática, pois herança não é permitida.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.constants">
|
|
<title>Constantes de enumeração</title>
|
|
|
|
<para>
|
|
Enumerações podem incluir constantes, que podem ser públicas, privadas ou protegidas,
|
|
apesar das privadas e protegidas serem equivalentes na prática, pois herança não é permitida.
|
|
</para>
|
|
|
|
<para>Uma constante de enumeração pode se referir a um caso de enumeração:</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum Tamanho
|
|
{
|
|
case Pequeno;
|
|
case Medio;
|
|
case Grande;
|
|
|
|
public const Enorme = self::Grande;
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.traits">
|
|
<title>Traits</title>
|
|
|
|
<para>Enumerações podem utilizar Traits, que se comportam da mesma maneira que nas classes.
|
|
A limitação é que usados com <literal>use</literal> em uma enumeração não podem conter propriedades.
|
|
Eles podem incluir apenas métodos, métodos estáticos e constantes. Um Trait com propriedades
|
|
resultará em um erro fatal.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
interface Colorido
|
|
{
|
|
public function cor(): string;
|
|
}
|
|
|
|
trait Retangulo
|
|
{
|
|
public function shape(): string {
|
|
return "Retângulo";
|
|
}
|
|
}
|
|
|
|
enum Naipe implements Colorido
|
|
{
|
|
use Retangulo;
|
|
|
|
case Copas;
|
|
case Ouros;
|
|
case Paus;
|
|
case Espadas;
|
|
|
|
public function cor(): string
|
|
{
|
|
return match($this) {
|
|
Naipe::Copas, Naipe::Ouros => 'Vermelho',
|
|
Naipe::Paus, Naipe::Espadas => 'Preto',
|
|
};
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.expressions">
|
|
<title>Valores de enumeração em expressões constantes</title>
|
|
|
|
<para>
|
|
Como os casos são representados como constantes na própria enumeração, eles podem ser usados como valores
|
|
estáticos na maioria das expressões constantes: padrões de propriedades, padrões de variáveis estáticas, padrões
|
|
de parâmetros, valores constantes globais e de classes. Eles não podem ser usados em outros valores de caso de enumeração, mas
|
|
constantes normais podem se referir a um caso.
|
|
</para>
|
|
|
|
<para>
|
|
No entanto, chamadas implícitas de métodos mágicos como <classname>ArrayAccess</classname> em enumerações não são permitidas em definições estáticas
|
|
ou de constantes já que não podemos garantir absolutamente que o valor resultante é determinístico
|
|
ou que a invocação do método é livre de efeitos colaterais. Chamadas de funções, chamadas de métodos e
|
|
acesso às propriedades continuam a ser operações inválidas em expressões constantes.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
// Esta é uma definição de enumeração perfeitamente legal.
|
|
enum Direcao implements ArrayAccess
|
|
{
|
|
case Cima;
|
|
case Baixo;
|
|
|
|
public function offsetExists($offset): bool
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public function offsetGet($offset): mixed
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public function offsetSet($offset, $value): void
|
|
{
|
|
throw new Exception();
|
|
}
|
|
|
|
public function offsetUnset($offset): void
|
|
{
|
|
throw new Exception();
|
|
}
|
|
}
|
|
|
|
class Foo
|
|
{
|
|
// Isto é permitido.
|
|
const BAIXO = Direcao::Baixo;
|
|
|
|
// Isto não é permitido, pois pode não ser determinístico.
|
|
const CIMA = Direcao::Cima['curta'];
|
|
// Fatal error: Cannot use [] on enums in constant expression
|
|
}
|
|
|
|
// Isto é perfeitamente legal, porque não é uma expressão constante.
|
|
$x = Direcao::Cima['curta'];
|
|
var_dump("\$x é " . var_export($x, true));
|
|
|
|
$foo = new Foo();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.object-differences">
|
|
<title>Diferenças de objetos</title>
|
|
|
|
<para>
|
|
Apesar de enumerações serem construídas sobre classes e objetos, elas não suportam todas as funcionalidades relacionadas a objetos.
|
|
Em particular, casos de enumeração são proibidos de ter estado.
|
|
</para>
|
|
|
|
<simplelist>
|
|
<member>Construtores e Destrutores são proibidos.</member>
|
|
<member>Herança não é suportada. Enumerações não podem estender ou ser estendidas.</member>
|
|
<member>Propriedades estáticas ou de objeto não são permitidas.</member>
|
|
<member>Clonar um caso de enumeração não é suportado, pois os casos devem ser instâncias únicas.</member>
|
|
<member><link linkend="language.oop5.magic">Métodos mágicos</link>, exceto para aqueles listados abaixo, não são permitidos.</member>
|
|
<member>Enumerações sempre precisam ser declaradas antes de serem utilizadas.</member>
|
|
</simplelist>
|
|
|
|
<para>As seguintes funcionalidades de objeto estão disponíveis, e se comportam exatamente como em qualquer outro objeto:</para>
|
|
|
|
<simplelist>
|
|
<member>Métodos públicos, privados e protegidos.</member>
|
|
<member>Métodos estáticos públicos, privados e protegidos.</member>
|
|
<member>Constantes públicas, privadas e protegidas.</member>
|
|
<member>Enumerações podem implementar qualquer número de interfaces.</member>
|
|
<member>
|
|
Enumerações e casos podem ter <link linkend="language.attributes">atributos</link> anexados
|
|
a eles. O filtro de alvo <constant>TARGET_CLASS</constant>
|
|
inclui as próprias enumerações. O filtro de alvo <constant>TARGET_CLASS_CONST</constant>
|
|
inclui Casos de Enumeração.
|
|
</member>
|
|
<member>
|
|
Os métodos mágicos <link linkend="object.call">__call</link>, <link linkend="object.callstatic">__callStatic</link>,
|
|
e <link linkend="object.invoke">__invoke</link>
|
|
</member>
|
|
<member>Constantes <constant>__CLASS__</constant> e <constant>__FUNCTION__</constant> se comportam normalmente</member>
|
|
</simplelist>
|
|
|
|
<para>
|
|
A constante mágica <literal>::class</literal> em um tipo 'enum' avalia ao nome
|
|
do tipo incluindo qualquer namespace, exatamente o mesmo que um objeto. A constante mágica <literal>::class</literal>
|
|
em uma instância de Caso também avalia para o tipo 'enum', pois ele é uma
|
|
instância daquele tipo.
|
|
</para>
|
|
|
|
<para>
|
|
Adicionalmente, casos não podem ser instanciados diretamente com <literal>new</literal>, nem com
|
|
<methodname>ReflectionClass::newInstanceWithoutConstructor</methodname> em reflexão. Ambos resultarão em um erro.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
$trevos = new Naipe();
|
|
// Error: Cannot instantiate enum Naipe
|
|
|
|
$ferraduras = (new ReflectionClass(Naipe::class))->newInstanceWithoutConstructor()
|
|
// Error: Cannot instantiate enum Naipe
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.listing">
|
|
<title>Listagem de valores</title>
|
|
|
|
<para>
|
|
Tanto Enumerações Puras quanto Enumerações Apoiadas implementam uma interface interna chamada
|
|
<interfacename>UnitEnum</interfacename>. <literal>UnitEnum</literal> inclui um método estático
|
|
<literal>cases()</literal>. <literal>cases()</literal> retorna um array embalado com
|
|
todos os Casos definidos na ordem de declaração.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
Naipe::cases();
|
|
// Produz: [Naipe::Copas, Naipe::Ouros, Naipe::Paus, Naipe::Espadas]
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>Definir manualmente um método <literal>cases()</literal> em uma enumeração resultará em um erro fatal.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.serialization">
|
|
<title>Serialização</title>
|
|
|
|
<para>
|
|
As enumerações são serializadas de maneira diferente dos objetos. Especificamente, elas têm um novo código de serialização,
|
|
<literal>"E"</literal>, que especifica o nome do caso de enumeração. A rotina de desserialização pode
|
|
então usar isso para definir uma variável para o valor singleton existente. Isso garante que:
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
Naipe::Copas === unserialize(serialize(Naipe::Copas));
|
|
|
|
print serialize(Naipe::Copas);
|
|
// E:11:"Naipe:Copas";
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Na desserialização, se uma enumeração de um caso não pode ser encontrada para combinar com um valor
|
|
serializado, um aviso será emitido e &false; retornado.</para>
|
|
|
|
<para>
|
|
Se uma Enumeração Pura for serializada para JSON, um erro será lançado. Se uma Enumeração Apoiada
|
|
for serializada para JSON, ela será representada apenas por seu valor escalar, no
|
|
tipo apropriado. O comportamento de ambas pode ser sobrescrito ao implementar
|
|
<classname>JsonSerializable</classname>.
|
|
</para>
|
|
|
|
<para>Para <function>print_r</function>, a saída de um caso de enumeração é ligeiramente diferente
|
|
dos objetos para minimizar confusões.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum Foo {
|
|
case Bar;
|
|
}
|
|
|
|
enum Baz: int {
|
|
case Beep = 5;
|
|
}
|
|
|
|
print_r(Foo::Bar);
|
|
print_r(Baz::Beep);
|
|
|
|
/* Produz
|
|
|
|
Foo Enum (
|
|
[name] => Bar
|
|
)
|
|
Baz Enum:int {
|
|
[name] => Beep
|
|
[value] => 5
|
|
}
|
|
*/
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.object-differences.inheritance">
|
|
|
|
<title>Porque enumerações não são extensíveis</title>
|
|
|
|
<simpara>
|
|
Classes possuem contratos em seus métodos:
|
|
</simpara>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
class A {}
|
|
class B extends A {}
|
|
|
|
function foo(A $a) {}
|
|
|
|
function bar(B $b) {
|
|
foo($b);
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<simpara>
|
|
Esse código é correto para tipos, pois B segue o contrato de A, e através da
|
|
co e contra variância, quaisquer expectativas a respeito dos métodos será
|
|
atendida.
|
|
</simpara>
|
|
|
|
<simpara>
|
|
Enumerações possuem contratos em seus itens, não em seus métodos:
|
|
</simpara>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum ErrorCode {
|
|
case SOMETHING_BROKE;
|
|
}
|
|
|
|
function quux(ErrorCode $errorCode)
|
|
{
|
|
// Nesse primeiro momento, esse código prevê todos os casos
|
|
match ($errorCode) {
|
|
ErrorCode::SOMETHING_BROKE => true,
|
|
};
|
|
}
|
|
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<simpara>
|
|
A instrução &match; na função <code>quux</code> pode ser analisada estaticamente
|
|
para verificar se cobre todos os casos possíveis em ErrorCode.
|
|
</simpara>
|
|
|
|
<simpara>
|
|
Mas imagine que seja possível estender enumerações, assim:
|
|
</simpara>
|
|
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
// Apenas para o exemplo onde enumerações não são finais.
|
|
// Isto *não* funciona no PHP.
|
|
enum MoreErrorCode extends ErrorCode {
|
|
case PEBKAC;
|
|
}
|
|
|
|
function fot(MoreErrorCode $errorCode) {
|
|
quux($errorCode);
|
|
}
|
|
|
|
fot(MoreErrorCode::PEBKAC);
|
|
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<simpara>
|
|
Em condições normais de herança, uma classe que estende outra passará
|
|
na checagem de tipos.
|
|
</simpara>
|
|
|
|
<simpara>
|
|
O problema ocorreria na instrução &match; da função <code>quux()</code> que agora não cobre todos
|
|
os casos. Porque a função não prevê o item <code>MoreErrorCode::PEBKAC</code> o match irá falhar com uma exceção.
|
|
</simpara>
|
|
|
|
<simpara>
|
|
Por conta disso, as enumerações são finais e não podem ser estendidas.
|
|
</simpara>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.examples">
|
|
&reftitle.examples;
|
|
|
|
<para>
|
|
<example>
|
|
<title>Valores limitados básicos</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum Ordem
|
|
{
|
|
case Asc;
|
|
case Desc;
|
|
}
|
|
|
|
function consulta($campos, $filtros, Ordem $ordem = Ordem::Asc)
|
|
{
|
|
/* ... */
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
<para>
|
|
A função <literal>consulta()</literal> agora pode prosseguir segura, sabendo que a
|
|
<literal>$ordem</literal> tem garantia de ser <literal>Ordem::Asc</literal>
|
|
ou <literal>Ordem::Desc</literal>. Qualquer outro valor resultaria em um
|
|
<classname>TypeError</classname>, então nenhuma verificação ou teste de erro é necessária.
|
|
</para>
|
|
</example>
|
|
</para>
|
|
|
|
<para>
|
|
|
|
<example>
|
|
<title>Valores exclusivos avançados</title>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum EstadoDeUsuario: string
|
|
{
|
|
case Pendente = 'P';
|
|
case Ativo = 'A';
|
|
case Suspenso = 'S';
|
|
case CanceladoPeloUsuario = 'C';
|
|
|
|
public function rotulo(): string
|
|
{
|
|
return match($this) {
|
|
self::Pendente => 'Pendente',
|
|
self::Ativo => 'Ativo',
|
|
self::Suspenso => 'Suspenso',
|
|
self::CanceladoPeloUsuario => 'Cancelado pelo usuário',
|
|
};
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Neste exemplo, o estado de um usuário pode ser, exclusivamente, <literal>EstadoDeUsuario::Pendente</literal>,
|
|
<literal>EstadoDeUsuario::Ativo</literal>, <literal>EstadoDeUsuario::Suspenso</literal>, ou
|
|
<literal>EstadoDeUsuario::CanceladoPeloUsuario</literal>. Uma função pode tipar um parâmetro contra
|
|
<literal>EstadoDeUsuario</literal> e aceitar então apenas aqueles quatro valores, ponto final.
|
|
</para>
|
|
|
|
<para>
|
|
Todos os quatros valores possuem um método <literal>rotulo()</literal>, que retorna uma string legível para humanos.
|
|
Essa string é independente da string equivalente escalar do "nome de máquina", que pode ser usado em,
|
|
por exemplo, um campo de banco de dados um uma caixa de seleção HTML.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
foreach (EstadoDeUsuario::cases() as $caso) {
|
|
printf('<option value="%s">%s</option>\n', $caso->value, $caso->label());
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|
|
|
|
<!-- Keep this comment at the end of the file
|
|
Local variables:
|
|
mode: sgml
|
|
sgml-omittag:t
|
|
sgml-shorttag:t
|
|
sgml-minimize-attributes:nil
|
|
sgml-always-quote-attributes:t
|
|
sgml-indent-step:1
|
|
sgml-indent-data:t
|
|
indent-tabs-mode:nil
|
|
sgml-parent-document:nil
|
|
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
|
sgml-exposed-tags:nil
|
|
sgml-local-catalogs:nil
|
|
sgml-local-ecat-files:nil
|
|
End:
|
|
vim600: syn=xml fen fdm=syntax fdl=2 si
|
|
vim: et tw=78 syn=sgml
|
|
vi: ts=1 sw=1
|
|
-->
|