mirror of
https://github.com/php/doc-es.git
synced 2026-03-23 23:12:09 +01:00
988 lines
29 KiB
XML
988 lines
29 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- EN-Revision: 2e5f2910f3a2a95dcbdaf580ba57a0b60b072c2a Maintainer: PhilDaiguille Status: ready -->
|
|
<!-- Reviewed: no -->
|
|
<chapter xml:id="language.enumerations" xmlns="http://docbook.org/ns/docbook">
|
|
<title>Enumeraciones</title>
|
|
<sect1 xml:id="language.enumerations.overview">
|
|
<title>Visión general de las enumeraciones</title>
|
|
<?phpdoc print-version-for="enumerations"?>
|
|
|
|
<para>
|
|
Las enumeraciones, o "Enums", permiten a un desarrollador definir un tipo personalizado que se limita a uno
|
|
de un número discreto de valores posibles. Esto puede ser especialmente útil al definir un
|
|
modelo de dominio, ya que permite "hacer que los estados no válidos sean irrepresentables".
|
|
</para>
|
|
|
|
<para>
|
|
Las enumeraciones aparecen en muchos lenguajes con una variedad de características diferentes. En PHP,
|
|
las enumeraciones son un tipo especial de objeto. La enumeración en sí es una clase, y sus posibles
|
|
casos son todos objetos de instancia única de esa clase. Eso significa que los casos de enumeración son
|
|
objetos válidos y pueden usarse dondequiera que se pueda usar un objeto, incluyendo comprobaciones de tipo.
|
|
</para>
|
|
|
|
<para>
|
|
El ejemplo más popular de enumeraciones es el tipo booleano integrado, que es un
|
|
tipo enumerado con valores legales &true; y &false;.
|
|
Las enumeraciones permiten a los desarrolladores definir sus propias enumeraciones arbitrariamente robustas.
|
|
</para>
|
|
</sect1>
|
|
<sect1 xml:id="language.enumerations.basics">
|
|
<title>Enumeraciones básicas</title>
|
|
|
|
<para>
|
|
Las enumeraciones son similares a las clases y comparten los mismos espacios de nombres que las clases,
|
|
interfaces y traits. También son autocargables de la misma manera. Una enumeración define un nuevo tipo,
|
|
que tiene un número fijo y limitado de valores legales posibles.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum Suit
|
|
{
|
|
case Hearts;
|
|
case Diamonds;
|
|
case Clubs;
|
|
case Spades;
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Esta declaración crea un nuevo tipo enumerado llamado <literal>Suit</literal>, que tiene
|
|
cuatro y solo cuatro valores legales: <literal>Suit::Hearts</literal>, <literal>Suit::Diamonds</literal>,
|
|
<literal>Suit::Clubs</literal>, y <literal>Suit::Spades</literal>. Las variables pueden ser asignadas
|
|
a uno de esos valores legales. Una función puede ser comprobada de tipo contra un tipo enumerado,
|
|
en cuyo caso solo se pueden pasar valores de ese tipo.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
function pick_a_card(Suit $suit)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
$val = Suit::Diamonds;
|
|
|
|
// OK
|
|
pick_a_card($val);
|
|
|
|
// OK
|
|
pick_a_card(Suit::Clubs);
|
|
|
|
// TypeError: pick_a_card(): Argument #1 ($suit) must be of type Suit, string given
|
|
pick_a_card('Spades');
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Una enumeración puede tener cero o más definiciones de <literal>case</literal>, sin máximo.
|
|
Una enumeración sin casos es sintácticamente válida, aunque bastante inútil.
|
|
</para>
|
|
|
|
<para>
|
|
Para los casos de enumeración, se aplican las mismas reglas sintácticas que a cualquier etiqueta en PHP, ver
|
|
<link linkend="language.constants">Constantes</link>.
|
|
</para>
|
|
|
|
<para>
|
|
Por omisión, los casos no están respaldados intrínsecamente por un valor escalar. Es decir, <literal>Suit::Hearts</literal>
|
|
no es igual a <literal>"0"</literal>. En su lugar, cada caso está respaldado por un objeto singleton de ese nombre. Eso significa que:
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
$a = Suit::Spades;
|
|
$b = Suit::Spades;
|
|
|
|
$a === $b; // true
|
|
|
|
$a instanceof Suit; // true
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
También significa que los valores de enumeración nunca son <literal><</literal> o <literal>></literal> entre sí,
|
|
ya que esas comparaciones no tienen significado en los objetos. Esas comparaciones siempre devolverán
|
|
&false; al trabajar con valores de enumeración.
|
|
</para>
|
|
|
|
<para>
|
|
Este tipo de caso, sin datos relacionados, se denomina "Caso Puro". Una enumeración que contiene
|
|
solo Casos Puros se denomina Enumeración Pura.
|
|
</para>
|
|
|
|
<para>
|
|
Todos los Casos Puros se implementan como instancias de su tipo de enumeración. El tipo de enumeración está representado internamente como una clase.
|
|
</para>
|
|
|
|
<para>
|
|
Todos los Casos tienen una propiedad de solo lectura, <literal>name</literal>, que es el nombre sensible a mayúsculas y minúsculas
|
|
del caso en sí.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
print Suit::Spades->name;
|
|
// imprime "Spades"
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
También es posible usar las funciones <function>defined</function> y <function>constant</function>
|
|
para verificar la existencia o leer un caso de enumeración si el nombre se obtiene dinámicamente.
|
|
Sin embargo, esto se desaconseja ya que el uso de <link linkend="language.enumerations.backed">Enumeraciones respaldadas</link>
|
|
debería funcionar para la mayoría de los casos de uso.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.backed">
|
|
<title>Enumeraciones respaldadas</title>
|
|
|
|
<para>
|
|
Por omisión, los Casos Enumerados no tienen equivalente escalar. Son simplemente objetos singleton. Sin embargo,
|
|
hay amplios casos en los que un Caso Enumerado necesita poder hacer un viaje de ida y vuelta a una base de datos o
|
|
un almacén de datos similar, por lo que tener un equivalente escalar integrado (y por lo tanto trivialmente serializable) definido
|
|
intrínsecamente es útil.
|
|
</para>
|
|
|
|
<para>Para definir un equivalente escalar para una Enumeración, la sintaxis es la siguiente:</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum Suit: string
|
|
{
|
|
case Hearts = 'H';
|
|
case Diamonds = 'D';
|
|
case Clubs = 'C';
|
|
case Spades = 'S';
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Un caso que tiene un equivalente escalar se denomina Caso Respaldado, ya que está "respaldado"
|
|
por un valor más simple. Una enumeración que contiene todos los Casos Respaldados se denomina "Enumeración Respaldada".
|
|
Una Enumeración Respaldada solo puede contener Casos Respaldados. Una Enumeración Pura solo puede contener Casos Puros.
|
|
</para>
|
|
|
|
<para>
|
|
Una Enumeración Respaldada puede estar respaldada por tipos de <literal>int</literal> o <literal>string</literal>,
|
|
y una enumeración dada admite solo un tipo a la vez (es decir, no hay unión de <literal>int|string</literal>).
|
|
Si una enumeración está marcada como que tiene un equivalente escalar, entonces todos los casos deben tener un equivalente
|
|
escalar único definido explícitamente. No hay equivalentes escalares generados automáticamente
|
|
(por ejemplo, enteros secuenciales). Los casos respaldados deben ser únicos; dos casos de enumeración respaldados no pueden
|
|
tener el mismo equivalente escalar. Sin embargo, una constante puede referirse a un caso, creando efectivamente
|
|
un alias. Ver <link linkend="language.enumerations.constants">Constantes de enumeración</link>.
|
|
</para>
|
|
|
|
<para>
|
|
Los valores equivalentes pueden ser una expresión escalar constante.
|
|
Antes de PHP 8.2.0, los valores equivalentes debían ser literales o expresiones literales.
|
|
Esto significa que las constantes y expresiones constantes no estaban admitidas.
|
|
Es decir, <code>1 + 1</code> estaba permitido, pero <code>1 + SOME_CONST</code> no.
|
|
</para>
|
|
|
|
<para>
|
|
Los Casos Respaldados tienen una propiedad adicional de solo lectura, <literal>value</literal>, que es el valor
|
|
especificado en la definición.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
print Suit::Clubs->value;
|
|
// Imprime "C"
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Para hacer cumplir la propiedad <literal>value</literal> como de solo lectura, una variable no puede
|
|
ser asignada como referencia a ella. Es decir, lo siguiente genera un error:
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
$suit = Suit::Clubs;
|
|
$ref = &$suit->value;
|
|
// Error: No se puede obtener una referencia a la propiedad Suit::$value
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Las enumeraciones respaldadas implementan una interfaz interna <interfacename>BackedEnum</interfacename>,
|
|
que expone dos métodos adicionales:
|
|
</para>
|
|
|
|
<simplelist>
|
|
<member>
|
|
<literal>from(int|string): self</literal> tomará un escalar y devolverá el Caso de Enumeración correspondiente.
|
|
Si no se encuentra ninguno, lanzará un <classname>ValueError</classname>. Esto es principalmente
|
|
útil en casos donde el escalar de entrada es confiable y un valor de enumeración faltante debería ser
|
|
considerado un error que detiene la aplicación.
|
|
</member>
|
|
<member>
|
|
<literal>tryFrom(int|string): ?self</literal> tomará un escalar y devolverá el
|
|
Caso de Enumeración correspondiente. Si no se encuentra ninguno, devolverá <literal>null</literal>.
|
|
Esto es principalmente útil en casos donde el escalar de entrada no es confiable y el llamador quiere
|
|
implementar su propia lógica de manejo de errores o valores por omisión.
|
|
</member>
|
|
</simplelist>
|
|
|
|
<para>
|
|
Los métodos <literal>from()</literal> y <literal>tryFrom()</literal> siguen las reglas estándar
|
|
de tipado débil/fuerte. En modo de tipado débil, pasar un entero o string es admisible
|
|
y el sistema coercionará el valor en consecuencia. Pasar un float también funcionará y será
|
|
coercionado. En modo de tipado estricto, pasar un entero a <literal>from()</literal> en una
|
|
enumeración respaldada por string (o viceversa) resultará en un <classname>TypeError</classname>,
|
|
al igual que un float en todas las circunstancias. Todos los demás tipos de parámetros lanzarán un TypeError
|
|
en ambos modos.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
$record = get_stuff_from_database($id);
|
|
print $record['suit'];
|
|
|
|
$suit = Suit::from($record['suit']);
|
|
// Datos no válidos lanzan un ValueError: "X" no es un valor escalar válido para la enumeración "Suit"
|
|
print $suit->value;
|
|
|
|
$suit = Suit::tryFrom('A') ?? Suit::Spades;
|
|
// Datos no válidos devuelven null, por lo que se usa Suit::Spades en su lugar.
|
|
print $suit->value;
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>Definir manualmente un método <literal>from()</literal> o <literal>tryFrom()</literal> en una Enumeración Respaldada resultará en un error fatal.</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.methods">
|
|
<title>Métodos de enumeración</title>
|
|
|
|
<para>
|
|
Las enumeraciones (tanto Enumeraciones Puras como Enumeraciones Respaldadas) pueden contener métodos y pueden implementar interfaces.
|
|
Si una enumeración implementa una interfaz, entonces cualquier comprobación de tipo para esa interfaz también aceptará
|
|
todos los casos de esa enumeración.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
interface Colorful
|
|
{
|
|
public function color(): string;
|
|
}
|
|
|
|
enum Suit implements Colorful
|
|
{
|
|
case Hearts;
|
|
case Diamonds;
|
|
case Clubs;
|
|
case Spades;
|
|
|
|
// Cumple con el contrato de la interfaz.
|
|
public function color(): string
|
|
{
|
|
return match($this) {
|
|
Suit::Hearts, Suit::Diamonds => 'Red',
|
|
Suit::Clubs, Suit::Spades => 'Black',
|
|
};
|
|
}
|
|
|
|
// No forma parte de una interfaz; eso está bien.
|
|
public function shape(): string
|
|
{
|
|
return "Rectangle";
|
|
}
|
|
}
|
|
|
|
function paint(Colorful $c)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
paint(Suit::Clubs); // Funciona
|
|
|
|
print Suit::Diamonds->shape(); // imprime "Rectangle"
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
En este ejemplo, las cuatro instancias de <literal>Suit</literal> tienen dos métodos,
|
|
<literal>color()</literal> y <literal>shape()</literal>. En cuanto al código de llamada
|
|
y las comprobaciones de tipo, se comportan exactamente igual que cualquier otra instancia de objeto.
|
|
</para>
|
|
|
|
<para>
|
|
En una Enumeración Respaldada, la declaración de interfaz va después de la declaración del tipo de respaldo.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
interface Colorful
|
|
{
|
|
public function color(): string;
|
|
}
|
|
|
|
enum Suit: string implements Colorful
|
|
{
|
|
case Hearts = 'H';
|
|
case Diamonds = 'D';
|
|
case Clubs = 'C';
|
|
case Spades = 'S';
|
|
|
|
// Cumple con el contrato de la interfaz.
|
|
public function color(): string
|
|
{
|
|
return match($this) {
|
|
Suit::Hearts, Suit::Diamonds => 'Red',
|
|
Suit::Clubs, Suit::Spades => 'Black',
|
|
};
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Dentro de un método, la variable <literal>$this</literal> está definida y se refiere a la instancia del Caso.
|
|
</para>
|
|
|
|
<para>
|
|
Los métodos pueden ser arbitrariamente complejos, pero en la práctica suelen devolver un valor estático o
|
|
&match; en <literal>$this</literal> para proporcionar
|
|
diferentes resultados para diferentes casos.
|
|
</para>
|
|
|
|
<para>
|
|
Tenga en cuenta que en este caso sería una mejor práctica de modelado de datos también definir un
|
|
tipo de enumeración <literal>SuitColor</literal> con valores Red y Black y devolver eso en su lugar.
|
|
Sin embargo, eso complicaría este ejemplo.
|
|
</para>
|
|
|
|
<para>
|
|
La jerarquía anterior es lógicamente similar a la siguiente estructura de clase
|
|
(aunque este no es el código real que se ejecuta):
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
interface Colorful
|
|
{
|
|
public function color(): string;
|
|
}
|
|
|
|
final class Suit implements UnitEnum, Colorful
|
|
{
|
|
public const Hearts = new self('Hearts');
|
|
public const Diamonds = new self('Diamonds');
|
|
public const Clubs = new self('Clubs');
|
|
public const Spades = new self('Spades');
|
|
|
|
private function __construct(public readonly string $name) {}
|
|
|
|
public function color(): string
|
|
{
|
|
return match($this) {
|
|
Suit::Hearts, Suit::Diamonds => 'Red',
|
|
Suit::Clubs, Suit::Spades => 'Black',
|
|
};
|
|
}
|
|
|
|
public function shape(): string
|
|
{
|
|
return "Rectangle";
|
|
}
|
|
|
|
public static function cases(): array
|
|
{
|
|
// Método ilegal, porque definir manualmente un método cases() en una Enumeración no está permitido.
|
|
// Ver también la sección "Listado de valores".
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Los métodos pueden ser públicos, privados o protegidos, aunque en la práctica privado y
|
|
protegido son equivalentes ya que la herencia no está permitida.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.static-methods">
|
|
<title>Métodos estáticos de enumeración</title>
|
|
|
|
<para>
|
|
Las enumeraciones también pueden tener métodos estáticos. El uso de métodos estáticos en la
|
|
enumeración en sí es principalmente para constructores alternativos. Por ejemplo:
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum Size
|
|
{
|
|
case Small;
|
|
case Medium;
|
|
case Large;
|
|
|
|
public static function fromLength(int $cm): self
|
|
{
|
|
return match(true) {
|
|
$cm < 50 => self::Small,
|
|
$cm < 100 => self::Medium,
|
|
default => self::Large,
|
|
};
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Los métodos estáticos pueden ser públicos, privados o protegidos, aunque en la práctica privado
|
|
y protegido son equivalentes ya que la herencia no está permitida.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.constants">
|
|
<title>Constantes de enumeración</title>
|
|
|
|
<para>
|
|
Las enumeraciones pueden incluir constantes, que pueden ser públicas, privadas o protegidas,
|
|
aunque en la práctica privada y protegida son equivalentes ya que la herencia no está permitida.
|
|
</para>
|
|
|
|
<para>Una constante de enumeración puede referirse a un caso de enumeración:</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum Size
|
|
{
|
|
case Small;
|
|
case Medium;
|
|
case Large;
|
|
|
|
public const Huge = self::Large;
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.traits">
|
|
<title>Traits</title>
|
|
|
|
<para>Las enumeraciones pueden aprovechar los traits, que se comportarán igual que en las clases.
|
|
La salvedad es que los traits <literal>use</literal>ados en una enumeración no deben contener propiedades.
|
|
Solo pueden incluir métodos, métodos estáticos y constantes. Un trait con propiedades resultará en un error fatal.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
interface Colorful
|
|
{
|
|
public function color(): string;
|
|
}
|
|
|
|
trait Rectangle
|
|
{
|
|
public function shape(): string {
|
|
return "Rectangle";
|
|
}
|
|
}
|
|
|
|
enum Suit implements Colorful
|
|
{
|
|
use Rectangle;
|
|
|
|
case Hearts;
|
|
case Diamonds;
|
|
case Clubs;
|
|
case Spades;
|
|
|
|
public function color(): string
|
|
{
|
|
return match($this) {
|
|
Suit::Hearts, Suit::Diamonds => 'Red',
|
|
Suit::Clubs, Suit::Spades => 'Black',
|
|
};
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.expressions">
|
|
<title>Valores de enumeración en expresiones constantes</title>
|
|
|
|
<para>
|
|
Debido a que los casos están representados como constantes en la enumeración en sí, pueden usarse como valores estáticos
|
|
en la mayoría de las expresiones constantes: valores por omisión de propiedades, valores por omisión de variables estáticas,
|
|
valores por omisión de parámetros, valores de constantes globales y de clase. No pueden usarse en otros valores de casos de enumeración,
|
|
pero las constantes normales pueden referirse a un caso de enumeración.
|
|
</para>
|
|
|
|
<para>
|
|
Sin embargo, las llamadas implícitas a métodos mágicos como <classname>ArrayAccess</classname> en enumeraciones no están permitidas
|
|
en definiciones estáticas o constantes, ya que no podemos garantizar absolutamente que el valor resultante sea determinista
|
|
o que la invocación del método esté libre de efectos secundarios. Las llamadas a funciones, llamadas a métodos y
|
|
el acceso a propiedades continúan siendo operaciones no válidas en expresiones constantes.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
// Esta es una definición de Enumeración completamente legal.
|
|
enum Direction implements ArrayAccess
|
|
{
|
|
case Up;
|
|
case Down;
|
|
|
|
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
|
|
{
|
|
// Esto está permitido.
|
|
const DOWN = Direction::Down;
|
|
|
|
// Esto no está permitido, ya que puede no ser determinista.
|
|
const UP = Direction::Up['short'];
|
|
// Error fatal: No se puede usar [] en enumeraciones en expresión constante
|
|
}
|
|
|
|
// Esto es completamente legal, porque no es una expresión constante.
|
|
$x = Direction::Up['short'];
|
|
var_dump("\$x is " . var_export($x, true));
|
|
|
|
$foo = new Foo();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.object-differences">
|
|
<title>Diferencias con los objetos</title>
|
|
|
|
<para>
|
|
Aunque las enumeraciones están construidas sobre clases y objetos, no admiten toda la funcionalidad relacionada con objetos.
|
|
En particular, los casos de enumeración tienen prohibido tener estado.
|
|
</para>
|
|
|
|
<simplelist>
|
|
<member>Los constructores y destructores están prohibidos.</member>
|
|
<member>La herencia no está admitida. Las enumeraciones no pueden extender ni ser extendidas.</member>
|
|
<member>No se permiten propiedades estáticas u objetuales.</member>
|
|
<member>Clonar un caso de enumeración no está admitido, ya que los casos deben ser instancias singleton.</member>
|
|
<member><link linkend="language.oop5.magic">Métodos mágicos</link>, excepto los listados a continuación, no están permitidos.</member>
|
|
<member>Las enumeraciones siempre deben declararse antes de ser usadas.</member>
|
|
</simplelist>
|
|
|
|
<para>La siguiente funcionalidad de objetos está disponible y se comporta igual que en cualquier otro objeto:</para>
|
|
|
|
<simplelist>
|
|
<member>Métodos públicos, privados y protegidos.</member>
|
|
<member>Métodos estáticos públicos, privados y protegidos.</member>
|
|
<member>Constantes públicas, privadas y protegidas.</member>
|
|
<member>Las enumeraciones pueden implementar cualquier número de interfaces.</member>
|
|
<member>
|
|
Las enumeraciones y los casos pueden tener <link linkend="language.attributes">atributos</link>
|
|
adjuntos a ellos. El filtro de destino <constant>TARGET_CLASS</constant>
|
|
incluye las enumeraciones en sí. El filtro de destino <constant>TARGET_CLASS_CONST</constant>
|
|
incluye los Casos de Enumeración.
|
|
</member>
|
|
<member>
|
|
<link linkend="object.call">__call</link>, <link linkend="object.callstatic">__callStatic</link>,
|
|
y <link linkend="object.invoke">__invoke</link> métodos mágicos
|
|
</member>
|
|
<member>Las constantes <constant>__CLASS__</constant> y <constant>__FUNCTION__</constant> se comportan normalmente</member>
|
|
</simplelist>
|
|
|
|
<para>
|
|
La constante mágica <literal>::class</literal> en un tipo de enumeración se evalúa al nombre
|
|
del tipo incluyendo cualquier espacio de nombres, exactamente igual que un objeto. La constante mágica <literal>::class</literal>
|
|
en una instancia de Caso también se evalúa al tipo de enumeración, ya que es una
|
|
instancia de ese tipo.
|
|
</para>
|
|
|
|
<para>
|
|
Además, los casos de enumeración no pueden ser instanciados directamente con <literal>new</literal>, ni con
|
|
<methodname>ReflectionClass::newInstanceWithoutConstructor</methodname> en reflexión. Ambos resultarán en un error.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
$clovers = new Suit();
|
|
// Error: No se puede instanciar la enumeración Suit
|
|
|
|
$horseshoes = (new ReflectionClass(Suit::class))->newInstanceWithoutConstructor()
|
|
// Error: No se puede instanciar la enumeración Suit
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.listing">
|
|
<title>Listado de valores</title>
|
|
|
|
<para>
|
|
Tanto las Enumeraciones Puras como las Enumeraciones Respaldadas implementan una interfaz interna llamada
|
|
<interfacename>UnitEnum</interfacename>. <literal>UnitEnum</literal> incluye un método estático
|
|
<literal>cases()</literal>. <literal>cases()</literal> devuelve un array compacto de
|
|
todos los Casos definidos en el orden de declaración.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
Suit::cases();
|
|
// Produce: [Suit::Hearts, Suit::Diamonds, Suit::Clubs, Suit::Spades]
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>Definir manualmente un método <literal>cases()</literal> en una Enumeración resultará en un error fatal.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.serialization">
|
|
<title>Serialización</title>
|
|
|
|
<para>
|
|
Las enumeraciones se serializan de manera diferente a los objetos. Específicamente, tienen un nuevo código de serialización,
|
|
<literal>"E"</literal>, que especifica el nombre del caso de enumeración. La rutina de deserialización puede entonces
|
|
usar eso para establecer una variable al valor singleton existente. Eso asegura que:
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
Suit::Hearts === unserialize(serialize(Suit::Hearts));
|
|
|
|
print serialize(Suit::Hearts);
|
|
// E:11:"Suit:Hearts";
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
Al deserializar, si no se puede encontrar una enumeración y un caso para coincidir con un valor serializado,
|
|
se emitirá una advertencia y se devolverá &false;.
|
|
</para>
|
|
|
|
<para>
|
|
Si una Enumeración Pura se serializa a JSON, se lanzará un error. Si una Enumeración Respaldada
|
|
se serializa a JSON, estará representada solo por su valor escalar, en el
|
|
tipo apropiado. El comportamiento de ambas puede ser sobrescrito implementando
|
|
<classname>JsonSerializable</classname>.
|
|
</para>
|
|
|
|
<para>Para <function>print_r</function>, la salida de un caso de enumeración es ligeramente diferente
|
|
de los objetos para minimizar la confusión.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum Foo {
|
|
case Bar;
|
|
}
|
|
|
|
enum Baz: int {
|
|
case Beep = 5;
|
|
}
|
|
|
|
print_r(Foo::Bar);
|
|
print_r(Baz::Beep);
|
|
|
|
/* Produce
|
|
|
|
Foo Enum (
|
|
[name] => Bar
|
|
)
|
|
Baz Enum:int {
|
|
[name] => Beep
|
|
[value] => 5
|
|
}
|
|
*/
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.object-differences.inheritance">
|
|
|
|
<title>Por qué las enumeraciones no son extensibles</title>
|
|
|
|
<simpara>
|
|
Las clases tienen contratos en sus 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>
|
|
Este código es seguro en cuanto a tipos, ya que B sigue el contrato de A, y a través de la magia de
|
|
la co/contravariancia, cualquier expectativa que uno pueda tener de los métodos será
|
|
preservada, exceptuando las excepciones.
|
|
</simpara>
|
|
|
|
<simpara>
|
|
Las enumeraciones tienen contratos en sus casos, no en sus métodos:
|
|
</simpara>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum ErrorCode {
|
|
case SOMETHING_BROKE;
|
|
}
|
|
|
|
function quux(ErrorCode $errorCode)
|
|
{
|
|
// Cuando se escribe, este código parece cubrir todos los casos
|
|
match ($errorCode) {
|
|
ErrorCode::SOMETHING_BROKE => true,
|
|
};
|
|
}
|
|
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<simpara>
|
|
La sentencia &match; en la función <code>quux</code> puede ser analizada estáticamente para cubrir
|
|
todos los casos en ErrorCode.
|
|
</simpara>
|
|
|
|
<simpara>
|
|
Pero imagina que estuviera permitido extender enumeraciones:
|
|
</simpara>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
// Código de experimento mental donde las enumeraciones no son finales.
|
|
// Nota: esto no funcionará realmente en PHP.
|
|
enum MoreErrorCode extends ErrorCode {
|
|
case PEBKAC;
|
|
}
|
|
|
|
function fot(MoreErrorCode $errorCode) {
|
|
quux($errorCode);
|
|
}
|
|
|
|
fot(MoreErrorCode::PEBKAC);
|
|
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<simpara>
|
|
Bajo las reglas normales de herencia, una clase que extiende a otra pasará
|
|
la comprobación de tipo.
|
|
</simpara>
|
|
|
|
<simpara>
|
|
El problema sería que la sentencia &match; en <code>quux()</code> ya no cubriría todos
|
|
los casos. Debido a que no sabe acerca de <code>MoreErrorCode::PEBKAC</code>, el match lanzará una excepción.
|
|
</simpara>
|
|
|
|
<simpara>
|
|
Debido a esto, las enumeraciones son finales y no pueden ser extendidas.
|
|
</simpara>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="language.enumerations.examples">
|
|
&reftitle.examples;
|
|
|
|
<para>
|
|
<example>
|
|
<title>Valores limitados básicos</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum SortOrder
|
|
{
|
|
case Asc;
|
|
case Desc;
|
|
}
|
|
|
|
function query($fields, $filter, SortOrder $order = SortOrder::Asc)
|
|
{
|
|
/* ... */
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
<para>
|
|
La función <literal>query()</literal> ahora puede proceder con la seguridad de que
|
|
<literal>$order</literal> está garantizado que sea <literal>SortOrder::Asc</literal>
|
|
o <literal>SortOrder::Desc</literal>. Cualquier otro valor habría resultado en un
|
|
<classname>TypeError</classname>, por lo que no se necesita ninguna comprobación o prueba de errores adicional.
|
|
</para>
|
|
</example>
|
|
</para>
|
|
|
|
<para>
|
|
|
|
<example>
|
|
<title>Valores exclusivos avanzados</title>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
enum UserStatus: string
|
|
{
|
|
case Pending = 'P';
|
|
case Active = 'A';
|
|
case Suspended = 'S';
|
|
case CanceledByUser = 'C';
|
|
|
|
public function label(): string
|
|
{
|
|
return match($this) {
|
|
self::Pending => 'Pending',
|
|
self::Active => 'Active',
|
|
self::Suspended => 'Suspended',
|
|
self::CanceledByUser => 'Canceled by user',
|
|
};
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
|
|
<para>
|
|
En este ejemplo, el estado de un usuario puede ser uno de, y exclusivamente, <literal>UserStatus::Pending</literal>,
|
|
<literal>UserStatus::Active</literal>, <literal>UserStatus::Suspended</literal>, o
|
|
<literal>UserStatus::CanceledByUser</literal>. Una función puede tipar un parámetro contra
|
|
<literal>UserStatus</literal> y luego solo aceptar esos cuatro valores, punto.
|
|
</para>
|
|
|
|
<para>
|
|
Los cuatro valores tienen un método <literal>label()</literal>, que devuelve un string legible por humanos.
|
|
Ese string es independiente del string equivalente escalar de "nombre de máquina", que puede usarse en,
|
|
por ejemplo, un campo de base de datos o un cuadro de selección HTML.
|
|
</para>
|
|
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
|
|
foreach (UserStatus::cases() as $case) {
|
|
printf('<option value="%s">%s</option>\n', $case->value, $case->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
|
|
-->
|