主題分野の説明の枠組みの中で、限られた数の意味を持つ概念が一般的です。これには列挙が最適です。PHPには、列挙を記述するための特別な構造はありませんが、オブジェクト指向のアプローチを使用して模倣できます。
最も単純な実装
最も単純なケースでは、入力引数をプログラムで制限することにより、単純な型のラッパーオブジェクトとして列挙を実装できます。例として、季節は4つしかありません。
class Season
{
public const SUMMER = 'summer';
public const AUTUMN = 'autumn';
public const WINTER = 'winter';
public const SPRING = 'spring';
private string $value;
public function __construct(string $value)
{
if (
self::SUMMER !== $value &&
self::AUTUMN !== $value &&
self::WINTER !== $value &&
self::SPRING !== $value
) {
throw new InvalidArgumentException(sprintf(
"Wrong season value. Awaited '%s', '%s', '%s' or '%s'.",
self::SUMMER,
self::AUTUMN,
self::WINTER,
self::SPRING
));
}
$this->value = $value;
}
テストを使用して列挙を作成するプロセスを示すことができます。
public function testCreation(): void
{
$summer = new Season(Season::SUMMER);
$autumn = new Season(Season::AUTUMN);
$winter = new Season(Season::WINTER);
$spring = new Season(Season::SPRING);
$this->expectException(InvalidArgumentException::class);
$wrongSeason = new Season('Wrong season');
}
-. toValue()
getValue()
. , , __toString()
.
public function __toString(): string
{
return $this->value;
}
__toString()
- .
public function testStringConcatenation(): void
{
$autumn = new Season(Season::AUTUMN);
$spring = new Season(Season::SPRING);
$value = $autumn . ' ' . $spring;
$this->assertIsString($value);
$this->assertSame(Season::AUTUMN . ' ' . Season::SPRING, $value);
}
PHP , . , , , .
public function testEquality(): void
{
$firstSummer = new Season(Season::SUMMER);
$secondSummer = new Season(Season::SUMMER);
$winter = new Season(Season::WINTER);
$this->assertTrue($firstSummer == $secondSummer);
$this->assertFalse($firstSummer == $winter);
$this->assertFalse($firstSummer === $secondSummer);
$this->assertFalse($firstSummer === $winter);
}
equals
.
public function equals(Season $season): bool
{
return $this->value === $season->value;
}
public function testEquals(): void
{
$firstSummer = new Season(Season::SUMMER);
$secondSummer = new Season(Season::SUMMER);
$firstWinter = new Season(Season::WINTER);
$secondWinter = new Season(Season::WINTER);
$this->assertTrue($firstSummer->equals($secondSummer));
$this->assertTrue($firstWinter->equals($secondWinter));
$this->assertFalse($firstSummer->equals($secondWinter));
}
: Season . .
: . xlr 3pin, jack, mini jack usb .
class MicrophoneConnector
{
public const XLR_3PIN = 'xlr_3pin';
public const JACK = 'jack';
public const MINI_JACK = 'mini_jack';
public const USB = 'usb';
private string $value;
private function __construct(string $value)
{
$this->value = $value;
}
public function __toString(): string
{
return $this->value;
}
. . .
public static function xlr3pin(): self
{
return new self(self::XLR_3PIN);
}
jack
, miniJack
usb
.
public function testEquality(): void
{
$firstJack = MicrophoneConnector::jack();
$secondJack = MicrophoneConnector::jack();
$xlr3pin = MicrophoneConnector::xlr3pin();
$this->assertTrue($firstJack == $secondJack);
$this->assertFalse($firstJack == $xlr3pin);
$this->assertFalse($firstJack === $secondJack);
$this->assertFalse($firstJack === $xlr3pin);
}
, . .
: . , , , . agree, disagree hold.
class Decision
{
public const AGREE = 'agree';
public const DISAGREE = 'disagree';
public const HOLD = 'hold';
private string $value;
private function __construct(string $value)
{
$this->value = $value;
}
private function __clone() { }
public function __toString(): string
{
return $this->value;
}
__clone()
. .
private static $agreeInstance = null;
public static function agree(): self
{
if (null === self::$agreeInstance) {
self::$agreeInstance = new self(self::AGREE);
}
return self::$agreeInstance;
}
c agree
disagree
hold
.
public function testEquality(): void
{
$firsAgree = Decision::agree();
$secondAgree = Decision::agree();
$firstDisagree = Decision::disagree();
$secondDisagree = Decision::disagree();
$this->assertTrue($firsAgree == $secondAgree);
$this->assertTrue($firstDisagree == $secondDisagree);
$this->assertFalse($firsAgree == $secondDisagree);
$this->assertTrue($firsAgree === $secondAgree);
$this->assertTrue($firstDisagree === $secondDisagree);
$this->assertFalse($firsAgree === $secondDisagree);
}
. __toString()
. : . .
public static function from($value): self
{
switch ($value) {
case self::AGREE:
return self::agree();
case self::DISAGREE:
return self::disagree();
case self::HOLD:
return self::hold();
default:
throw new InvalidArgumentException(sprintf(
"Wrong decision value. Awaited '%s', '%s' or '%s'.",
self::AGREE,
self::DISAGREE,
self::HOLD
));
}
}
: . . : __sleep()
__wakeup()
. __sleep()
. __wakeup()
from()
Decision
.
class Order
{
private Decision $decision;
private string $description;
public function getDecision(): Decision
{
return $this->decision;
}
public function getDescription(): string
{
return $this->description;
}
public function __construct(Decision $decision, string $description)
{
$this->decision = $decision;
$this->description = $description;
}
public function __sleep(): array
{
return ['decision', 'description'];
}
public function __wakeup(): void
{
$this->decision = Decision::from($this->decision);
}
}
/ .
public function testSerialization(): void
{
$order = new Order(Decision::hold(), 'Some order description');
$serializedOrder = serialize($order);
$this->assertIsString($serializedOrder);
/** @var Order $unserializedOrder */
$unserializedOrder = unserialize($serializedOrder);
$this->assertInstanceOf(Order::class, $unserializedOrder);
$this->assertTrue($order->getDecision() === $unserializedOrder->getDecision());
}
, , . , , . , .
class Season
{
public const SEASONS = ['summer', 'autumn', 'winter', 'spring'];
private string $value;
public function __construct(string $value)
{
if (!in_array($value, self::SEASONS)) {
throw new InvalidArgumentException(sprintf(
"Wrong season value. Awaited one from: '%s'.",
implode("', '", self::SEASONS)
));
}
$this->value = $value;
}
.
: . .
class Size
{
public const SIZES = ['xxs', 'xs', 's', 'm', 'l', 'xl', 'xxl'];
private string $value;
private function __construct(string $value)
{
$this->value = $value;
}
public function __toString(): string
{
return $this->value;
}
public static function __callStatic($name, $arguments)
{
$value = strtolower($name);
if (!in_array($value, self::SIZES)) {
throw new BadMethodCallException("Method '$name' not found.");
}
if (count($arguments) > 0) {
throw new InvalidArgumentException("Method '$name' expected no arguments.");
}
return new self($value);
}
}
public function testEquality(): void
{
$firstXxl = Size::xxl();
$secondXxl = Size::xxl();
$firstXxs = Size::xxs();
$secondXxs = Size::xxs();
$this->assertTrue($firstXxl == $secondXxl);
$this->assertTrue($firstXxs == $secondXxs);
$this->assertFalse($firstXxl == $secondXxs);
$this->assertFalse($firstXxl === $secondXxl);
$this->assertFalse($firstXxs === $secondXxs);
$this->assertFalse($firstXxl === $secondXxs);
}
, , IDE __callStatic()
. DocBlock', .
/**
* @method static Size xxs()
* @method static Size xs()
* @method static Size s()
* @method static Size m()
* @method static Size l()
* @method static Size xl()
* @method static Size xxl()
*/
class Size
{
, , , .
PECL- SPL SplEnum. SPL ( ).
PHPには、列挙を記述するための特別な構造はありませんが、オブジェクト指向のアプローチを使用して模倣できます。シングルトンとして列挙を使用することにはいくつかの利点がありますが、実装を大幅に複雑にする重大な欠点があります。これは、PHPでの「シングルトン」パターンの実装の一般的な問題ほど、列挙実装の問題ではないことに注意してください。特定のメソッドをDocBlockで説明する必要があるため、一般化されたソリューションには実質的に利点はありません。
すべての例はGitHubにあります。