PHP枚举

在主题领域描述的框架内,含义有限的概念是常见的。枚举是最好的选择。PHP没有用于描述枚举的特殊构造,但是可以使用面向对象的方法来模仿它们。



最简单的实施



在最简单的情况下,可以通过编程限制输入参数,将枚举实现为简单类型上的包装对象。例如,您可以选择四个季节,只有四个季节。



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;
    }


您可以演示使用测试创建枚举的过程。



    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没有用于描述枚举的特殊构造,但是可以使用面向对象的方法来模仿它们。尽管将枚举作为单例使用具有一些优点,但它具有一个严重的缺点,使实现变得非常复杂。应该注意的是,这与其说是枚举实现问题,还不如说是PHP中“单身”模式实现的一般问题。通用解决方案实际上没有任何优势,因为DocBlock中仍然必须描述特定的方法。



所有示例都在GitHub上




All Articles