-
Notifications
You must be signed in to change notification settings - Fork 2
Usage
This page discusses usage of the php-enum project.
This is the primary feature of php-enum.
A set of simple values, such as behavior options, can be easily expressed using a set of constants:
class DataProvider
{
const OPTION_TRIM_WHITEPSACE = 1;
const OPTION_NULLIFY_EMPTY_STRING = 2;
const OPTION_EXCEPTION_IF_MISSING = 4;
public function getDataValue($key, $options = 0) { ... }
}This is a sufficient solution since we just need to identify each option. getDataValue() simply needs to react differently based on which options are given. When all you need is simple identification, PHP's build in constants should be used; they solve that problem well.
However, this familiar solution does not work for defining a complex constant...
Consider a chat application which allows the user to pick 1 of 10, predefined chat colors. Identification of the color, while important, would not be enough on its own. This is where php-enum can help.
The ChatColor is considered a complex constant because all of its attributes are constant. That is to say, the red, green, and blue values for the light red chat color are always 255, 76, and 76 respectively. There is nothing that happens at run-time which can alter those values. All attributes of the ChatColor are immutable; they are constant.
We can easily represent the full set of available ChatColor options using php-enum:
/**
* Enum names are represented as dynamic methods. They do not exist
* in code thanks to the __callStatic() magic method. We SHOULD
* always declare them using phpDoc annotations so that they appear
* as part of exported docs and to provide code completion in our
* IDE.
*
* @method static ChatColor LIGHT_RED()
* @method static ChatColor DARK_BROWN()
* @method ...
*/
<?php
class ChatColor
{
// Bring in core php-enum functionality
use CRussell52\Enum\TEnum;
// Define standard class stuff. Provide properties and getters,
// but no setters because all aspects of an Enum value are
// always immutable.
private $_redValue;
private $_greenValue;
private $_blueValue;
public function getRedValue() { ... }
public function getBlueValue() { ... }
public function getGreenValue() { ... }
// Now, provide the php-enum stuff...
// Provide definitions for each ChatColor
protected static function _initializeDefinitions()
{
return [
'LIGHT_RED' => [255, 76, 76],
'DARK_BROWN' => [115, 29, 29],
...
];
}
// Provide mapper which sets properties.
protected function _populate(array $args)
{
// Definition provides red, green, and blue value (in that order)
$this->_redValue = $args[0];
$this->_greenValue = $args[1];
$this->_blueValue = $args[2];
}
}We've now represented a set of complex constants, each of which can be used like any other object. Invoking ChatColor::LIGHT_RED() provides an instance of ChatColor.
$color = ChatColor::LIGHT_RED();Because we now have an instance of ChatColor, we can access its details just like any other object!
$color->getRed(); // 255
$color->getGreen(); // 255
$color->getBlue(); // 0Since every item given by the above php-enum is a ChatColor instance, we can also add in methods which interact with the defined properties. For example, we can add in a method which provides the HTML hex code based on the RGB value.
class ChatColor
{
...
public function getHTMLHexCode() {
// Original algorithm from:
// http://bavotasan.com/2011/convert-hex-color-to-rgb-using-php/
$hex = "#";
$hex .= str_pad(dechex($this->_redValue), 2, "0", STR_PAD_LEFT);
$hex .= str_pad(dechex($this->_greenValue), 2, "0", STR_PAD_LEFT);
$hex .= str_pad(dechex($this->_blueValue), 2, "0", STR_PAD_LEFT);
return $hex; // returns the hex value including the number sign (#)
}
}Now it is simple to get the HTML hex code for any enum value:
$color = ChatColor::LIGHT_RED();
echo $color->getHTMLHexCode(); // #ff4c4cIt's reasonable to say that our fictional chat application has some methods that must operate based on a chat color.
If we were using class constants to identify our chat colors, we would have something like this:
/**
* Generate a chat message using the given text and chat color.
*
* @param string $text The text to appear in the message.
* @param int $chatColor The chat color to apply to the message. This should
* be a chat color as defined by the ChatColor::COLOR_*
* family of constants. Any other value will be ignored
* the default chat color will be applied.
*/
function generateMessage($text, $chatColor);This implementation has a couple of challenges. As discussed earlier, it needs some internal means by which to interpret the $chatColor identifier to get the details of the target color. It also needs some way to tell if the value given is an invalid chat color so that it can apply the "default chat color".
If we substitute our php-enum implementation of ChatColor from above, we can simply add type-hinting to the method signature and be done with it.
/**
* Generate a chat message using the given text and chat color.
*
* @param string $text The text to appear in the message.
* @param ChatColor $chatColor The chat color to apply to the message.
*/
function generateMessage($text, ChatColor $chatColor);This solves both problems. The function is given all of the color details and the type-hint guarantees that the value $chatColor is one of the available chat colors.
Logic is kind of important for what we programmers do. Using constants as identifiers makes it easy for us to do stuff like if statements and switch blocks. The good news is, it's just as easy with php-enum values!
Since all attributes of a php-enum are immutable, we can reliable perform comparisons:
ChatColor::LIGHT_RED() == ChatColor::LIGHT_RED(); // Always trueThat means we can do conditionals and switch blocks just fine...
// Use in conditional
if ($color == ChatColor::LIGHT_RED()) { ... }
// Use in switch block
switch ($color)
{
case ChatColor::LIGHT_RED():
...
break;
case ChatColor::DARK_BROWN():
...
break;
}tldr; Never use PHP's identical operator (===) when comparing php-enum values. Instead, use the equals operator (==).
Internally, php-enum will re-use the same instance every time a specific enum value is requested. So, identical comparisons will often evaluate to true:
ChatColor::LIGHT_RED() === ChatColor::LIGHT_RED(); // true
$color1 = ChatColor::DARK_BROWN();
$color2 = ChatColor::DARK_BROWN();
$color1 === $color2; // trueHowever, a deserialized enum value will never be considered identical to any other enum value:
$color1 = ChatColor::DARK_BROWN();
$colorSerial = serialize($color);
$color2 = unserialize($colorSerial);
$color1 === $color1; // false
$color2 === ChatColor::DARK_BROWN(); // falseThis is an unavoidable situation due to the limitations of user-land code's influence on the deserialization process. For this reason, the equals operator (==) should always be used when comparing enum values.
Want to paint a help screen which lists all of the available colors? Easy:
foreach (ChatColor::getValues() as $color)
{
echo '<div style="background-color:' . $color->getHtmlCode() . '">' . $color->getName() . '</div>';
}Technically speaking, this isn't really a feature of php-enum. It's really just an added benefit of representing our complex constants as class instances. Since we define our Enum as a class, we can attach an interface to it to add a degree of abstraction.
For example, imagine that we have imported a library to help generate the colored messages for our chat application. The fictional library provides the following constructs:
namespace Acme\ColoredMessages;
class MessageBuilder
{
public function buildMessage($text, RGBColor $color) { ... }
}namespace Acme\ColoredMessages;
interface RGBColor
{
/**
* @return array This method returns a 3-element array where the values represent a color's
* red, green, and blue values (in that order).
*/
public function getRGBValue()
}We can simply adapt our ChatColor class from earlier to implement the new interface:
class ChatColor implements Acme\ColoredMessages\RGBColor
{
use CRussell52\Enum\TEnum;
// php-enum implementation
...
// RGBColor implementation
public function getRGBValue()
{
return [$this->_redValue, $this->_greenValue, $this->_blueValue];
}
}And then we can use any of the enum values to interact with the library:
$msgBuilder = new Acme\ColoredMessages\MessageBuilder();
$msgBuilder->buildMessage($someText, ChatColor::LIGHT_RED());