在 PHP 7 中创建类似枚举的行为的推荐方法是啥? [复制]

Posted

技术标签:

【中文标题】在 PHP 7 中创建类似枚举的行为的推荐方法是啥? [复制]【英文标题】:What is the recommended way of creating enum-like behaviour in PHP 7? [duplicate]在 PHP 7 中创建类似枚举的行为的推荐方法是什么? [复制] 【发布时间】:2019-09-13 13:17:14 【问题描述】:

有点令人惊讶的是,php 没有对枚举的原生支持。在 PHP 5.4 中,可以使用原生扩展 SPL_Types 来模拟 SplEnum 的这种行为,但此扩展的最后一次更新是在 2012 年。在 PHP 7 上,尝试安装扩展会由于接口更改而引发编译错误.

因此,我想知道目前推荐的获得类枚举行为的方法是什么。我希望能够编写至少类似于此的代码:

enum DaysOfTheWeek

    Monday = 1,
    Tuesday = 2,
    Wednesday = 3,
    Thursday = 4,
    Friday = 5,
    Saturday = 6,
    Sunday = 7


$day = DaysOfTheWeek::Monday;
echo $day; //Prints 1

我当前的代码看起来不太简洁,并且有很多(不必要的)开销:

class DayOfTheWeek

    private __value;

    const Monday = 1;
    const Tuesday = 2;
    const Wednesday = 3;
    const Thursday = 4;
    const Friday = 5;
    const Saturday = 6;
    const Sunday = 7;

    public function __construct(int $value)
    
        if ($value !== DayOfTheWeek::Monday
            && $value !== DayOfTheWeek::Tuesday
            && $value !== DayOfTheWeek::Wednesday
            && $value !== DayOfTheWeek::Thursday
            && $value !== DayOfTheWeek::Friday
            && $value !== DayOfTheWeek::Saturday
            && $value !== DayOfTheWeek::Sunday
        )
        
            throw new InvalidArgumentException("Invalid day of the week");
        
    

    public function GetValue(): int
    
        return $this->__value;
    


一些说明:

为什么不使用带有几个常量值的抽象类?

因为这完全违背了 PHP 7 中类型提示的想法。这个想法是我可以告诉一个函数接受这个枚举的一个实例并确定它是一个合法的值。

为什么不优化一周中的日子?您可以通过查看它是否在 0 和 6 之间来简化检查。

因为枚举不必须按顺序排列,或显示彼此之间的任何关系。枚举的值可以是 56、1999、120、-12400、8、-1239 和 44。枚举的代码不应依赖于枚举的值。

【问题讨论】:

考虑使用范围 0..6(它从 0 开始计数)而不是 1..7。这有一些优点,例如:$dayname = [ 'Mon'..,'Sun'] 或者如果计算 $day = ($day + 6 ) %7 来获得现在 +6 的工作日。 @Wiimm 我特别没有使用任何“快捷方式”,因为我想尽可能保持通用性。枚举可以包含任何类型的值,它们之间可能没有任何关系。将此视为“最坏情况”,根本无法进行优化。 为什么不定义一个从 1 到 7 的数组(值是天数)并且在构造函数中只检查键是否存在?还是在私有中定义范围? 具有私有构造函数的最终类?这样它就永远不能被扩展或实例化 - 这应该给你一个具体的常量值集,比如 DayOfTheWeek::Monday 就像一个枚举......或者我错过了什么? @CD001 是的,因为实例化实际上对枚举很有用。 【参考方案1】:

我喜欢在我的项目中这样做。如果您好奇,您会意识到您可以构建枚举层次结构,甚至可以为这些类提供其他用途。

https://gist.github.com/rafageist/aef9825b7c935cdeb0c6187a2d363909

我将此解决方案转换为实际项目。完整的解决方案在这里:https://github.com/divengine/enum。

安装它:

composer require divengine\enum;

定义你的枚举:

<?php

namespace MyEnums;

use divengine\enum;

class Temperature extends enum /* Father of all types of temperatures */
class ExtremeTemperature extends Temperature /* Father of all types of extreme temperatures */
class FIRE extends ExtremeTemperature 
class ICE extends ExtremeTemperature 

class NormalTemperature extends Temperature /* Father of all types of normal temperatures */
class HOT extends NormalTemperature 
class COOL extends NormalTemperature 
class COLD extends NormalTemperature 

使用你的枚举:

<?php

use MyEnums;


// Constants are good tricks, but optional
const COOL = COOL::class;

class AllTemperatures 
    const COOL = COOL::class; // maybe better
    const HOT = 'Enums\\HOT';  // ugly !!!

    //...


// Define some function with type hinting
function WhatShouldIdo(Temperature $temperature)

    switch (true) 
        case $temperature instanceof ExtremeTemperature:
            switch (true) 
                case $temperature instanceof FIRE:
                    return "Call the fire department";

                case $temperature instanceof ICE:
                    return "Warm up";
            
            break;

        case $temperature instanceof NormalTemperature:
            switch ($temperature) 

                case HOT::class: // compare using classname
                    return "Drink a bear :D";

                case COOL or AllTemperatures::COOL: // compare using constants
                    return "Just go away !";

                case 'Enums\\COLD': // compare using string, ugly !!!
                    return "Wear a coat";
            

            break;
    

    return "I don't know";


// Call to function with a instance of any Temperature
echo WhatShouldIdo(new HOT()) . PHP_EOL;

【讨论】:

【参考方案2】:

我仍会使用数组,但基于ReflectionClass 检查给定值是否在类常量中定义

<?
class DayOfTheWeek

    private $__value;
    private $__day;

    const Monday = 1;
    const Tuesday = 2;
    const Wednesday = 3;
    const Thursday = 4;
    const Friday = 5;
    const Saturday = 6;
    const Sunday = 7;

    public function __construct(int $value)
    
        $myClass = new ReflectionClass ( get_class($this) );
        $constants = $myClass->getConstants();
        foreach($constants as $dayName=>$dayValue) 
            if($value === $dayValue) 
                $this->__day = $dayName;
                $this->__value = $dayValue;
                break;
            
        
        if(is_null($this->__day)) 
            throw new InvalidArgumentException("Invalid day of the week");
        
    

    public function GetValue(): int
    
        return $this->__value;
    

用法:

$return = new DayOfTheWeek(5);
var_dump($return);

输出:

object(DayOfTheWeek)#1 (2) 
  ["__value":"DayOfTheWeek":private]=>
  int(5)
  ["__day":"DayOfTheWeek":private]=>
  string(6) "Friday"

我不确定性能问题,但发现了this on SO

更新:在阅读 the documentation 后,我似乎不是唯一一个想出这个想法的人……不过还是很丑……

【讨论】:

我对此表示赞同,但我正在等待接受它,以免阻止更多答案。正如您自己所说,它不必要地丑陋和复杂,但它似乎是 PHP 可以做到的最好的。 你可以等多久,我也很好奇。顺便说一句,好问题

以上是关于在 PHP 7 中创建类似枚举的行为的推荐方法是啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

是否可以在ABAP中创建枚举(枚举)?

PHP:在 yii 中创建更高级别的查询语言以创建条件过滤器的最佳方法是啥

在 C++ 中创建嵌套列表的最佳方法是啥?

单击下拉选项 magento 1.7.0.2 在管理面板中创建网格

如何在 Python 中创建一个行为类似于 Django 抽象基类的类?

PHP:带有值的枚举[重复]