Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • jkuchar1/grifart-enum
1 result
Select Git revision
Show changes
Commits on Source (7)
# grifart/enum
repositories: [GRIFART GitLab](https://gitlab.grifart.cz/jkuchar1/grifart-enum), [GitHub](https://github.com/grifart/enum)
Enumeration value object. Enumerate values and behaviour with type-safety.
Repositories [gitlab.grifart.cz](https://gitlab.grifart.cz/jkuchar1/grifart-enum)
and [github.com](https://github.com/grifart/enum).
[![pipeline status](https://gitlab.grifart.cz/jkuchar1/grifart-enum/badges/master/pipeline.svg)](https://gitlab.grifart.cz/jkuchar1/grifart-enum/commits/master)
Sponsored by [grifart.com](https://grifart.com).
## Introduction
Enums represent predefined set of values. The available values are defined statically by each enum class. Each value is represented by an instance of this class in a flyweight manner.
- This enum allows you to add individal behaviour to every enum value (as in Java). This allows you to transform your `switch`es/`if`s into more readable composition. (see example bellow)
- Checks enum annotations if phpdoc-declared methods are properly declared (will generate docblock for you in exception)
- This enum allows you to add individual behaviour for every enum value (as in Java). This allows you to transform your `switch`es/`if`s into more readable composition. (see example bellow)
- Checks enum annotations if phpdoc-declared methods are properly declared (will generate docblock for you when not specified or incorrect)
- `===`, `==` and usage of `switch`es is supported
- string or integer scalar keys are supported
- Easily access scalar value of enum `DayOfWeek::MONDAY()->toScalar()` or `(string) DayOfWeek::MONDAY()`
Common with other enum implementations:
Also includes:
- You can type-hint: `function setCurrentDay(DayOfWeek $day) {`
- You can get a list of all the possible values
- It is type safe. By annotating your enumeration type, you are guaranteed that there will be no other values then you declared. `function translateTo(DayOfWeek $day) { ...`
- You can get a list of all the possible values `Enum::getAvailableValues()`
## Installation
......@@ -21,7 +32,64 @@ composer require grifart/enum
This library uses [**semantic versioning 2.0**](https://semver.org/spec/v2.0.0.html).
You can safely use `^` constrain in you `composer.json`.
## Let code speak: individual behaviour for each value
## Requirements
This library requires PHP 7.1 and later.
## Project status & release process
While this library is still under development, it is well tested and should be stable enough to use in production environments.
The current releases are numbered 0.x.y. When a non-breaking change is introduced (adding new methods, optimizing existing code, etc.), y is incremented.
When a breaking change is introduced, a new 0.x version cycle is always started.
It is therefore safe to lock your project to a given release cycle, such as 0.1.*.
If you need to upgrade to a newer release cycle, check the release history for a list of changes introduced by each further 0.x.0 version.
## Overview
### Static methods
- fromScalar() - returns enum instance (value) for given scalar
- getAvailableValues() - returns all values for given type
- provideInstances() - implement to return enum instances or automatically implemented by `Grifart\Enum\AutoInstances` trait.
### Instance methods
- toScalar() - return scalar value identifier
- equals() - returns true if the same enum value is passed
- scalarEquals() - returns true if passed scalar value is equal to current value
### Basic enumeration
```php
/**
* @method static DayOfWeek MONDAY()
* @method static DayOfWeek TUESDAY()
*/
final class DayOfWeek extends \Grifart\Enum\Enum
{
use Grifart\Enum\AutoInstances;
private const MONDAY = 'monday';
private const TUESDAY = 'tuesday';
// ...
public function process(self $value): void { /* ... */ }
}
$monday = DayOfWeek::MONDAY();
$monday->process(DayOfWeek::TUESDAY());
````
### Migrating from class constants
This guide show how to migrate from classes with constants to `\Grifart\Enum` in few simple steps. [Continue to example](tests/Example/MigratingLegacyCode/readme.md)
### Adding behaviour to values
Let's **refactor** following existing code:
......@@ -44,13 +112,15 @@ $monday = DayOfWeek::MONDAY();
function nextDay(DayOfWeek $dayOfWeek): DayOfWeek
{
if($dayOfWeek === DayOfWeek::MONDAY()) {
return DayOfWeek::TUESDAY();
} else if (...) {
...
}
throw new ShouldNotHappenException();
switch($dayOfWeek) {
case DayOfWeek::MONDAY():
return DayOfWeek::TUESDAY();
case DayOfWeek::TUESDAY():
// ...
}
throw new ShouldNotHappenException();
}
$tuesday = nextDay($monday);
......@@ -73,12 +143,15 @@ final class DayOfWeek extends \Grifart\Enum\Enum
public function nextDay(): self
{
if($this === self::MONDAY()) {
return self::TUESDAY();
} else if (...) {
...
switch($this) {
case self::MONDAY():
return self::TUESDAY();
case self::TUESDAY():
// ...
}
throw new ShouldNotHappenException();
}
......@@ -115,14 +188,14 @@ abstract class DayOfWeek extends \Grifart\Enum\Enum
protected const TUESDAY = 'tuesday';
// ...
public abstract function nextDay(): self;
abstract public function nextDay(): self;
/** @return static[] */
protected static function provideInstances(): array
{
return [
self::MONDAY => new class extends DayOfWeek
new class(self::MONDAY) extends DayOfWeek
{
public function nextDay(): DayOfWeek
{
......@@ -130,11 +203,11 @@ abstract class DayOfWeek extends \Grifart\Enum\Enum
}
},
self::TUESDAY => new class extends DayOfWeek
new class(self::TUESDAY) extends DayOfWeek
{
public function nextDay(): DayOfWeek
{
return return DayOfWeek::WEDNESDAY();
return DayOfWeek::WEDNESDAY();
}
},
];
......
# Migrating legacy code to `\Grifart\Enum`
This is step-by-step guide how to migrate you legacy code to `\Grifart\Enum`.
We will start with non-type safe enum represented by class with constants. [[full source code](step0.phpt)]
```php
class OrderState {
public const NEW = 'new';
public const PROCESSING = 'processing';
}
```
Our business logic is this:
```php
$result = '';
switch ($state) {
// your business logic
case OrderState::NEW:
$result = 'new';
break;
case OrderState::PROCESSING:
$result = 'processing';
break;
}
```
## Step 1: add new type-safe API [[source](step1.phpt)]
This is done by
- extending `\Grifart\Enum\Enum` class
- by automatically implementing enum values by including `use \Grifart\Enum\AutoInstances;` trait
- and by adding magic methods annotations
There is not backward incompatible change introduced. And now you can use new APIs!
```php
/**
* @method static OrderState NEW()
* @method static OrderState PROCESSING()
*/
class OrderState extends \Grifart\Enum\Enum {
use \Grifart\Enum\AutoInstances;
public const NEW = 'new';
public const PROCESSING = 'processing';
}
```
## Step 2: Migrating existing code to new API [[source](step2.phpt)]
Migrating old code to new API is usually easy, just add parenthesis `()` when you access value.
```php
$state = OrderState::NEW();
$result = '';
switch ($state) {
// your business logic
case OrderState::NEW():
$result = 'new';
break;
case OrderState::PROCESSING():
$result = 'processing';
break;
}
Assert::same('new', $result);
```
Please note, that you will need to handle some cases manually as `OrderState::NEW()` returns object, enum instance, not a string.
#### Removing old API
So when you are finally ready to remove old API, just change constant visibility to `private`.
```php
/**
* @method static OrderState NEW()
* @method static OrderState PROCESSING()
*/
class OrderState extends \Grifart\Enum\Enum {
use \Grifart\Enum\AutoInstances;
private const NEW = 'new';
private const PROCESSING = 'processing';
}
```
## Step 3: Enjoy new features [[source](step3.phpt)]
Now, when you decided that you what to move your business logic inside enum declaration. You are now free to do so. And there are many more options, see other examples.
<?php declare(strict_types=1);
namespace MigratingOldCode;
require __DIR__ . '/../../bootstrap.php';
use Tester\Assert;
class OrderState {
public const NEW = 'new';
public const PROCESSING = 'processing';
}
$state = OrderState::NEW;
$result = '';
switch ($state) {
// your business logic
case OrderState::NEW:
$result = 'new';
break;
case OrderState::PROCESSING:
$result = 'processing';
break;
}
Assert::same('new', $result);
<?php declare(strict_types=1);
namespace MigratingOldCode;
require __DIR__ . '/../../bootstrap.php';
use Tester\Assert;
/**
* @method static OrderState NEW()
* @method static OrderState PROCESSING()
*/
class OrderState extends \Grifart\Enum\Enum {
use \Grifart\Enum\AutoInstances;
public const NEW = 'new';
public const PROCESSING = 'processing';
}
// code bellow is untouched:
$state = OrderState::NEW;
$result = '';
switch ($state) {
// your business logic
case OrderState::NEW:
$result = 'new';
break;
case OrderState::PROCESSING:
$result = 'processing';
break;
}
Assert::same('new', $result);
<?php declare(strict_types=1);
namespace MigratingOldCode;
require __DIR__ . '/../../bootstrap.php';
use Tester\Assert;
/**
* @method static OrderState NEW()
* @method static OrderState PROCESSING()
*/
class OrderState extends \Grifart\Enum\Enum {
use \Grifart\Enum\AutoInstances;
private const NEW = 'new';
private const PROCESSING = 'processing';
}
$state = OrderState::NEW();
$result = '';
switch ($state) {
// your business logic
case OrderState::NEW():
$result = 'new';
break;
case OrderState::PROCESSING():
$result = 'processing';
break;
}
Assert::same('new', $result);
<?php declare(strict_types=1);
namespace MigratingOldCode;
require __DIR__ . '/../../bootstrap.php';
use Tester\Assert;
/**
* @method static OrderState NEW()
* @method static OrderState PROCESSING()
*/
class OrderState extends \Grifart\Enum\Enum {
use \Grifart\Enum\AutoInstances;
private const NEW = 'new';
private const PROCESSING = 'processing';
public function doBusinessLogic(): string
{
switch ($this) {
// your business logic
case self::NEW():
return 'new';
break;
case self::PROCESSING():
return 'processing';
break;
}
throw new \LogicException('should never happen');
}
}
$state = OrderState::NEW();
$result = $state->doBusinessLogic();
Assert::same('new', $result);