PHPを使用して列挙を実装するにはどうすればよいですか?

不言
リリース: 2023-04-05 20:00:02
転載
3790 人が閲覧しました

この記事では、PHP を使用して列挙を実装する方法について説明します。一定の参考値があるので、困っている友人は参考にしていただければ幸いです。

列挙

数学とコンピュータ サイエンスの理論では、集合の 列挙とは、ある有限の数列セットのすべてのメンバーをリストするプログラム、または数列の数をリストするプログラムです。特定のタイプのオブジェクト。この 2 つのタイプは、多くの場合 (常にではありませんが) 重複します。

列挙型は、整定定数の名前付きコレクションです。列挙型は日常生活で非常に一般的です。たとえば、週を表す SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、および SATURDAY は列挙型です。 ——Wikipedia

ビジネス シナリオ

実際の開発プロセスでは、列挙型に触れるのは非常に簡単ですが、PHP の列挙型のネイティブ サポートはあまり充実していないためです。多くの場合、開発者は列挙型の使用に注意を払わず、代わりにグローバル定数またはクラス定数を使用しますが、原則として、これら 2 つのデータは依然として 文字列 であり、型の判定には使用できません。

ビジネス

  • 注文ステータス支払い保留中/出荷保留中/受領保留中/評価保留中
  • メンバーステータス有効化/非アクティブ
  • . ...

待ってください。多くの場合、単純な 1/2/3/4 または 0/1 を使用してそれを表し、ドキュメントまたはコメントでこれらを指定します。

より高度な方法としては、定数として定義して統一アクセスを容易にすることですが、定数の値は文字列のままであり、型を判断することはできません。

ここで、PHP の列挙のサポートについて見てみる必要があります。PHP は列挙を完全にはサポートしていませんが、SPL

SPL Enumeration

SplEnum extends SplType {/ Constants /
const NULL __default = NULL ;
/ 方法 /
public getConstList ([ bool $include_default = FALSE ] ) : array
/ 继承的方法 /
SplType::__construct ( [mixed $initial_value [, bool $strict ]] )
}
ログイン後にコピー
には基本的な列挙クラスが存在します。 ###しかし!これには、PECL の追加インストールが必要です。PECL を使用して

Spl_Types をインストールすると、意図せず使用コストが増加します。他に解決策はありますか?答えは「はい」です。

手書きで直接書きます。

準備を開始します

最初に列挙型を定義します

class Enum
{
    // 默认值
    const __default = self::WAIT_PAYMENT;
    // 待付款
    const WAIT_PAYMENT = 0;
    // 待发货
    const WAIT_SHIP = 1;
    // 待收货
    const WAIT_RECEIPT = 2;
    // 待评价
    const WAIT_COMMENT = 3;
}
ログイン後にコピー

これで完了したようです。

Enum::WAIT_PAYMENT を直接使用して取得できます。価値があります。しかし、パラメータが渡される場所ではそれを検証できません。

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

ここでは、PHP オブジェクト指向でマジック メソッド __toString() を使用する必要があります。

public __toString (void): string

__toString() メソッドの使用方法クラスは文字列として扱われる場合に応答する必要があります。たとえば、echo $obj; は何かを表示するはずです。このメソッドは文字列を返す必要があります。そうでない場合は、E_RECOVERABLE_ERROR レベルの致命的なエラーが発行されます。
次に、このメソッドを改良してみましょう。

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;
ログイン後にコピー

まずは形になりつつある

部分的には達成できたような気がしますが、どうやってもっとうまくやってもらおうか?もう一度作り直してみましょう。

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;
ログイン後にコピー

今回は、

コンストラクターを追加し、オプションの値を渡すことができるようにし、その値が __toString メソッドの出力値として使用されます。 time 渡されたパラメータが期待したものと異なっていても、機能は実装されているようです。しかし、一致しない場合はどうすればよいでしょうか?ほら、3️⃣の時点ですでに事故になっていますが、何か改善方法はありますか?答えはもちろん Yes です。ここでは、PHP のもう 1 つの優れた点であるリフレクション クラスを使用します。もちろん、これは PHP に固有のものではなく、他の言語でも利用できます。 もちろん、リフレクションに加えて、
__callStatic メソッド内で別の機能 メソッドのオーバーロード も使用します。

さらに進みます

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

静的コンテキストでアクセスできないメソッドを呼び出す場合、 __callStatic() が呼び出されます。
$name パラメータは、呼び出されるメソッドの名前です。 $arguments パラメーターは、メソッド $name に渡されるパラメーターを含む列挙配列です。

変革を続けます。

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);
ログイン後にコピー

これで、単純な列挙クラスが完成しました。

End

値が列挙範囲内にあるかどうかを判断するなど、他のニーズがある場合はどうすればよいでしょうか?すべての列挙値を取得しますか?すべての列挙キーを取得し、列挙キーが有効かどうかを判断しますか?自動フォーマット "

__toString メソッドでは文字列のみを返すことができるため、場合によっては整数、ブール値、およびその他の型の必要性を強制する場合があります。"

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));
ログイン後にコピー
現時点では、完全な列挙が完了しています ~

以上がPHPを使用して列挙を実装するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:segmentfault.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート