bramus/enumeration – A Package to work with Enumerations in PHP

Earlier this week I released a new package on Packagist. It’s a Package to work with Enumerations in PHP, named bramus/enumeration. The package is my own take on Enumerations in PHP, which grew over time.

πŸ‘¨β€πŸ’» Looking back at the code that eventually would form the basis for this package, I can see – thanks to Git – that I started working on it back in 2013 (😱). Back then PHP Enumeration packages were still sparse, unlike now.

The first implementation basically was a straight copy from this simple Enum class I had then found on StackOverflow. It’s only about a year ago though – when I started working on a new project, that makes heavy use of enumerations – that I started adding new features such as making enumerations instantiable, composed enumerations support, etc.

~

When first looking at a class built upon bramus/enumeration, you can see that it’s quite similar to other implementations (they, after all, all allow you to use enums)

<?php

use Bramus\Enumeration\Enumeration;

class Weekday extends Enumeration
{
	const MONDAY = 1;
	const TUESDAY = 2;
	const WEDNESDAY = 3;
	const THURSDAY = 4;
	const FRIDAY = 5;
	const SATURDAY = 6;
	const SUNDAY = 7;
}

When using the Weekday enumeration shown above, the package starts to differ from some other packages, as bramus/enumeration provides you with several methods to creating enumerations.

Next to directly accessing a value through the class constant’s identifier it’s also possible to instantiate a bramus/enumeration, either through its constructor or by calling a static function that holds the name of the identifier (thanks __callStatic()!).

Weekday::MONDAY
// ~> 1
$instance = new Weekday(1);
$instance = new Weekday(Weekday::MONDAY);
$instance = Weekday::MONDAY(); // <- this is the preferred way to working with bramus/enumeration

πŸ’‘ TIP: By allowing one to create instances of a Enumeration-extended class, you can type hint your enumerations when used as function arguments πŸ˜‰

After having created an instance of a bramus/enumeration class, you can access its data using some logically named accessors:

$instance->getValue();
// ~> 1

$instance->getIdentifier();
// ~> 'MONDAY'

And thanks to its __toString() implementation you can easily pass these instances into query builders and the like, the value will automatically be returned (as a string)

(string) $instance;
// ~> '1'

~

Each class that builds upon bramus/enumeration also has some static functions for:

  • Converting between values and identifiers:

    Weekday::toValue('MONDAY');
    // ~> 1
    
    Weekday::toIdentifier(1);
    // ~> 'MONDAY;
  • Listing values and identifiers:

    Weekday::values();
    // ~> [1, 2, 3, 4, 5, 6, 7]
    
    Weekday::identifiers();
    // ~> ['MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY']
  • Validating values and identifiers:

    Weekday::isValidValue(2);
    // ~> true
    
    Weekday::isValidValue(8);
    // ~> false
    
    Weekday::isValidIdentifier('TUESDAY');
    // ~> true
    
    Weekday::isValidIdentifier('SUMMERDAY');
    // ~> false

~

If you want, you can define a default value to use for a bramus/enumeration. To achieve this, define it as a constant named __DEFAULT on your class and you’re good to go:

<?php

use Bramus\Enumeration\Enumeration;

class Weekday extends Enumeration
{
	const __DEFAULT = 1;

	const MONDAY = 1;
	const TUESDAY = 2;
	…
}
$instance = new Weekday();

// object(Weekday)#424 (2) {
//  ["value":"Bramus\Enumeration\Enumeration":private]=>
//  int(1)
//  ["identifier":"Bramus\Enumeration\Enumeration":private]=>
//  string(6) "MONDAY"
//}

~

Where bramus/enumeration differs from almost all other enumeration packages, is the fact that it also allows one to create Composed Enumerations. These are enumerations that are composed of other enumerations.

A typical example would be the implementation for HTTP Status Codes. Since they are defined in groups, it would be logical to also implement ‘m that way, and then to combine ‘m.

<?php

namespace Bramus\Http\StatusCodes;

use Bramus\Enumeration\Enumeration;
use Bramus\Enumeration\ComposedEnumeration;

abstract class Informational extends Enumeration
{
	const CONTINUE = 100;
	const SWITCHING_PROTOCOLS = 101;
	const PROCESSING = 102;
}

abstract class Success extends Enumeration
{
	const OK = 200;
	const CREATED = 201;
	const ACCEPTED = 202;
	…
}

abstract class Redirection extends Enumeration
{
	const MULTIPLE_CHOICES = 300;
	const MOVED_PERMANENTLY = 301;
	const FOUND = 302;
	…
}

abstract class ClientError extends Enumeration
{
	const BAD_REQUEST = 400;
	const UNAUTHORIZED = 401;
	const PAYMENT_REQUIRED = 402;
	…
	const IM_A_TEAPOT = 418;
	…
}

abstract class ServerError extends Enumeration
{
	const INTERNAL_SERVER_ERROR = 500;
	const NOT_IMPLEMENTED = 501;
	const BAD_GATEWAY = 502;
	…
	const NETWORK_AUTHENTICATION_REQUIRED = 511;
}

class StatusCode extends ComposedEnumeration
{
	public static $classes = [
		'\Bramus\Http\StatusCodes\Informational',
		'\Bramus\Http\StatusCodes\Success',
		'\Bramus\Http\StatusCodes\Redirection',
		'\Bramus\Http\StatusCodes\ClientError',
		'\Bramus\Http\StatusCodes\ServerError',
	];
}

⚠️ Due to PHP not having something like __getStatic() you cannot access constants directly on Composed Enumerations. You must use the direct value in the constructor to create instances when working with Composed Enumerations.

use Bramus\Http\StatusCodes\StatusCode;

// $instance = new StatusCode(StatusCode::OK); <-- This won't work,
$instance = new StatusCode(200);

Due to this limitation, the __callStatic() method to creating instances is recommended, as it works on both singular as composed enumerations:

$instance = StatusCode::OK();

πŸ’‘ If PHP were to support __getStatic(), I would be able to reroute the request to the correct constant on any of the contained classes and return the correct result … but that’s unfortunately not the case right now.

Default values also work with Composed Enumerations:

class StatusCode extends ComposedEnumeration
{
	const __DEFAULT = 200;

	public static $classes = [
		…
	];
}

~

Also shipping with bramus/enumeration are two utility classes. Whilst you most likely don’t need to use these directly, they might be of help:

  1. \Bramus\Enumeration\Helpers\Extractor

    This class extracts constants/identifiers/values from enumerations. It is used by \Bramus\Enumeration\Enumeration internally.

  2. \Bramus\Enumeration\Helpers\Generator

    This class allows one to generate instances of enumerations. Given the example \Bramus\Http\StatusCodes\StatusCode class from above, its usage might be something like this:

    use Bramus\Enumeration\Helpers\Generator;
    
    Generator::setNamespace('\\Bramus\\Http\\StatusCodes\\');
    
    Generator::generateStatusCode(); // Generates a \Bramus\Http\StatusCodes\StatusCode instance with its default value
    Generator::generateStatusCode(404); // Generates a \Bramus\Http\StatusCodes\StatusCode instance with the value 404

    This class might come in handy during unit tests.

    πŸ’β€β™‚οΈ In case the Enumeration has no __DEFAULT (e.g. it is NULL), calling Generator::generate* will return a random value for the Enumeration.

~

Feel free to use this package if you think it will float your boat. If you’ve find an issue, have a feature request, or something else regarding this project, then please open an issue on its GitHub repo.

bramus/enumeration Source (GitHub) →
bramus/enumeration on Packagist →

Did this help you out? Like what you see?
Thank me with a coffee.

I don\'t do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

BuymeaCoffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Published by Bramus!

Bramus is a frontend web developer from Belgium, working as a Chrome Developer Relations Engineer at Google. From the moment he discovered view-source at the age of 14 (way back in 1997), he fell in love with the web and has been tinkering with it ever since (more …)

Unless noted otherwise, the contents of this post are licensed under the Creative Commons Attribution 4.0 License and code samples are licensed under the MIT License

Join the Conversation

2 Comments

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.