开发者

How to implement Enum like functionality in PHP? [duplicate]

开发者 https://www.devze.com 2022-12-08 01:12 出处:网络
This question already has answers here: Enumer开发者_JS百科ations on PHP (39 answers) Closed last year.
This question already has answers here: Enumer开发者_JS百科ations on PHP (39 answers) Closed last year.

How can Enum-like functionality (as provided in Java and other high level languages) be used in PHP? I know PHP doesn't allow you to create enums currently, but what's the closest one could get?


Using const, perhaps.

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


This is an updated version from the @Kris code, to work better with newer versions of PHP. It was made based on @lassombra comment.

/**
 * Implements the abstract base for all enum types
 * @see http://stackoverflow.com/a/2324746/1003020
 * @see http://stackoverflow.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;
    }
}


Since I posted this answer, @Vinicius-Garcia has improved upon this solution and his version is undoubtedly better for most users.

Old answer below:

I use class constants, and a bit of reflection trickery.

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


You may also use this one:

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());


There is an SplEnum class provided.

Sample usage from the docs:

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

The above example will output

6
Value not a const in enum Month

Another possibility is to use the myclabs/php-enum package.


You could use constants

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

But it wouldn't give a nice way of iterating through them so i would probably opt for an associate array as it would be easier to manage:

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

}


A cheap trick is to create an array with possible values. However, unlike the above answers, I'd opt for an array where key/value pairs are equal, i.e.:

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

That way, if ($enum[$value] != $value), you know the value given is not in the set.

Ofcourse, if you want the key/value pairs to be different, a regular array would be the way to go.


As an array.

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

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


In my case I needed to store permission names which are used throughout the app. I ended up with a base enum abstract class that defined several utility functions for enums, and then extended it. Here's the base enum class:

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

Here are example enums created by extending the abstract class:

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

And here's an example usage of the Permissions enum:

/**
 * Determine whether the user can create reactions.
 *
 * @param User $user
 * @return mixed
 */
public function create(User $user)
{
    return $user->can(Permissions::REACTIONS_CREATE);
}

Hope this helps.

0

精彩评论

暂无评论...
验证码 换一张
取 消