如何在 PHP 中实现类似 Enum 的功能? [复制]

Posted

技术标签:

【中文标题】如何在 PHP 中实现类似 Enum 的功能? [复制]【英文标题】:How to implement Enum like functionality in PHP? [duplicate] 【发布时间】:2010-12-04 10:30:36 【问题描述】:

如何在 php 中使用类似 Enum 的功能(在 Java 和其他高级语言中提供)?我知道 PHP 目前不允许您创建枚举,但最接近的枚举是什么?

【问题讨论】:

我找到了这篇 (it.toolbox.com/blogs/macsploitation/…) 文章。 另见:***.com/questions/1486747/… 【参考方案1】:

也许使用const

class SomeClass 
    const FIRSTVAL = 1;
    const SECONDVAL = 2;
;

【讨论】:

问题在于 Enum 需要是全局的。类常量不会是真正的 嗯,SomeClass::FIRSTVAL 可以从任何地方引用 ;-) self::FIRSTVAL 类内 我也会将类设为抽象类和最终类 @MikeL,如果您仅将类用作命名空间,这可能是一个很好的预防措施,但如果您只是在具有其他目的的类中这样做,则可能不是这样;)【参考方案2】:

这是@Kris 代码的更新版本,可以更好地与较新版本的 PHP 配合使用。它是根据@lassombra 评论制作的。

/**
 * Implements the abstract base for all enum types
 * @see http://***.com/a/2324746/1003020
 * @see http://***.com/a/254543/1003020
 *
 * Example of a typical enum:
 *
 *    class DayOfWeek extends Enum
 *    
 *        const Sunday    = 0;
 *        const Monday    = 1;
 *        const Tuesday   = 2;
 *        const Wednesday = 3;
 *        const Thursday  = 4;
 *        const Friday    = 5;
 *        const Saturday  = 6;
 *    
 *
 * Usage examples:
 *
 *     $monday = DayOfWeek::Monday                      // (int) 1
 *     DayOfWeek::isValidName('Monday')                 // (bool) true
 *     DayOfWeek::isValidName('monday', $strict = true) // (bool) false
 *     DayOfWeek::isValidValue(0)                       // (bool) true
 *     DayOfWeek::fromString('Monday')                  // (int) 1
 *     DayOfWeek::toString(DayOfWeek::Tuesday)          // (string) "Tuesday"
 *     DayOfWeek::toString(5)                           // (string) "Friday"
 **/
abstract class Enum

    private static $constCacheArray = NULL;

    private static function getConstants()
    
        if (self::$constCacheArray == NULL) 
            self::$constCacheArray = [];
        
        $calledClass = get_called_class();
        if (!array_key_exists($calledClass, self::$constCacheArray)) 
            $reflect = new \ReflectionClass($calledClass);
            self::$constCacheArray[$calledClass] = $reflect->getConstants();
        
        return self::$constCacheArray[$calledClass];
    

    public static function isValidName($name, $strict = false)
    
        $constants = self::getConstants();

        if ($strict) 
            return array_key_exists($name, $constants);
        

        $keys = array_map('strtolower', array_keys($constants));
        return in_array(strtolower($name), $keys);
    

    public static function isValidValue($value, $strict = true)
    
        $values = array_values(self::getConstants());
        return in_array($value, $values, $strict);
    

    public static function fromString($name)
    
        if (self::isValidName($name, $strict = true)) 
            $constants = self::getConstants();
            return $constants[$name];
        

        return false;
    

    public static function toString($value)
    
        if (self::isValidValue($value, $strict = true)) 
            return array_search($value, self::getConstants());
        

        return false;
    

【讨论】:

我喜欢这个,非常喜欢。它真的应该比我的原版评分更高。【参考方案3】:

自从我发布这个答案后,@Vinicius-Garcia 已经改进了这个解决方案,his version is undoubtedly better 对于大多数用户来说。

下面的旧答案:

我使用类常量和一些反射技巧。

<?php
/**
 * @package Red.Core
 * @author kris@theredhead.nl
 *
 * Implements the abstract base for all enum types
 *
 * example of a typical enum:
 *
 *    class DayOfWeek extends Enum
 *    
 *        const Sunday    = 0;
 *        const Monday    = 1;
 *        const Tuesday   = 2;
 *        const Wednesday = 3;
 *        const Thursday  = 4;
 *        const Friday    = 5;
 *        const Saturday  = 6;
 *    
 *
 * usage examples:
 *
 *     $monday = Enum::FromString( 'DayOfWeek::Monday' );           // (int) 1
 *     $monday = DayOfWeek::Monday                                  // (int) 1
 *     $monday = Enum::ToString( 'DayOfWeek', DayOfWeek::Monday );  // (string) "DayOfWeek::Monday"
 *     $monday = Enum::Label( 'DayOfWeek', DayOfWeek::Monday );     // (string) "Monday"
 *
 **/
abstract class Enum

    // make sure there are never any instances created
    final private function __construct()
    
        throw new Exception( 'Enum and Subclasses cannot be instantiated.' );
    

    /**
     * Give the integer associated with the const of the given string in the format of "class:const"
     *
     * @param string $string
     * @return integer
     */
    final public static function FromString( $string )
    
        if ( strpos( $string, '::' ) < 1 )
        
            throw new Exception( 'Enum::FromString( $string ) Input string is not in the expected format.' );
        
        list( $class, $const ) = explode( '::', $string );

        if ( class_exists( $class, false ) )
        
            $reflector = new ReflectionClass( $class );
            if ( $reflector->IsSubClassOf( 'Enum' ) )
            
                if ( $reflector->hasConstant( $const ) )
                
                    return eval( sprintf( 'return %s;', $string ) );
                
            
        
        throw new Excption( sprintf( '%s does not map to an Enum field', $string ) );
    

    final public static function IsValidValue( $enumType, $enumValue )
    
        if ( class_exists( $enumType ) )
        
            $reflector = new ReflectionClass( $enumType );
            if ( $reflector->IsSubClassOf( 'Enum' ) )
            
                foreach( $reflector->getConstants() as $label => $value )
                
                    if ( $value == $enumValue )
                    
                        return true;
                    
                
            
        
        return false;
    

    final public static function IsValidLabel( $enumType, $enumValue )
    
        if ( class_exists( $enumType ) )
        
            $reflector = new ReflectionClass( $enumType );
            if ( $reflector->IsSubClassOf( 'Enum' ) )
            
                foreach( $reflector->getConstants() as $label => $value )
                
                    if ( $label == $enumValue )
                    
                        return true;
                    
                
            
        
        return false;
    

    /**
     * For a given $enumType, give the complete string representation for the given $enumValue (class::const)
     *
     * @param string $enumType
     * @param integer $enumValue
     * @return string
     */
    final public static function ToString( $enumType, $enumValue )
    
        $result = 'NotAnEnum::IllegalValue';

        if ( class_exists( $enumType, false ) )
        
            $reflector = new ReflectionClass( $enumType );
            $result = $reflector->getName() . '::IllegalValue';
            foreach( $reflector->getConstants() as $key => $val )
            
                if ( $val == $enumValue )
                
                    $result = str_replace( 'IllegalValue', $key, $result );
                    break;
                
            
        
        return $result;
    

    /**
     * For a given $enumType, give the label associated with the given $enumValue (const name in class definition)
     *
     * @param string $enumType
     * @param integer $enumValue
     * @return string
     */
    final public static function Label( $enumType, $enumValue )
    
        $result = 'IllegalValue';

        if ( class_exists( $enumType, false ) )
        
            $reflector = new ReflectionClass( $enumType );

            foreach( $reflector->getConstants() as $key => $val )
            
                if ( $val == $enumValue )
                
                    $result = $key;
                    break;
                
            
        
        return $result;
    

?>

【讨论】:

我喜欢它——这是一个很棒的解决方案! 我知道这是可怕的 necro,但是使用 PHP 5.3+ static::class 常量,您不必使用传递 $enumType 的版本,因为您可以在方法中解决它。这也使您不必进行大量检查以查看该值是否有效,因为 PHP 本身提供了它。它还使您能够避免 eval,而是返回 static::$enumValue【参考方案4】:

你也可以用这个:

class Enum

    private $m_valueName = NULL;

    private function __construct($valueName)
        $this->m_valueName = $valueName;
    

    public static function __callStatic($methodName, $arguments)
        $className = get_called_class();
        return new $className($methodName);
    

    function __toString()
        return $this->m_valueName;
    


class NotificationType extends Enum
    const Notification = NULL;
    const Warning = NULL;
    const Error = NULL;


function Test(NotificationType $type)
    echo "Test function, type: $type<br>";


Test(NotificationType::Warning());

【讨论】:

不错,尤其是考虑到NotificationType::Warning() == NotificationType::Warning()。但是,有一个缺点,就是每次调用都会创建一个新对象。 创建一个新对象是一个巨大的缺点。枚举器背后的想法是这些常量易于访问。【参考方案5】:

提供了一个SplEnum 类。

文档中的示例用法:

<?php
class Month extends SplEnum 
    const __default = self::January;

    const January = 1;
    const February = 2;
    const March = 3;
    const April = 4;
    const May = 5;
    const June = 6;
    const July = 7;
    const August = 8;
    const September = 9;
    const October = 10;
    const November = 11;
    const December = 12;


echo new Month(Month::June) . PHP_EOL;

try 
    new Month(13);
 catch (UnexpectedValueException $uve) 
    echo $uve->getMessage() . PHP_EOL;

上面的例子会输出

6
Value not a const in enum Month

另一种可能是使用myclabs/php-enum 包。

【讨论】:

【参考方案6】:

你可以使用常量

class myClass 
    const aValue = 123;
    const aString = "ABC";
;

但它不会提供一个很好的方式来遍历它们,所以我可能会选择一个关联数组,因为它更容易管理:

class myClass
  $enum = array ("first" => 123, 
                "second" => "ABC");


【讨论】:

【参考方案7】:

一个便宜的技巧是创建一个包含可能值的数组。但是,与上述答案不同,我会选择键/值对相等的数组,即:

<?php
$enum = Array(
'apple' => 'apple',
'pear' => 'pear',
'orange' => 'orange'
);
?>

这样,如果($enum[$value] != $value),你知道给定的值不在集合中。

当然,如果您希望键/值对不同,则可以使用常规数组。

【讨论】:

【参考方案8】:

作为一个数组。

$arr = array('A','B','C','D');

$find = 'A';
$key = array_search($find,$arr);
echo $arr[$key];

【讨论】:

【参考方案9】:

就我而言,我需要存储在整个应用程序中使用的权限名称。我最终得到了一个基本枚举抽象类,它为枚举定义了几个实用函数,然后对其进行了扩展。这是基本枚举类:

<?php

namespace App\Enums;


use ReflectionClass;

abstract class BasicEnum 
    private static $constCacheArray = NULL;

    public static function getConstants() 
        if (self::$constCacheArray == NULL) 
            self::$constCacheArray = [];
        
        $calledClass = get_called_class();
        if (!array_key_exists($calledClass, self::$constCacheArray)) 
            $reflect = new ReflectionClass($calledClass);
            self::$constCacheArray[$calledClass] = $reflect->getConstants();
        
        return self::$constCacheArray[$calledClass];
    

    public static function isValidName($name, $strict = false) 
        $constants = self::getConstants();

        if ($strict) 
            return array_key_exists($name, $constants);
        

        $keys = array_map('strtolower', array_keys($constants));
        return in_array(strtolower($name), $keys);
    

    public static function isValidValue($value, $strict = true) 
        $values = array_values(self::getConstants());
        return in_array($value, $values, $strict);
    

以下是通过扩展抽象类创建的示例枚举:

<?php

namespace App\Enums;


class Permissions extends BasicEnum

    const COMMENTS_CREATE = 'create comments';
    const COMMENTS_VIEW = 'view comments';
    const COMMENTS_EDIT = 'edit comments';
    const COMMENTS_DELETE = 'delete comments';
    const COMMENTS_RESTORE = 'restore comments';

    const REACTIONS_CREATE = 'create reactions';
    const REACTIONS_VIEW = 'view reactions';
    const REACTIONS_EDIT = 'edit reactions';
    const REACTIONS_DELETE = 'delete reactions';
    const REACTIONS_RESTORE = 'restore reactions';

    const QUESTIONS_CREATE = 'create questions';
    const QUESTIONS_VIEW = 'view questions';
    const QUESTIONS_EDIT = 'edit questions';
    const QUESTIONS_DELETE = 'delete questions';
    const QUESTIONS_RESTORE = 'restore questions';

    const PERMISSIONS_CREATE = 'create permissions';
    const PERMISSIONS_VIEW = 'view permissions';
    const PERMISSIONS_EDIT = 'edit permissions';
    const PERMISSIONS_DELETE = 'delete permissions';
    const PERMISSIONS_RESTORE = 'restore permissions';



<?php

namespace App\Enums;


class PostTypes extends BasicEnum

    const POST    = 'post';
    const NEWS    = 'news';
    const SERVICE    = 'service';

下面是 Permissions 枚举的示例用法:

/**
 * Determine whether the user can create reactions.
 *
 * @param User $user
 * @return mixed
 */
public function create(User $user)

    return $user->can(Permissions::REACTIONS_CREATE);

希望这会有所帮助。

【讨论】:

以上是关于如何在 PHP 中实现类似 Enum 的功能? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Web 应用程序中实现版本控制?

javascript中实现类似php 的var_dump

如何在 Jinja 中实现类似 Django 的标签

如何在zepto中实现类似slideDown()的jquery

如何在 UITextView 中实现滚动功能?

如何在Python中实现EXCEL的查找功能