PHPackages                             dborsatto/smart-enums - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. dborsatto/smart-enums

AbandonedArchivedLibrary[Utility &amp; Helpers](/categories/utility)

dborsatto/smart-enums
=====================

4.1.0(2y ago)16.0k↓100%MITPHPPHP ^7.4|^8.0CI failing

Since Feb 4Pushed 11mo agoCompare

[ Source](https://github.com/dborsatto/smart-enums)[ Packagist](https://packagist.org/packages/dborsatto/smart-enums)[ RSS](/packages/dborsatto-smart-enums/feed)WikiDiscussions main Synced today

READMEChangelog (8)Dependencies (7)Versions (15)Used By (0)

Smart Enums - A dumb way to use enums in PHP
============================================

[](#smart-enums---a-dumb-way-to-use-enums-in-php)

[![Packagist](https://camo.githubusercontent.com/1b6dfeb32e8388e20e446a15bc91e187e2395ba13d5eb26bac5a71f5bd8beba4/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f64626f72736174746f2f736d6172742d656e756d732e737667)](https://packagist.org/packages/dborsatto/smart-enums)[![PHP version](https://camo.githubusercontent.com/7864dde70656f872c2c1c74207974dfa97450034c3fa307f19f4d5c0f968fc24/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f64626f72736174746f2f736d6172742d656e756d732e737667)](https://packagist.org/packages/dborsatto/smart-enums)[![Packagist](https://camo.githubusercontent.com/384955af73b042a487cd1066346ec327e004b53613e3e0c82d85816ad49143fa/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f64626f72736174746f2f736d6172742d656e756d732e7068702e737667)](https://packagist.org/packages/dborsatto/smart-enums)

Deprecation notice
------------------

[](#deprecation-notice)

As enums are now native in PHP, this library is deprecated and will not be maintained anymore. We recommend you to use the native enum feature in PHP 8.1.

---

`dborsatto/smart-enums` is a PHP library that allows you to use enums in PHP without having to wait for version 8.1 It provides integration with Doctrine, so you can use enum objects in your entities, and with Symfony forms.

Installation
------------

[](#installation)

As with any Composer package, run the CLI command to require the library in your application:

```
composer require dborsatto/smart-enums
```

Getting started
---------------

[](#getting-started)

This library is built around the need of having a defined set of values available, and some sort of description for each value. We find ourselves often in a situation where a property's value must be limited to a given set of options (and that's an enum), but those options are really internal representations of what the user in our applications will see as a more informative, descriptive message.

Let's use an example: an order's status can be either open, shipped, or delivered. The enum in this situation has clearly these three possible values. The problem is, when showing a message to the user with the current status, you need some sort of conversion from the string `'open'` to some text that can be used in a given context. Furthermore, if you really think about it, at the end of the day `'open'` is just an internal representation of an enum and you only care about it *being open* and not really about *using the `'open'` string to define the status*.

This library is built around the concept that every possibile value for an enum will have some sort of textual description, which can just be a symbol used in an internationalization process, and methods to verify internal status and apply transactions (and more).

At the core of this library, there are an interface and an abstract class which implements this interface. Your job is to extend the abstract class and implement the only method it requires you to create. Let's have an example using the order status from earlier.

```
class OrderStatus extends \DBorsatto\SmartEnums\AbstractEnum
{
    // First is a list of all possible values, defined as constants
    private const STATUS_OPEN = 'open';
    private const STATUS_SHIPPED = 'shipped';
    private const STATUS_DELIVERED = 'delivered';

    // In this example, the text representation is implemented in a way
    // that can be easily fed into an internationalization system
    // for easy translation, but if you don't need that you can use actual text
    private const STATUSES = [
        self::STATUS_OPEN => 'order_status.open',
        self::STATUS_SHIPPED => 'order_status.shipped',
        self::STATUS_DELIVERED => 'order_status.delivered',
    ];

    // This is the only method you will *need* to implement
    // You need to return an array with available options as keys,
    // and their text representation as values
    protected static function getValues(): array
    {
        return self::STATUSES;
    }

    // Even though you could have public constants and create enums using
    // OrderStatus::fromValue(OrderStatus::STATUS_OPEN), we think that's not the right way.
    // We see the constant as an internal representation of the possible value,
    // but the user does not need to be aware of this.
    // Also, from a purely formal point of value, `::fromValue()` can throw an exception
    // if the given value is not available, but calling the method using the constant
    // you are sure that the status is available, yet you still need to handle the exception.
    // By implementing named constructors, you can keep the visibility to private,
    // and there is no need to handle meaningless exceptions.
    public static function open(): self
    {
        return self::newInstance(self::STATUS_OPEN);
    }

    public static function shipped(): self
    {
        return self::newInstance(self::STATUS_SHIPPED);
    }

    public static function delivered(): self
    {
        return self::newInstance(self::STATUS_DELIVERED);
    }

    public function isDelivered(): bool
    {
        return $this->value === self::STATUS_DELIVERED;
    }

    public function canBeShipped(): bool
    {
        return $this->value === self::STATUS_OPEN;
    }

    public function canBeDelivered(): bool
    {
        return $this->value === self::STATUS_SHIPPED;
    }

    /**
     * @throws OrderStatusException
     */
    public function ship(): self
    {
        if (!$this->canBeShipped()) {
            // We recommend creating your own exceptions
            throw OrderStatusException::orderCannotBeShipped();
        }

        return self::shipped();
    }

    /**
     * @throws OrderStatusException
     */
    public function deliver(): self
    {
        if (!$this->canBeDelivered()) {
            throw OrderStatusException::orderCannotBeDelivered();
        }

        return self::delivered();
    }
}

// Elsewhere
$status = OrderStatus::open();
// Will return order_status.open, as defined in the STATUSES constant
echo $status->getDescription();
// ...
try {
    $shippedStatus = $status->ship();
} catch (OrderStatusException $exception) {
    // ...
}
```

This is quite a lot of boilerplate, especially considering the magic provided by other libraries such as `myclabs/php-enum`, where you don't need to write half as much code. But this is intentional, because we don't like magic and would rather have things that are a bit longer but are also explicit. That's why this library is called *smart enums*: it's smart because everything is designed to be dumb and make as few assumptions as possible.

You can add as many methods as you need. The great thing about this is that your enums will be fully self-aware and contain the logic they need. In this example the statuses are just three and their relationship is clear, but we have situations with a dozen possible options and transitions are complex. You can code anything in the enum, and the logic will be fully encapsulated.

### Use within entities

[](#use-within-entities)

The main benefit of using an enum that contains logic is when it becomes part of your entities:

```
class Order
{
    // ...

    /**
     * @var OrderStatus
     */
    private $status;

    // ...

    public function __construct()
    {
        $this->status = OrderStatus::open();
    }

    // ...

    /**
     * @throws OrderStatusException
     */
    public function ship(): void
    {
        $this->status = $this->status->ship();
    }

    /**
     * @throws OrderStatusException
     */
    public function delivered(DateTimeImmutable $deliveryDate): void
    {
        $this->status = $this->status->deliver();
        $this->deliveryDate = $deliveryDate;
    }
}
```

In this example, all logic regarding order status will be encapsulated in the enum, and the entity will be able to access it and behave accordingly.

In order to ease this integration within entities, we created a bridge with Doctrine that allows you to easily add custom types so that instead of a string, an enum object will be created.

There are two steps required for you to use enums with Doctrine: create a custom type, and tell Doctrine about it. The first step is where this library helps you:

```
class OrderStatusType extends \DBorsatto\SmartEnums\Bridge\Doctrine\Type\AbstractEnumType
{
    public const NAME = 'order_status_type';

    protected function getEnumClass(): string
    {
       return OrderStatus::class;
    }

    public function getName(): string
    {
        return self::NAME;
    }
}
```

By extending `AbstractEnumType`, all conversion processes will be handled for you. If in your configuration you declared the type as nullable, `null` values will be properly handled for you.

The second step is to let Doctrine know about your custom type. We use Symfony so we add the proper configuration to the `doctrine.dbal.types` section (see the [reference configuration](https://symfony.com/doc/current/reference/configuration/doctrine.html) for more details). If you are using vanilla Doctrine, you must call `Doctrine\DBAL\Types\Type::addType()` as explained in the [official docs](https://www.doctrine-project.org/projects/doctrine-orm/en/2.8/cookbook/custom-mapping-types.html).

After having set up the type, you can configure you entity to use it. If you are using annotations, your code will look like this:

```
class Order
{
    // ...

    /**
     * @var OrderStatus
     *
     * @ORM\Column(type="enum_order_status")
     */
    private $status;
}
```

### Integration with Symfony forms

[](#integration-with-symfony-forms)

As we already mentioned, we use Symfony. This meant that we had to find a way to make enums work with forms, and for this reason we also included a form type that is ready to use:

```
use DBorsatto\SmartEnums\Bridge\Symfony\Form\Type\EnumType;

class OrderType extends \Symfony\Component\Form\AbstractType
{
    public function buildForm(FormBuilderInterface $builder,array $options)
    {
        // This example is not the best because ideally you would transition
        // an order status manually by calling an entity method,
        // but sometimes you just have to let users pick an option
        $builder->add('orderStatus', EnumType::class, [
            'enum_class' => OrderStatus::class,
            'label' => 'Status',
        ]);
    }
}
```

This will give you a `ChoiceType` input will all available options. If you need to restrict the selection of possible choices, you can pass the `choices` value to the configuration array with a list of available objects for the user to choose.

### Integration with Symfony validator

[](#integration-with-symfony-validator)

This library ships with a constraint you can use with Symfony's validator. It requires the `enumClass` parameter, and optionally an error `message`.

```
/** @var \Symfony\Component\Validator\Validator\ValidatorInterface $validator */
$violations = $validator->validate($value, [
    new \DBorsatto\SmartEnums\Bridge\Symfony\Validator\EnumConstraint(['enumClass' => Enum::class]),
]);
```

The constraint works just like any other Symfony constraint, which means you can also use it as an annotation.

### Utilities

[](#utilities)

This library ships with a couple of utility classes that you probably will not need in everyday use, but are still available for you.

```
// EnumFactory acts as a wrapper for when you only have the enum class available,
// but you need guarantees about it being a valid enum
$factory = new \DBorsatto\SmartEnums\EnumFactory(OrderStatus::class);
// At this point, all methods just forward to the actual enum methods
$factory->fromValue('...');
$factory->fromValues([...]);
$factory->all();

// Sometimes you just need to get the enum value and description as an key => value array
// Because this is usually a formatting problem, instead of breaking encapsulation
// and making the enum constant public, use this formatter
$formatter = new \DBorsatto\SmartEnums\EnumFormatter(OrderStatus::class);

// These methods both return array values
$formatter->toValueDescriptionList();
$formatter->toDescriptionValueList();
```

### An important note about enum identity

[](#an-important-note-about-enum-identity)

Because two enums with the same value are conceptually the same, we built `AbstractEnum` to make sure that instances are reused. This means that `OrderStatus::open() === OrderStatus::open()` will evaluate to true.

For this to work, you need to remember two things:

- For technical reasons, this can't be enforced at an interface level. This is why we recommend you always extend `AbstractEnum` and never implement `EnumInterface` directly.
- Inside an enum, you must **never** modify `$this->value`. State transitions must always return a new enum and they must never update the current enum. Unfortunately PHP before version 8.1 does not support read only properties and as we said, we don't like magic solutions that would let us work around this, so we trust users to be smart and not mess this up.

License
-------

[](#license)

This repository is published under the [MIT](LICENSE) license.

###  Health Score

36

—

LowBetter than 82% of packages

Maintenance38

Infrequent updates — may be unmaintained

Popularity20

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity65

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~72 days

Recently: every ~105 days

Total

14

Last Release

933d ago

Major Versions

1.1.0 → 2.0.02021-11-25

2.2.2 → 3.0.02022-07-09

3.2.0 → 4.0.02023-06-28

PHP version history (2 changes)1.0.0-beta1PHP ^7.3|^8.0

2.0.0PHP ^7.4|^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/2e1c61de690e3a51a67237e831ce74274e1f38181d2f9bef575c49579e03c5c2?d=identicon)[dborsatto](/maintainers/dborsatto)

---

Top Contributors

[![dborsatto](https://avatars.githubusercontent.com/u/94651?v=4)](https://github.com/dborsatto "dborsatto (19 commits)")

---

Tags

phpphp-library

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/dborsatto-smart-enums/health.svg)

```
[![Health](https://phpackages.com/badges/dborsatto-smart-enums/health.svg)](https://phpackages.com/packages/dborsatto-smart-enums)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
