Heim  >  Artikel  >  Backend-Entwicklung  >  Wie implementiert man eine Aufzählung mit PHP?

Wie implementiert man eine Aufzählung mit PHP?

不言
不言nach vorne
2019-04-12 10:23:093786Durchsuche

Der Inhalt dieses Artikels befasst sich mit der Frage, wie man mit PHP eine Aufzählung implementiert. Es hat einen gewissen Referenzwert. Freunde in Not können sich darauf beziehen. Ich hoffe, es wird Ihnen hilfreich sein.

Aufzählung

In der Mathematik- und Informatiktheorie ist eine Aufzählung einer Menge ein Programm, das alle Mitglieder einer endlichen Menge von Folgen auflistet, oder eine Anzahl von Objekte eines bestimmten Typs. Die beiden Typen überschneiden sich oft (aber nicht immer).

Eine Aufzählung ist eine benannte Sammlung ganzzahliger Konstanten. Aufzählungen sind beispielsweise SONNTAG, MONTAG, DIENSTAG, MITTWOCH, FREITAG und SAMSTAG, die die Woche darstellen. —— Wikipedia

Geschäftsszenario

Im eigentlichen Entwicklungsprozess kommen wir sehr leicht mit Aufzählungstypen in Kontakt, da PHP jedoch keine native Unterstützung für Aufzählungen bietet gut, daher achten Entwickler oft nicht auf die Verwendung von Aufzählungen, sondern verwenden stattdessen globale Konstanten oder Klassenkonstanten. Im Prinzip sind diese beiden Daten immer noch 字符串 und können nicht zur Typbeurteilung verwendet werden.

Geschäftlich

  • Bestellstatus Ausstehende Zahlung/Ausstehender Versand/Ausstehende Quittung/Ausstehende Bewertung
  • Mitgliedsstatus Aktiviert/Inaktiv
  • ...

Warten Sie, oft verwenden wir einfach 1/2/3/4 oder 0/1, um es darzustellen, und geben diese Dinge dann in Dokumenten oder Kommentaren an.

Ein fortgeschrittenerer Ansatz besteht darin, sie als Konstante zu definieren und dann einen einheitlichen Zugriff zu ermöglichen, aber der Wert der Konstante ist immer noch eine Zeichenfolge und eine Typbeurteilung kann nicht durchgeführt werden.

Hier müssen wir einen Blick auf die PHP-Unterstützung für Aufzählungen werfen. Obwohl PHP keine perfekte Unterstützung für Aufzählungen bietet, gibt es in SPL immer noch eine grundlegende Aufzählungsklasse

SPL Enumeration

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

Aber! Dies erfordert eine zusätzliche Installation von PECL. Verwenden Sie PECL, um Spl_Types zu installieren, was die Nutzungskosten unbeabsichtigt erhöht. Die Antwort ist ja.

Schreiben Sie eines direkt von Hand.

Vorbereitung starten

Definieren Sie zunächst eine Aufzählung

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

Dies scheint abgeschlossen zu sein, indem wir Enum::WAIT_PAYMENT verwenden, aber Parameter übergeben, die wir nicht überprüfen können Es.

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

Hier müssen wir eine magische Methode in PHP verwenden, objektorientiert __toString()

public __toString (void): string

__toString() Methode wird verwendet Wie a Die Klasse sollte reagieren, wenn sie als Zeichenfolge behandelt wird. Beispielsweise sollte echo $obj; etwas anzeigen. Diese Methode muss eine Zeichenfolge zurückgeben, andernfalls wird ein schwerwiegender Fehler der Stufe E_RECOVERABLE_ERROR ausgegeben.

Jetzt verbessern wir diese Methode.

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;

Das Modell nimmt Gestalt an

Es scheint, dass ein Teil davon hier umgesetzt wurde. Wie können wir ihn also dazu bringen, es besser zu machen? Lassen Sie es uns noch einmal überarbeiten.

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;

Dieses Mal haben wir 构造函数 hinzugefügt und ihm erlaubt, einen optionalen Wert zu übergeben, der dann als Ausgabewert der __toString-Methode verwendet wird. Diesmal sieht es gut aus und die Funktionen waren es auch implementiert, wenn die übergebenen Parameter nicht unseren Erwartungen entsprechen. Was aber, wenn es nicht passt? Schauen Sie, am Punkt 3️⃣ ist es bereits ein Unfall geworden. Gibt es eine Möglichkeit, Abhilfe zu schaffen? Die Antwort lautet natürlich: 有的 Hier verwenden wir eine weitere gute Sache von PHP, die Reflection-Klasse. Diese gibt es natürlich nicht nur bei PHP, sondern gibt es auch in anderen Sprachen.
Natürlich werden wir die 方法重载Methode neben der Reflexion auch in einer anderen Sache __callStatic anwenden.

Gehen Sie einen Schritt weiter

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

Beim Aufrufen einer unzugänglichen Methode in einem statischen context wird __callStatic() aufgerufen.

Der Parameter $name ist der Name der aufzurufenden Methode. Der Parameter $arguments ist ein Aufzählungsarray, das die Parameter enthält, die an die Methode $name übergeben werden sollen.

Verwandeln Sie sich weiter.

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

Das ist eine einfache Aufzählungsklasse.

Ende

Was ist, wenn wir andere Anforderungen haben, beispielsweise die Feststellung, ob ein Wert innerhalb des Aufzählungsbereichs liegt? Alle Aufzählungswerte abrufen? Alle Aufzählungsschlüssel abrufen und feststellen, ob die Aufzählungsschlüssel gültig sind? Automatische Formatierung „Weil die __toString-Methode nur die Rückgabe von Zeichenfolgen zulässt, aber manchmal erzwingen wir die Notwendigkeit von Ganzzahlen, Bool-Werten und anderen Typen

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

Ab sofort ist eine vollständige Aufzählung abgeschlossen ~

Das obige ist der detaillierte Inhalt vonWie implementiert man eine Aufzählung mit PHP?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen