Enumeraciones PHP

En el marco de la descripción del área temática, son comunes conceptos con un número limitado de significados. Las enumeraciones son las mejores para esto. PHP no tiene construcciones especiales para describir una enumeración, pero se pueden imitar usando un enfoque orientado a objetos.



Implementación más simple



En el caso más simple, puede implementar una enumeración como un objeto contenedor sobre un tipo simple, limitando programáticamente los argumentos de entrada. Como ejemplo, puede tomar las estaciones, de las cuales hay cuatro y solo cuatro.



class Season
{
    public const SUMMER = 'summer';
    public const AUTUMN = 'autumn';
    public const WINTER = 'winter';
    public const SPRING = 'spring';

    private string $value;

    public function __construct(string $value)
    {
        if (
            self::SUMMER !== $value &&
            self::AUTUMN !== $value &&
            self::WINTER !== $value &&
            self::SPRING !== $value
        ) {
            throw new InvalidArgumentException(sprintf(
                "Wrong season value. Awaited '%s', '%s', '%s' or '%s'.",
                self::SUMMER,
                self::AUTUMN,
                self::WINTER,
                self::SPRING
            ));
        }

        $this->value = $value;
    }


Puede demostrar el proceso de creación de una enumeración con una prueba.



    public function testCreation(): void
    {
        $summer = new Season(Season::SUMMER);
        $autumn = new Season(Season::AUTUMN);
        $winter = new Season(Season::WINTER);
        $spring = new Season(Season::SPRING);

        $this->expectException(InvalidArgumentException::class);

        $wrongSeason = new Season('Wrong season');
    }


-. toValue() getValue(). , , __toString().



    public function __toString(): string
    {
        return $this->value;
    }


__toString() - .



    public function testStringConcatenation(): void
    {
        $autumn = new Season(Season::AUTUMN);
        $spring = new Season(Season::SPRING);
        $value = $autumn . ' ' . $spring;

        $this->assertIsString($value);
        $this->assertSame(Season::AUTUMN . ' ' . Season::SPRING, $value);
    }


PHP , . , , , .



    public function testEquality(): void
    {
        $firstSummer = new Season(Season::SUMMER);
        $secondSummer = new Season(Season::SUMMER);
        $winter = new Season(Season::WINTER);

        $this->assertTrue($firstSummer == $secondSummer);
        $this->assertFalse($firstSummer == $winter);
        $this->assertFalse($firstSummer === $secondSummer);
        $this->assertFalse($firstSummer === $winter);
    }


equals.



    public function equals(Season $season): bool
    {
        return $this->value === $season->value;
    }


    public function testEquals(): void
    {
        $firstSummer = new Season(Season::SUMMER);
        $secondSummer = new Season(Season::SUMMER);
        $firstWinter = new Season(Season::WINTER);
        $secondWinter = new Season(Season::WINTER);

        $this->assertTrue($firstSummer->equals($secondSummer));
        $this->assertTrue($firstWinter->equals($secondWinter));
        $this->assertFalse($firstSummer->equals($secondWinter));
    }


: Season . .



: . xlr 3pin, jack, mini jack usb .



class MicrophoneConnector
{
    public const XLR_3PIN = 'xlr_3pin';
    public const JACK = 'jack';
    public const MINI_JACK = 'mini_jack';
    public const USB = 'usb';

    private string $value;

    private function __construct(string $value)
    {
        $this->value = $value;
    }

    public function __toString(): string
    {
        return $this->value;
    }


. . .



    public static function xlr3pin(): self
    {
        return new self(self::XLR_3PIN);
    }


jack, miniJack usb.



    public function testEquality(): void
    {
        $firstJack = MicrophoneConnector::jack();
        $secondJack = MicrophoneConnector::jack();
        $xlr3pin = MicrophoneConnector::xlr3pin();

        $this->assertTrue($firstJack == $secondJack);
        $this->assertFalse($firstJack == $xlr3pin);
        $this->assertFalse($firstJack === $secondJack);
        $this->assertFalse($firstJack === $xlr3pin);
    }




, . .



: . , , , . agree, disagree hold.



class Decision
{
    public const AGREE = 'agree';
    public const DISAGREE = 'disagree';
    public const HOLD = 'hold';

    private string $value;

    private function __construct(string $value)
    {
        $this->value = $value;
    }

    private function __clone() { }

    public function __toString(): string
    {
        return $this->value;
    }


__clone(). .



    private static $agreeInstance = null;

    public static function agree(): self
    {
        if (null === self::$agreeInstance) {
            self::$agreeInstance = new self(self::AGREE);
        }

        return self::$agreeInstance;
    }


c agree disagree hold.



    public function testEquality(): void
    {
        $firsAgree = Decision::agree();
        $secondAgree = Decision::agree();
        $firstDisagree = Decision::disagree();
        $secondDisagree = Decision::disagree();

        $this->assertTrue($firsAgree == $secondAgree);
        $this->assertTrue($firstDisagree == $secondDisagree);
        $this->assertFalse($firsAgree == $secondDisagree);
        $this->assertTrue($firsAgree === $secondAgree);
        $this->assertTrue($firstDisagree === $secondDisagree);
        $this->assertFalse($firsAgree === $secondDisagree);
    }


. __toString() . : . .



    public static function from($value): self
    {
        switch ($value) {
            case self::AGREE:
                return self::agree();

            case self::DISAGREE:
                return self::disagree();

            case self::HOLD:
                return self::hold();

            default:
                throw new InvalidArgumentException(sprintf(
                    "Wrong decision value. Awaited '%s', '%s' or '%s'.",
                    self::AGREE,
                    self::DISAGREE,
                    self::HOLD
                ));
        }
    }


: . . : __sleep() __wakeup(). __sleep() . __wakeup() from() Decision.



class Order
{
    private Decision $decision;
    private string $description;

    public function getDecision(): Decision
    {
        return $this->decision;
    }

    public function getDescription(): string
    {
        return $this->description;
    }

    public function __construct(Decision $decision, string $description)
    {
        $this->decision = $decision;
        $this->description = $description;
    }

    public function __sleep(): array
    {
        return ['decision', 'description'];
    }

    public function __wakeup(): void
    {
        $this->decision = Decision::from($this->decision);
    }
}


/ .



    public function testSerialization(): void
    {
        $order = new Order(Decision::hold(), 'Some order description');

        $serializedOrder = serialize($order);
        $this->assertIsString($serializedOrder);

        /** @var Order $unserializedOrder */
        $unserializedOrder = unserialize($serializedOrder);
        $this->assertInstanceOf(Order::class, $unserializedOrder);

        $this->assertTrue($order->getDecision() === $unserializedOrder->getDecision());
    }




, , . , , . , .



class Season
{
    public const SEASONS = ['summer', 'autumn', 'winter', 'spring'];

    private string $value;

    public function __construct(string $value)
    {
        if (!in_array($value, self::SEASONS)) {
            throw new InvalidArgumentException(sprintf(
                "Wrong season value. Awaited one from: '%s'.",
                implode("', '", self::SEASONS)
            ));
        }

        $this->value = $value;
    }


.



: . .



class Size
{
    public const SIZES = ['xxs', 'xs', 's', 'm', 'l', 'xl', 'xxl'];

    private string $value;

    private function __construct(string $value)
    {
        $this->value = $value;
    }

    public function __toString(): string
    {
        return $this->value;
    }

    public static function __callStatic($name, $arguments)
    {
        $value = strtolower($name);
        if (!in_array($value, self::SIZES)) {
            throw new BadMethodCallException("Method '$name' not found.");
        }

        if (count($arguments) > 0) {
            throw new InvalidArgumentException("Method '$name' expected no arguments.");
        }

        return new self($value);
    }
}


    public function testEquality(): void
    {
        $firstXxl = Size::xxl();
        $secondXxl = Size::xxl();
        $firstXxs = Size::xxs();
        $secondXxs = Size::xxs();

        $this->assertTrue($firstXxl == $secondXxl);
        $this->assertTrue($firstXxs == $secondXxs);
        $this->assertFalse($firstXxl == $secondXxs);
        $this->assertFalse($firstXxl === $secondXxl);
        $this->assertFalse($firstXxs === $secondXxs);
        $this->assertFalse($firstXxl === $secondXxs);
    }


, , IDE __callStatic(). DocBlock', .



/**
 * @method static Size xxs()
 * @method static Size xs()
 * @method static Size s()
 * @method static Size m()
 * @method static Size l()
 * @method static Size xl()
 * @method static Size xxl()
 */
class Size
{




, , , .



PECL- SPL SplEnum. SPL ( ).





PHP no tiene construcciones especiales para describir una enumeración, pero se pueden imitar usando un enfoque orientado a objetos. Aunque el uso de la enumeración como singleton ofrece algunas ventajas, tiene un inconveniente crítico que complica significativamente la implementación. Cabe señalar que esto no es tanto un problema de implementación de enumeración como un problema general de implementación del patrón "singleton" en PHP. Las soluciones generalizadas no ofrecen prácticamente ninguna ventaja, ya que todavía hay que describir métodos específicos en DocBlock.



Todos los ejemplos están en GitHub .




All Articles