diff --git a/.gitattributes b/.gitattributes index c821984..2140930 100755 --- a/.gitattributes +++ b/.gitattributes @@ -4,7 +4,7 @@ .gitattributes export-ignore .github export-ignore .gitignore export-ignore -.travis.yml export-ignore tests/ export-ignore phpunit.xml export-ignore +psalm.xml export-ignore static-analysis/ export-ignore diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..7863acf --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,46 @@ +name: "CI" + +on: + pull_request: + push: + branches: + - "master" + +jobs: + phpunit: + name: "PHPUnit" + runs-on: "ubuntu-20.04" + + strategy: + matrix: + php-version: + - "7.3" + - "7.4" + - "8.0" + - "8.1" + - "8.2" + dependencies: + - "highest" + include: + - dependencies: "lowest" + php-version: "7.3" + + steps: + - name: "Checkout" + uses: "actions/checkout@v4" + with: + fetch-depth: 2 + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php-version }}" + ini-values: "zend.assertions=1" + + - name: "Install dependencies with Composer" + uses: "ramsey/composer-install@v1" + with: + dependency-versions: "${{ matrix.dependencies }}" + + - name: "Run PHPUnit" + run: "vendor/bin/phpunit" diff --git a/.github/workflows/static-analysis.yaml b/.github/workflows/static-analysis.yaml new file mode 100644 index 0000000..017d4a6 --- /dev/null +++ b/.github/workflows/static-analysis.yaml @@ -0,0 +1,31 @@ +name: "Static Analysis" + +on: + pull_request: + push: + branches: + - "master" + +jobs: + static-analysis-psalm: + name: "Static Analysis with Psalm" + runs-on: "ubuntu-20.04" + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Psalm + uses: docker://vimeo/psalm-github-actions:4.9.3 + with: + args: --shepherd + composer_ignore_platform_reqs: true + composer_require_dev: true + security_analysis: true + report_file: results.sarif + env: + CHECK_PLATFORM_REQUIREMENTS: "false" + - name: Upload Security Analysis results to GitHub + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: results.sarif diff --git a/.travis.yml b/.travis.yml deleted file mode 100755 index 92affbf..0000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: php - -php: - - '7.3' - - '7.4' - - '8.0' - -matrix: - fast_finish: true - -cache: - directories: - - $HOME/.composer/cache - -before_script: - - travis_retry composer install -n - -script: - - vendor/bin/phpunit - - vendor/bin/psalm --shepherd - -# Use Travis' new container-based infrastructure. -# See http://docs.travis-ci.com/user/migrating-from-legacy/#How-can-I-use-container-based-infrastructure%3F -sudo: false diff --git a/README.md b/README.md index 1e4d1ff..2bf98cd 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # PHP Enum implementation inspired from SplEnum -[![Build Status](https://travis-ci.org/myclabs/php-enum.png?branch=master)](https://travis-ci.org/myclabs/php-enum) -[![Latest Stable Version](https://poser.pugx.org/myclabs/php-enum/version.png)](https://packagist.org/packages/myclabs/php-enum) -[![Total Downloads](https://poser.pugx.org/myclabs/php-enum/downloads.png)](https://packagist.org/packages/myclabs/php-enum) -[![psalm](https://shepherd.dev/github/myclabs/php-enum/coverage.svg)](https://shepherd.dev/github/myclabs/php-enum) +[![GitHub Actions][GA Image]][GA Link] +[![Latest Stable Version](https://poser.pugx.org/myclabs/php-enum/version.svg)](https://packagist.org/packages/myclabs/php-enum) +[![Total Downloads](https://poser.pugx.org/myclabs/php-enum/downloads.svg)](https://packagist.org/packages/myclabs/php-enum) +[![Psalm Shepherd][Shepherd Image]][Shepherd Link] Maintenance for this project is [supported via Tidelift](https://tidelift.com/subscription/pkg/packagist-myclabs-php-enum?utm_source=packagist-myclabs-php-enum&utm_medium=referral&utm_campaign=readme). @@ -34,6 +34,8 @@ use MyCLabs\Enum\Enum; /** * Action enum + * + * @extends Enum */ final class Action extends Enum { @@ -130,9 +132,65 @@ final class Action extends Enum } ``` +## Native enums and migration +Native enum arrived to PHP in version 8.1: https://www.php.net/enumerations +If your project is running PHP 8.1+ or your library has it as a minimum requirement you should use it instead of this library. + +When migrating from `myclabs/php-enum`, the effort should be small if the usage was in the recommended way: +- private constants +- final classes +- no method overridden + +Changes for migration: +- Class definition should be changed from +```php +/** + * @method static Action VIEW() + * @method static Action EDIT() + */ +final class Action extends Enum +{ + private const VIEW = 'view'; + private const EDIT = 'edit'; +} +``` + to +```php +enum Action: string +{ + case VIEW = 'view'; + case EDIT = 'edit'; +} +``` +All places where the class was used as a type will continue to work. + +Usages and the change needed: + +| Operation | myclabs/php-enum | native enum | +|----------------------------------------------------------------|----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Obtain an instance will change from | `$enumCase = Action::VIEW()` | `$enumCase = Action::VIEW` | +| Create an enum from a backed value | `$enumCase = new Action('view')` | `$enumCase = Action::from('view')` | +| Get the backed value of the enum instance | `$enumCase->getValue()` | `$enumCase->value` | +| Compare two enum instances | `$enumCase1 == $enumCase2`
or
`$enumCase1->equals($enumCase2)` | `$enumCase1 === $enumCase2` | +| Get the key/name of the enum instance | `$enumCase->getKey()` | `$enumCase->name` | +| Get a list of all the possible instances of the enum | `Action::values()` | `Action::cases()` | +| Get a map of possible instances of the enum mapped by name | `Action::values()` | `array_combine(array_map(fn($case) => $case->name, Action::cases()), Action::cases())`
or
`(new ReflectionEnum(Action::class))->getConstants()` | +| Get a list of all possible names of the enum | `Action::keys()` | `array_map(fn($case) => $case->name, Action::cases())` | +| Get a list of all possible backed values of the enum | `Action::toArray()` | `array_map(fn($case) => $case->value, Action::cases())` | +| Get a map of possible backed values of the enum mapped by name | `Action::toArray()` | `array_combine(array_map(fn($case) => $case->name, Action::cases()), array_map(fn($case) => $case->value, Action::cases()))`
or
`array_map(fn($case) => $case->value, (new ReflectionEnum(Action::class))->getConstants()))` | + ## Related projects +- [PHP 8.1+ native enum](https://www.php.net/enumerations) - [Doctrine enum mapping](https://github.com/acelaya/doctrine-enum-type) - [Symfony ParamConverter integration](https://github.com/Ex3v/MyCLabsEnumParamConverter) - [PHPStan integration](https://github.com/timeweb/phpstan-enum) -- [Yii2 enum mapping](https://github.com/KartaviK/yii2-enum) + + +[GA Image]: https://github.com/myclabs/php-enum/workflows/CI/badge.svg + +[GA Link]: https://github.com/myclabs/php-enum/actions?query=workflow%3A%22CI%22+branch%3Amaster + +[Shepherd Image]: https://shepherd.dev/github/myclabs/php-enum/coverage.svg + +[Shepherd Link]: https://shepherd.dev/github/myclabs/php-enum diff --git a/composer.json b/composer.json index 924f924..eab6263 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "type": "library", "description": "PHP Enum implementation", "keywords": ["enum"], - "homepage": "http://github.com/myclabs/php-enum", + "homepage": "https://github.com/myclabs/php-enum", "license": "MIT", "authors": [ { @@ -14,7 +14,10 @@ "autoload": { "psr-4": { "MyCLabs\\Enum\\": "src/" - } + }, + "classmap": [ + "stubs/Stringable.php" + ] }, "autoload-dev": { "psr-4": { @@ -28,6 +31,6 @@ "require-dev": { "phpunit/phpunit": "^9.5", "squizlabs/php_codesniffer": "1.*", - "vimeo/psalm": "^4.6.2" + "vimeo/psalm": "^4.6.2 || ^5.2" } } diff --git a/phpunit.xml b/phpunit.xml index bd714f1..33b8f67 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -10,4 +10,10 @@ ./tests + + + + src + + diff --git a/src/Enum.php b/src/Enum.php index 6967ab5..1bd5592 100644 --- a/src/Enum.php +++ b/src/Enum.php @@ -19,7 +19,7 @@ * @psalm-immutable * @psalm-consistent-constructor */ -abstract class Enum implements \JsonSerializable +abstract class Enum implements \JsonSerializable, \Stringable { /** * Enum value @@ -95,7 +95,6 @@ public function __wakeup() /** * @param mixed $value * @return static - * @psalm-return static */ public static function from($value): self { @@ -177,6 +176,7 @@ public static function values() /** @psalm-var T $value */ foreach (static::toArray() as $key => $value) { + /** @psalm-suppress UnsafeGenericInstantiation */ $values[$key] = new static($value); } @@ -298,6 +298,7 @@ public static function __callStatic($name, $arguments) $message = "No static method or enum constant '$name' in class " . static::class; throw new \BadMethodCallException($message); } + /** @psalm-suppress UnsafeGenericInstantiation */ return self::$instances[$class][$name] = new static($array[$name]); } return clone self::$instances[$class][$name]; @@ -309,8 +310,8 @@ public static function __callStatic($name, $arguments) * * @return mixed * @link http://php.net/manual/en/jsonserializable.jsonserialize.php - * @psalm-pure */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->getValue(); diff --git a/src/PHPUnit/Comparator.php b/src/PHPUnit/Comparator.php index 302bf80..7c65e4e 100644 --- a/src/PHPUnit/Comparator.php +++ b/src/PHPUnit/Comparator.php @@ -43,7 +43,7 @@ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = f ); } - private function formatEnum(Enum $enum = null) + private function formatEnum(?Enum $enum = null) { if ($enum === null) { return "null"; diff --git a/stubs/Stringable.php b/stubs/Stringable.php new file mode 100644 index 0000000..4811af7 --- /dev/null +++ b/stubs/Stringable.php @@ -0,0 +1,11 @@ +