Simple opinionated framework agnostic PHP 8.1 enum helper

Overview

Enum Helper-DarkEnum Helper-Light

Enum Helper

Latest Version on Packagist Pest Tests number GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

A simple and opinionated collections of PHP 8.1 enum helpers inspired by archtechx/enums and BenSampo/laravel-enum.
This package is framework agnostic, but has there is an optional localization implementation based on Laravel framework.

Functionalities summary

  • Invokable cases: get the value of enum "invoking" it statically
  • Construct enum by name or value: from(), tryFrom(), fromName(), tryFromName() for all enums
  • Enums Equality: is(), isNot(), in(), notIn() methods
  • Names: methods to have a list of case names (names(), namesArray())
  • Values: methods to have a list of case values (values(), valuesArray())
  • Unique ID: get an unique identifier from instance or instance from identifier (uniqueId(), fromUniqueId())
  • Descriptions & Translations: add description to enum with optional translation (description(),descriptions(),descriptionsArray())

Installation

PHP 8.1+ is required.

composer require datomatic/enum-helper

Usage

You can use the traits you need, but for convenience, you can use only the EnumHelper trait that includes (EnumInvokable, EnumFroms, EnumNames, EnumValues, EnumEquality).
EnumDescription and EnumUniqueId are separated from EnumHelper because they cover edge cases.

The helper support both pure enum (e.g. Status, StatusPascalCase) and BackedEnum (e.g. StatusInt, StatusString).

In all examples we'll use the classes described below:

use Datomatic\EnumHelper\EnumHelper;

enum Status
{
    use EnumHelper;
    
    case PENDING;
    case ACCEPTED;
    case DISCARDED;
    case NO_RESPONSE;
}

enum StatusString: string
{
    use EnumHelper;
    
    case PENDING = 'P';
    case ACCEPTED = 'A';
    case DISCARDED = 'D';
    case NO_RESPONSE = 'N';
}

enum StatusInt: int
{
    use EnumHelper;
    
    case PENDING = 0;
    case ACCEPTED = 1;
    case DISCARDED = 2;
    case NO_RESPONSE = 3;
}

enum StatusPascalCase
{
    use EnumHelper;
    
    case Pending;
    case Accepted;
    case Discarded;
    case NoResponse;
}

The package works with cases written in UPPER_CASE, snake_case and PascalCase.

Jump To

Invokable Cases

This helper lets you get the value of a BackedEnum, or the name of a pure enum, by "invoking" it both statically (Status::pending()), and as an instance ($status()).
A good approach is to call methods in camelCase mode but you can invoke the enum in all cases ::STATICALLY(), ::statically() or ::Statically().

StatusInt::PENDING // Status enum instance
StatusInt::pending(); // 0

That way permits you to use enum invoke into an array keys definition:

'statuses' => [
    Status::pending() => 'some configuration',
...

or in database interactions $db_field_definition->default(Status::PENDING()) or invoke instances to get the primitive value

public function updateStatus(int $status): void;

$task->updateStatus(StatusInt::pending());

Examples use static calls to get the primitive value

// pure enum
Status::noResponse(); // 'NO_RESPONSE'
Status::NO_RESPONSE(); // 'NO_RESPONSE'
Status::NoResponse(); // 'NO_RESPONSE'

// pure eum with PascalCase
StatusPascalCase::noResponse(); // 'NoResponse'
StatusPascalCase::NO_RESPONSE(); // 'NoResponse'
StatusPascalCase::NoResponse(); // 'NoResponse'

// backed int eum
StatusInt::pending(); // 0

// backed int eum
StatusString::pending(); // 'P'

IDE code completion

To have a code completion you can get autosuggestions while typing the enum case and then add () or you can add phpDoc @method tags to the enum class to define all invokable cases like this:

/**
 * @method static string pending()
 * @method static string accepted()
 * @method static string discarded()
 * @method static string noResponse()
 */
enum Status
...

From FromName

This helper adds from() and tryFrom() to pure enums, and adds fromName() and tryFromName() to all enums.

Important Notes:

  • BackedEnum instances already implement their own from() and tryFrom() methods, which will not be overridden by this trait.
  • Pure enums only have named cases and not values, so the from() and tryFrom() methods are functionally equivalent to fromName() and tryFromName()

from()

Status::from('PENDING'); // Status::PENDING
StatusPascalCase::from('Pending'); // StatusPascalCase::Pending
Status::from('MISSING'); // ValueError Exception

tryFrom()

Status::tryFrom('PENDING'); // Status::PENDING
Status::tryFrom('MISSING'); // null

fromName()

Status::fromName('PENDING'); // Status::PENDING
Status::fromName('MISSING'); // ValueError Exception
StatusString::fromName('PENDING'); // StatusString::PENDING
StatusString::fromName('MISSING'); // ValueError Exception

tryFromName()

Status::tryFromName('PENDING'); // Status::PENDING
Status::tryFromName('MISSING'); // null
StatusString::tryFromName('PENDING'); // StatusString::PENDING
StatusString::tryFromName('MISSING'); // null

Equality

This helper permits to compare an enum instance (is(),isNot()) and search if it is present inside an array (in(),notIn()).

is() and isNot()

is() method permit checking the equality of an instance against an enum instance, a case name, or a case value.
For convenience, there is also an isNot() method which is the exact reverse of the is() method.

$enum = Status::PENDING;
$enum->is(Status::PENDING); // true
Status::PENDING->is(Status::ACCEPTED); // false
Status::PENDING->is('PENDING'); // true
Status::PENDING->is('ACCEPTED'); // false
Status::PENDING->isNot('ACCEPTED'); // true


$backedEnum = StatusInt::PENDING;
$backedEnum->is(StatusInt::PENDING); // true
StatusInt::PENDING->is(StatusInt::ACCEPTED); // false
StatusInt::PENDING->is(0); // true
StatusInt::PENDING->is('PENDING'); // true
StatusString::PENDING->is('P'); // true
StatusString::PENDING->isNot('P'); // false

in() and notIn()

in() method permit to see if an instance matches on an array of instances, names or values. For convenience, there is also a notIn() method which is the exact reverse of the i() method.

$enum = Status::PENDING;
$enum->in([Status::PENDING,Status::ACCEPTED]); // true
Status::PENDING->in([Status::DISCARDED, Status::ACCEPTED]); // false
Status::PENDING->in(['PENDING', 'ACCEPTED']); // true
Status::PENDING->in(['ACCEPTED', 'DISCARDED']); // false
Status::PENDING->notIn(['ACCEPTED']); // true

$backedEnum = StatusInt::PENDING;
$backedEnum->in([StatusInt::PENDING, StatusInt::ACCEPTED]); // true
StatusInt::PENDING->in([StatusInt::ACCEPTED])// false
StatusInt::PENDING->in([0, 1, 2]); // true
StatusInt::PENDING->in([2, 3]); // false
StatusInt::PENDING->in(['PENDING', 'ACCEPTED']); // true
StatusInt::PENDING->in(['DISCARDED', 'ACCEPTED']); // false
StatusString::PENDING->in(['P', 'D']); // true
StatusString::PENDING->notIn(['A','D']); // true

Names

This helper offer names() and namesArray() methods.

names()

This method returns a list of case names in the enum.

Status::names(); // ['PENDING', 'ACCEPTED', 'DISCARDED', 'NO_RESPONSE']
StatusPascalCase::names(); // ['Pending', 'Accepted', 'Discarded', 'NoResponse']
StatusString::names(); // ['PENDING', 'ACCEPTED', 'DISCARDED', 'NO_RESPONSE']
// Subset
Status::names([Status::NO_RESPONSE, Status::DISCARDED]); // ['NO_RESPONSE', 'DISCARDED']
StatusPascalCase::names([StatusPascalCase::Accepted, StatusPascalCase::Discarded]); // ['Accepted', 'Discarded']

namesArray()

This method returns a associative array of [value => name] on BackedEnum, names array otherwise.

Status::namesArray(); // ['PENDING', 'ACCEPTED', 'DISCARDED', 'NO_RESPONSE']
StatusString::namesArray(); // [ 'P'=>'PENDING', 'A'=>'ACCEPTED', 'D'=>'DISCARDED'...
StatusInt::namesArray(); // [ 0=>'PENDING', 1=>'ACCEPTED', 2=>'DISCARDED'...
// Subset
StatusInt::namesArray([StatusInt::NO_RESPONSE, StatusInt::DISCARDED]); // [ 3=>'NO_RESPONSE', 2=>'DISCARDED']

Values

This helper offer values() and valuesArray() methods.

values()

This method returns a list of case values for BackedEnum or a list of case names for pure enums.

StatusString::values(); // ['P', 'A', 'D', 'N']
StatusInt::values(); // [0, 1, 2, 3]
// Subset
StatusString::values([StatusString::NO_RESPONSE, StatusString::DISCARDED]); // ['N', 'D']
StatusInt::values([StatusInt::NO_RESPONSE, StatusInt::DISCARDED]); // [3, 2]

valuesArray()

This method returns a associative array of [case name => case value] on BackedEnum, names array otherwise

StatusString::valuesArray(); // ['PENDING' => 'P','ACCEPTED' => 'A','DISCARDED' => 'D','NO_RESPONSE' => 'N']
StatusInt::valuesArray(); // ['PENDING' => 0,'ACCEPTED' => 1,'DISCARDED' => 2,'NO_RESPONSE' => 3]
// Subset
StatusString::valuesArray([StatusString::NO_RESPONSE, StatusString::DISCARDED]); // ['NO_RESPONSE' => 'N', 'DISCARDED' => 'D']
StatusInt::valuesArray([StatusInt::NO_RESPONSE, StatusInt::DISCARDED]); // ['NO_RESPONSE' => 3, 'DISCARDED' => 2]

UniqueId

This helper permits to get an unique identifier from enum or an enum instance from identifier.

The helper is not included on the base EnumHelper trait and does not depend on it, so if you need it you must use EnumUniqueId.

use Datomatic\EnumHelper\Traits\EnumUniqueId;

enum Status
{
    use EnumUniqueId;
    
    ...

uniqueId()

This method returns the enum unique identifier based on Namespace\ClassName.CASE_NAME. You can use this identifier to save multiple types of enums in a database on a polymorphic column.

Status::PENDING->uniqueId(); // Namespace\Status.PENDING
$enum = StatusString::NO_RESPONSE;
$enum->uniqueId(); // Namespace\StatusString.NO_RESPONSE

fromUniqueId()

This method returns an enum instance from unique identifier.

Status::fromUniqueId('Namespace\Status.PENDING'); // Status::PENDING
StatusInt::fromUniqueId('Namespace\StatusInt.PENDING'); // StatusInt::PENDING
StatusInt::fromUniqueId('NOT.valid.uniqueId'); // throw InvalidUniqueId Exception
StatusInt::fromUniqueId('Wrong\Namespace\StatusInt.PENDING'); // throw InvalidUniqueId Exception
StatusInt::fromUniqueId('Namespace\StatusInt.MISSING'); // throw InvalidUniqueId Exception

Global getEnumFromUniqueId() helper

The method fromUniqueId() has little possibility of use because it's related to only an enum class. A better approach is to create a global helper to instantiate any enum from uniqueId like this:

use Datomatic\EnumHelper\Exceptions\InvalidUniqueId;

public function getEnumFromUniqueId(string $uniqueId): object
{
    if (
        !strpos($uniqueId, '.')
        || substr_count($uniqueId, '.') !== 1
    ) {
        throw InvalidUniqueId::uniqueIdFormatIsInvalid($uniqueId);
    }

    list($enumClass, $case) = explode('.', $uniqueId);

    $cases = array_filter($enumClass::cases(), fn($c) => $c->name === $case);

    if (empty($cases)) {
        throw InvalidUniqueId::caseNotPresent($case);
    }
    
    return array_values($cases)[0];
}

Descriptions and Translations

This helper permits to have a description of each case of an enum. Work with both singular language and multilingual application. This is useful when you need descriptions to characterize the cases better or in a multilingual context.

The helper is not included on the base EnumHelper trait and does not depend on it, so if you need it you must use EnumDescription and implement the abstract description() method to define the descriptions. You can use it on both pure enums and BackedEnum.

use Datomatic\EnumHelper\EnumHelper;
use Datomatic\EnumHelper\Traits\EnumDescription;

enum StatusString: string
{
    use EnumHelper;
    use EnumDescription;
    
    case PENDING = 'P';
    case ACCEPTED = 'A';
    case DISCARDED = 'D';
    case NO_RESPONSE = 'N';

    public function description(?string $lang = null): string
    {
        return match ($this) {
            self::PENDING => 'Await decision',
            self::ACCEPTED => 'Recognized valid',
            self::DISCARDED => 'No longer useful',
            self::NO_RESPONSE => 'No response',
        };
    }

After the implementation of description() method you can use it

Status::PENDING->description(); // 'Await decision'

Localization

You can change the description() method with your translation method/helper to translate the descriptions. If you are using Laravel in a multilingual context you can use EnumLaravelLocalization trait instead.

public function description(?string $lang = null): string
    {
        // this is only an example of implementation... translate method not exist
        // if $lang is null you have to use the current locale
        return return translate('status.'$this->name, $lang);
        
        // or translate each case
        return match ($this) {
            self::PENDING => translate('Await decision'),
            self::ACCEPTED => translate('Recognized valid'),
            self::DISCARDED => translate('No longer useful'),
            self::NO_RESPONSE => translate('No response'),
        };
        
        //or use EnumUniqueId trait
        return translate($this->uniqueId(), $lang);
    }

After the implementation of description method you can use it

$enum = Status::PENDING;
$enum->description(); // 'Await decision'
// forcing language
$enum->description('it'); // 🇮🇹 'In attesa'

Laravel

If you use Laravel framework and you need localization, you can use the EnumLaravelLocalization trait instead EnumDescription. This trait extends EnumDescription and implements the description() method using Laravel Localization features.

use Datomatic\EnumHelper\Traits\EnumLaravelLocalization;

enum StatusString
{
    use EnumLaravelLocalization;

    case PENDING = 'P';

Using this trait there is 2 way to manage translation strings.

Using Short Keys

Language strings may be stored in enums.php files within the lang directory. Within this directory, there may be subdirectories for each language supported by the application.

/lang
    /en
        enums.php
    /it
        enums.php

All language files return an array of keyed strings. The array has 2 levels: the first level has as a key the complete class name of the enum, and the second has as a key the name of the enum case.

// /lang/it/enums.php

return [
    Status::class => [
        'PENDING' => 'In attesa',
        'ACCEPTED' => 'Accettato',
        'DISCARDED' => 'Rifiutato',
        'NO_RESPONSE' => 'Nessuna Risposta',
    ],
     // or using the enum object name attribute
    StatusString::class => [
        StatusString::PENDING->name => 'In attesa',
        StatusString::ACCEPTED->name => 'Accettato',
    ...
Using Translation Strings As Keys

Language strings are stored as JSON files in the lang directory (e.g. lang/it.json).

{
    "enums.Namespace\\StatusString.PENDING": "In attesa",
    ...

But if you want to use this way, you can simply use the EnumDescription trait and translate each case on the description method.

public function description(?string $lang = null): string
{
    return match ($this) {
        self::PENDING => __('Await decision', $lang),
        self::ACCEPTED => __('Recognized valid', $lang),
        self::DISCARDED => __('No longer useful', $lang),
        self::NO_RESPONSE => __('No response', $lang),
    };

descriptions()

This method returns a list of case descriptions of enum.

StatusString::descriptions(); // ['Await decision','Recognized valid','No longer useful','No response']
// Subset
StatusString::descriptions([StatusString::ACCEPTED, StatusString::NO_RESPONSE]); // ['Recognized valid','No response']
// Forcing language
Status::descriptions(null, 'it'); // 🇮🇹 ['In attesa','Accettato','Rifiutato','Nessuna Risposta']
// Subset and language 
Status::descriptions([Status::NO_RESPONSE, Status::DISCARDED], 'it'); // 🇮🇹 ['Nessuna Risposta', 'Rifiutato']

descriptionsArray()

This method returns an associative array of [value => description] on BackedEnum, [name => description] on pure enum.

StatusString::descriptionsArray(); // ['P' => 'Await decision', 'A' => 'Recognized valid',...
Status::descriptionsArray(); // ['PENDING' => 'Await decision', 'ACCEPTED' => 'Recognized valid',...
// Subset
StatusString::descriptionsArray([StatusString::DISCARDED, StatusString::ACCEPTED]); // ['D' => 'No longer useful', 'A' => 'Recognized valid']
Status::descriptionsArray([[Status::PENDING, Status::DISCARDED]); // ['PENDING' => 'Await decision', 'DISCARDED' => 'No longer useful']
// Forcing language
StatusString::descriptionsArray(null, 'it'); // 🇮🇹 ['P' => 'In attesa','A' => 'Accettato',...
// Subset and language 
Status::descriptionsArray([Status::DISCARDED, Status::NO_RESPONSE], 'it'); // 🇮🇹 ['DISCARDED' => 'Rifiutato','NO_RESPONSE' => 'Nessuna Risposta',...
You might also like...
WordPress plugin which contains a collection of modules to apply theme-agnostic front-end modifications

Soil A WordPress plugin which contains a collection of modules to apply theme-agnostic front-end modifications. Soil is a commercial plugin available

Faker-driven, configuration-based, platform-agnostic, locale-compatible data faker tool
Faker-driven, configuration-based, platform-agnostic, locale-compatible data faker tool

Masquerade Faker-driven, platform-agnostic, locale-compatible data faker tool Point Masquerade to a database, give it a rule-set defined in YAML and M

A simple helper to generate and display pagination navigation links

Intro to CHocoCode Paginator Friendly PHP paginator to paginate everything This package introduces a different way of pagination handling. You can rea

AlphaID Helper - Basic, Simple and Lightweight

AlphaID Helper - Basic, Simple and Lightweight

Plivo PHP Helper Library

plivo-php The Plivo PHP SDK makes it simpler to integrate communications into your PHP applications using the Plivo REST API. Using the SDK, you will

A PHP 7 value objects helper library.

valueobjects Requirements Requires PHP = 7.1 Installation Through Composer, obviously: composer require funeralzone/valueobjects Extensions This lib

PHP Japanese string helper functions for converting Japanese strings from full-width to half-width and reverse. Laravel Rule for validation Japanese string only full-width or only half-width.

Japanese String Helpers PHP Japanese string helper functions for converting Japanese strings from full-width to half-width and reverse. Laravel Rule f

A lightweight mulit-process helper base on PHP.
A lightweight mulit-process helper base on PHP.

workbunny/process 🐇 A lightweight multi-process helper base on PHP. 🐇 简介 这是一个基于ext-pcntl和ext-posix拓展的PHP多进程助手,用于更方便的调用使用。 快速开始 composer require work

:passport_control: Helper for Google's new noCAPTCHA (reCAPTCHA v2 & v3)
:passport_control: Helper for Google's new noCAPTCHA (reCAPTCHA v2 & v3)

noCAPTCHA (new reCAPTCHA) By ARCANEDEV© What is reCAPTCHA? reCAPTCHA is a free service that protects your site from spam and abuse. It uses advanced r

Releases(v1.1.0)
  • v1.1.0(Oct 15, 2022)

  • v1.0.0(Sep 9, 2022)

    • v 1.0 🚀 🎉
    • Added Inspection methods (isPure(),isBacked(),has(), doesntHave(), hasName(), doesntHaveName(), hasValue(), doesntHaveValue())
    • Refactored readme examples and test classes name
    Source code(tar.gz)
    Source code(zip)
  • v0.7.2(Sep 8, 2022)

  • v0.7.1(Jul 5, 2022)

  • v0.7.0(Jul 2, 2022)

    After migrating several projects with my fellow @RobertoNegro and listening to different opinions we have decided to simplify the package.
    From now on, I will consider a pure enum as a StringBackedEnum with names as values, so all ***AsSelect() methods will be replaced by ***ByValue() methods.

    • removed all methods ***AsSelect()
    • added support on ***ByValue() methods also for pure enum using name instead value
    • removed NotBackedEnum exception
    Source code(tar.gz)
    Source code(zip)
  • v0.6.2(Jun 28, 2022)

  • v0.6.1(Jun 20, 2022)

  • v0.6.0(Jun 18, 2022)

  • v0.5.0(Jun 14, 2022)

  • v0.4.0(Jun 14, 2022)

    Renamed these methods:

    • namesArray() => namesByValue()
    • valuesArray() => valuesByName()

    descriptionsArray() splitted into 2 methods descriptionsByName(), descriptionsByValue()

    Renamed EnumLaravelLocalization trait toLaravelEnum

    Added new [method]AsSelect() methods that return [method]ByValue() if is BackedEnum, [method]ByName() otherwise.

    Added these exceptions:

    • NotBackedEnum
    • EmptyCases
    • UndefinedStaticMethod

    Added dynamic methods on LaravelEnum trait

    Source code(tar.gz)
    Source code(zip)
  • v0.3.1(May 30, 2022)

  • v0.3.0(May 30, 2022)

  • v0.2.0(May 27, 2022)

    Enum Helper (v0.2.0) A simple and opinionated collections of PHP 8.1 enum helpers

    • Invokable cases: get the value of enum "invoking" it statically
    • Construct enum by name or value: from(), tryFrom(), fromName(), tryFromName() for all enums
    • Enums Equality: is(), isNot(), in(), notIn() methods
    • Names: methods to have a list of case names (names(), namesArray())
    • Values: methods to have a list of case values (values(), valuesArray())
    • Unique ID: get an unique identifier from instance or instance from identifier (uniqueId(), fromUniqueId())
    • Descriptions: add description method and relative utilities to an enum (description(),descriptions(),descriptionsArray())
    • Translations: use enums on a multilanguage project (translate(),translations(),translationsArray())
    Source code(tar.gz)
    Source code(zip)
Owner
Datomatic
Datomatic
An opinionated extension package for Laravel Orchid to extend its table handling capabilities, and some further useful helper methods.

OrchidTables An opinionated extension package for Laravel Orchid to extend its table handling capabilities, and some further useful helper methods. In

null 25 Dec 22, 2022
Simplified and enhanced version of php built-in enum.

PHP Enum enhanced Finally, in php81 has been added support for Enums. But as enums are new in php, we do not have some helpers to work with that easil

Lazizbek Ergashev 40 Nov 20, 2022
This library provides a collection of native enum utilities (traits) which you almost always need in every PHP project.

This library provides a collection of native enum utilities (traits) which you almost always need in every PHP project.

DIVE 20 Nov 11, 2022
A framework agnostic PHP library to build chat bots

BotMan If you want to learn how to create reusable PHP packages yourself, take a look at my upcoming PHP Package Development video course. About BotMa

BotMan 5.8k Jan 1, 2023
A framework agnostic, multi-gateway payment processing library for PHP 5.6+

Omnipay An easy to use, consistent payment processing library for PHP Omnipay is a payment processing library for PHP. It has been designed based on i

The League of Extraordinary Packages 5.7k Dec 30, 2022
A lightweight framework-agnostic library in pure PHP for part-of-speech tagging

N-ai php pos tagger A lightweight framework-agnostic library in pure PHP for part-of-speech tagging. Can be used for chatbots, personal assistants, ke

Giorgio Rey 8 Nov 8, 2022
⚡ Php snippets, random stuff, demos, functions, fast message system, agnostic and framework free - 100% compactible ;) ⚡

⚡ Php8 FPM Nginx Fast, Scripts, Pearls & Treasures ?? Want to run and test asap ? docker-compose up -d phpgit_php8;ip=$(docker-machine ip default);ech

Benjamin FONTAINE 0 Mar 20, 2022
Laravel Pint is an opinionated PHP code style fixer for minimalists.

Laravel Pint is an opinionated PHP code style fixer for minimalists. Pint is built on top of PHP-CS-Fixer and makes it simple to ensure that your code style stays clean and consistent.

The Laravel Framework 2.2k Jan 5, 2023
Opinionated version of Wikimedia composer-merge-plugin to work in pair with Bamarni composer-bin-plugin.

Composer Inheritance Plugin Opinionated version of Wikimedia composer-merge-plugin to work in pair with bamarni/composer-bin-plugin. Usage If you are

Théo FIDRY 25 Dec 2, 2022
Contains a few tools usefull for making your test-expectations agnostic to operating system specifics

PHPUnit Tools to ease cross operating system Testing make assertEquals* comparisons end-of-line (aka PHP_EOL) character agnostic Make use of EolAgnost

Markus Staab 1 Jan 3, 2022