Maison  >  Article  >  développement back-end  >  Comment implémenter l’énumération en utilisant PHP ?

Comment implémenter l’énumération en utilisant PHP ?

不言
不言avant
2019-04-12 10:23:093720parcourir

Le contenu de cet article explique comment utiliser PHP pour implémenter l'énumération ? Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il vous sera utile.

Énumération

En théorie des mathématiques et de l'informatique, une énumération d'un ensemble est un programme qui répertorie tous les membres d'un ensemble fini de séquences, ou un nombre de objets d'un type spécifique. Les deux types se chevauchent souvent (mais pas toujours).

Une énumération est une collection nommée de constantes entières. Les énumérations sont très courantes dans la vie quotidienne. Par exemple, DIMANCHE, LUNDI, MARDI, MERCREDI, JEUDI, VENDREDI et SAMEDI représentant la semaine sont une énumération. —— Wikipédia

Scénario commercial

Dans le processus de développement réel, il nous est très facile d'entrer en contact avec les types d'énumération, mais parce que le support natif de PHP pour les énumérations n'est pas très bien, donc Souvent, les développeurs ne font pas attention à l'utilisation des énumérations, mais utilisent à la place des constantes globales ou des constantes de classe. En principe, ces deux données sont toujours 字符串 et ne peuvent pas être utilisées pour le jugement de type.

Entreprise

  • Statut de la commande en attente de paiement/en attente d'expédition/en attente de réception/en attente d'évaluation
  • Statut du membre activé/inactif
  • ...

Attendez, nous utiliserons souvent de simples 1/2/3/4 ou 0/1 pour le représenter, puis spécifierons ces choses dans des documents ou des commentaires.

Une approche plus avancée consiste à la définir comme une constante, puis à faciliter un accès unifié, mais la valeur de la constante est toujours une chaîne et le jugement de type ne peut pas être effectué.

Ici, nous devons jeter un œil à la prise en charge des énumérations par PHP. Bien que PHP n'ait pas un support parfait pour les énumérations, il existe toujours une classe d'énumération de base dans SPL

Énumération SPL

SplEnum extends SplType {/ Constants /
const NULL __default = NULL ;
/ 方法 /
public getConstList ([ bool $include_default = FALSE ] ) : array
/ 继承的方法 /
SplType::__construct ( [mixed $initial_value [, bool $strict ]] )
}

Mais ! Cela nécessite une installation supplémentaire de PECL. Utilisez PECL pour installer Spl_Types, ce qui augmente involontairement le coût d'utilisation. Existe-t-il d'autres solutions ? La réponse est oui.

Écrivez-en un directement à la main.

Démarrer la préparation

Définissez d'abord une énumération

class Enum
{
    // 默认值
    const __default = self::WAIT_PAYMENT;
    // 待付款
    const WAIT_PAYMENT = 0;
    // 待发货
    const WAIT_SHIP = 1;
    // 待收货
    const WAIT_RECEIPT = 2;
    // 待评价
    const WAIT_COMMENT = 3;
}

Cela semble terminé, nous pouvons obtenir la valeur à l'intérieur directement en utilisant Enum::WAIT_PAYMENT, mais nous ne pouvons pas vérifier le paramètres où ils sont passés.

function setStatus(Enum $status){
    // TODO
}
setStatus(Enum::WAIT_PAYMENT);
// Error 显然这是不行的 因为上面常量的值时一个 int 并不是 Enum 类型。

Ici, nous devons utiliser une méthode magique en PHP orientée objet __toString()

public __toString ( void ): string

méthode __toString() Utilisé pour comment répondre lorsqu'une classe est traitée comme une chaîne. Par exemple, echo $obj; devrait afficher quelque chose. Cette méthode doit renvoyer une chaîne, sinon une erreur fatale de niveau E_RECOVERABLE_ERROR sera émise.

Améliorons maintenant cette méthode.

class OrderStatus extends Enum
{
    // 默认值
    const __default = self::WAIT_PAYMENT;
    // 待付款
    const WAIT_PAYMENT = 0;
    // 待发货
    const WAIT_SHIP = 1;
    // 待收货
    const WAIT_RECEIPT = 2;
    // 待评价
    const WAIT_COMMENT = 3;

    public function __toString()
    {
        return '233';
    }
}
// object
echo gettype($orderStatus) . PHP_EOL;
// boolean true
var_dump($orderStatus instanceof Enum);
// 233
echo $orderStatus;

Modèle initial

Il semble qu'une partie de celui-ci ait été mise en œuvre ici, alors comment devrions-nous l'améliorer ? Réorganisons-le à nouveau.

class OrderStatus extends Enum
{
    // 默认值
    const __default = self::WAIT_PAYMENT;
    // 待付款
    const WAIT_PAYMENT = 0;
    // 待发货
    const WAIT_SHIP = 1;
    // 待收货
    const WAIT_RECEIPT = 2;
    // 待评价
    const WAIT_COMMENT = 3;
    /**
     * @var string
     */
    protected $value;

    public function __construct($value = null)
    {
        $this->value = is_null($value) ? self::__default : $value;
    }

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

// 1️⃣
$orderStatus = new OrderStatus(OrderStatus::WAIT_SHIP);

// object
echo gettype($orderStatus) . PHP_EOL;
// boolean true
var_dump($orderStatus instanceof Enum);
// 1
echo $orderStatus . PHP_EOL;
// 2️⃣
$orderStatus = new OrderStatus();
// object
echo gettype($orderStatus) . PHP_EOL;
// boolean true
var_dump($orderStatus instanceof Enum);
// 0
echo $orderStatus;
// 3️⃣
$orderStatus = new OrderStatus('意外的参数');
// object
echo gettype($orderStatus) . PHP_EOL;
// boolean true
var_dump($orderStatus instanceof Enum);
// 意外的参数
echo $orderStatus;

Cette fois, nous avons ajouté 构造函数 et lui avons permis de transmettre une valeur facultative, qui est ensuite utilisée comme valeur de sortie de la méthode __toString Cette fois, cela a l'air bien ainsi que les fonctions. sont tous prêts à être implémentés, si les paramètres transmis ne correspondent pas à nos attentes. Mais que se passe-t-il si cela ne correspond pas ? Écoutez, au point 3️⃣, c'est déjà devenu un accident. Y a-t-il un moyen d'y remédier ? La réponse est bien sûr 有的. Ici, nous utiliserons une autre bonne chose de PHP, la classe de réflexion. Bien sûr, elle n'est pas propre à PHP, mais existe également dans d'autres langages.
Bien sûr, en plus de la réflexion, nous utiliserons également la méthode 方法重载 dans autre chose __callStatic.

Aller plus loin

public static __callStatic ( string $name , array $arguments ) : mixte

Lors de l'appel d'une méthode inaccessible dans un static context , __callStatic() sera appelé.

Le paramètre $name est le nom de la méthode à appeler. Le paramètre $arguments est un tableau d'énumération contenant les paramètres à passer à la méthode $name.

Continuez à vous transformer.

class Enum
{
    const __default = null;
    /**
     * @var string
     */
    protected static $value;

    // 注意这里 将构造函数的 修饰符改成了 受保护的 即 外部无法直接 new
    protected function __construct($value = null)
    {
        // 很常规
        self::$value = is_null($value) ? static::__default : $value;
    }

    /**
     * @param $name
     * @param $arguments
     * @return mixed
     * @throws ReflectionException
     */
    public static function __callStatic($name, $arguments)
    {
        // 实例化一个反射类 static::class 表示调用者
        $reflectionClass = new ReflectionClass(static::class);
        // 这里我们要有一个约定, 就是类常量成员的名字必须的大写。
        // 这里就是取出来调用的静态方法名对应的常量值 虽然这里有个 getValue 方法
        // 但是因为其返回值不可靠 我们就依赖于他原本的隐式的 __toString 方法来帮我们输出字符串即可。
        $constant = $reflectionClass->getConstant(strtoupper($name));
        // 获取调用者的 构造方法
        $construct = $reflectionClass->getConstructor();
        // 设置成可访问 因为我们把修饰符设置成了受保护的 这里需要访问到,所以就需要设置成可访问的。
        $construct->setAccessible(true);
        // 因为现在类已经是可以访问的了所以我们直接实例化即可,实例化之后 PHP 会自动调用 __toString 方法 使得返回预期的值。
        $static = new static($constant);
        return $static;
    }

    public function __toString()
    {
        return (string)self::$value;
    }

}

class OrderStatus extends Enum
{
    // 默认值
    const __default = self::WAIT_PAYMENT;
    // 待付款
    const WAIT_PAYMENT = 0;
    // 待发货
    const WAIT_SHIP = 1;
    // 待收货
    const WAIT_RECEIPT = 2;
    // 待评价
    const WAIT_COMMENT = 3;

}

$WAIT_SHIP = OrderStatus::WAIT_SHIP();
var_dump($WAIT_SHIP . '');
var_dump($WAIT_SHIP instanceof Enum);

Ici, un simple cours d'énumération est terminé.

Fin

Et si nous avons d'autres besoins, comme déterminer si une valeur est dans la plage d'énumération ? Obtenir toutes les valeurs d'énumération ? Obtenir toutes les clés d'énumération et déterminer si les clés d'énumération sont valides ? Formatage automatique "Parce que la méthode __toString permet uniquement de renvoyer des chaînes, mais parfois nous forçons le besoin d'entier, de booléen et d'autres types "

class Enum
{
    const __default = null;
    /**
     * @var string
     */
    protected static $value;
    /**
     * @var ReflectionClass
     */
    protected static $reflectionClass;

    // 注意这里 将构造函数的 修饰符改成了 受保护的 即 外部无法直接 new
    protected function __construct($value = null)
    {
        // 很常规
        self::$value = is_null($value) ? static::__default : $value;
    }

    /**
     * @param $name
     * @param $arguments
     * @return mixed
     */
    public static function __callStatic($name, $arguments)
    {
        // 实例化一个反射类 static::class 表示调用者
        $reflectionClass = self::getReflectionClass();
        // 这里我们要有一个约定, 就是类常量成员的名字必须的大写。
        // 这里就是取出来调用的静态方法名对应的常量值 虽然这里有个 getValue 方法
        // 但是因为其返回值不可靠 我们就依赖于他原本的隐式的 __toString 方法来帮我们输出字符串即可。
        $constant = $reflectionClass->getConstant(strtoupper($name));
        // 获取调用者的 构造方法
        $construct = $reflectionClass->getConstructor();
        // 设置成可访问 因为我们把修饰符设置成了受保护的 这里需要访问到,所以就需要设置成可访问的。
        $construct->setAccessible(true);
        // 因为现在类已经是可以访问的了所以我们直接实例化即可,实例化之后 PHP 会自动调用 __toString 方法 使得返回预期的值。
        $static = new static($constant);
        return $static;
    }

    /**
     * 实例化一个反射类
     * @return ReflectionClass
     * @throws ReflectionException
     */
    protected static function getReflectionClass()
    {
        if (!self::$reflectionClass instanceof ReflectionClass) {
            self::$reflectionClass = new ReflectionClass(static::class);
        }
        return self::$reflectionClass;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return (string)self::$value;
    }

    /**
     * 判断一个值是否有效 即是否为枚举成员的值
     * @param $val
     * @return bool
     * @throws ReflectionException
     */
    public static function isValid($val)
    {
        return in_array($val, self::toArray());
    }

    /**
     * 转换枚举成员为键值对输出
     * @return array
     * @throws ReflectionException
     */
    public static function toArray()
    {
        return self::getEnumMembers();
    }

    /**
     * 获取枚举的常量成员数组
     * @return array
     * @throws ReflectionException
     */
    public static function getEnumMembers()
    {
        return self::getReflectionClass()
            ->getConstants();
    }

    /**
     * 获取枚举成员值数组
     * @return array
     * @throws ReflectionException
     */
    public static function values()
    {
        return array_values(self::toArray());
    }

    /**
     * 获取枚举成员键数组
     * @return array
     * @throws ReflectionException
     */
    public static function keys()
    {
        return array_keys(self::getEnumMembers());
    }

    /**
     * 判断 Key 是否有效 即存在
     * @param $key
     * @return bool
     * @throws ReflectionException
     */
    public static function isKey($key)
    {
        return in_array($key, array_keys(self::getEnumMembers()));
    }

    /**
     * 根据 Key 去获取枚举成员值
     * @param $key
     * @return static
     */
    public static function getKey($key)
    {
        return self::$key();
    }

    /**
     * 格式枚举结果类型
     * @param null|bool|int $type 当此处的值时什么类时 格式化输出的即为此类型
     * @return bool|int|string|null
     */
    public function format($type = null)
    {
        switch (true) {
            // 当为纯数字 或者类型处传入的为 int 值时 转为 int
            case ctype_digit(self::$value) || is_int($type):
                return (int)self::$value;
                break;
            // 当 type 传入 true 时 返回 bool 类型
            case $type === true:
                return (bool)filter_var(self::$value, FILTER_VALIDATE_BOOLEAN);
                break;
            default:
                return self::$value;
                break;
        }
    }

}

class OrderStatus extends Enum
{
    // 默认值
    const __default = self::WAIT_PAYMENT;
    // 待付款
    const WAIT_PAYMENT = 0;
    // 待发货
    const WAIT_SHIP = 1;
    // 待收货
    const WAIT_RECEIPT = 2;
    // 待评价
    const WAIT_COMMENT = 3;

}

$WAIT_SHIP = OrderStatus::WAIT_SHIP();
// 直接输出是字符串
echo $WAIT_SHIP;
// 判断类型是否存在
var_dump($WAIT_SHIP instanceof OrderStatus);
// 格式化输出一下 是要 字符串 、还是 bool 还是整形
// 自动
var_dump($WAIT_SHIP->format());
// 整形
var_dump($WAIT_SHIP->format(1));
// bool
var_dump($WAIT_SHIP->format(true));
// 判断这个值是否有效的枚举值
var_dump(OrderStatus::isValid(2));
// 判断这个值是否有效的枚举值
var_dump(OrderStatus::isValid(8));
// 获取所有枚举成员的 Key
var_dump(OrderStatus::keys());
// 获取所有枚举成员的值
var_dump(OrderStatus::values());
// 获取枚举成员的键值对
var_dump(OrderStatus::toArray());
// 判断枚举 Key 是否有效
var_dump(OrderStatus::isKey('WAIT_PAYMENT'));
// 判断枚举 Key 是否有效
var_dump(OrderStatus::isKey('WAIT_PAYMENT_TMP'));
// 根据 Key 取去 值 注意 这里取出来的已经不带有类型了
// 更加建议直接使用 取类常量的方式去取 或者在高版本的 直接使用类常量修饰符 
// 将类常量不可见最佳,但是需要额外处理了
var_dump(OrderStatus::getKey('WAIT_PAYMENT')
    ->format(1));

À partir de maintenant, une énumération complète est effectuée ~

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer