diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..95457d6 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +name: Continuous Integration + +on: + pull_request: + push: + branches: + - "master" + +jobs: + tests: + name: "Tests" + runs-on: ubuntu-latest + + strategy: + matrix: + include: + - php-version: 7.1 + symfony-version: 4.4.* + - php-version: 7.4 + symfony-version: 4.4.* + - php-version: 7.2 + symfony-version: 5.1.* + - php-version: 7.4 + symfony-version: 5.1.* + + steps: + - name: "Checkout" + uses: actions/checkout@v2.0.0 + + - name: "Install dependencies with composer" + run: | + composer require --no-update --dev symfony/symfony:${{ matrix.symfony-version }} + composer update --no-interaction --no-progress --no-suggest + + - name: "Run checkstyle with squizlabs/php_codesniffer" + run: vendor/bin/phpcs + + - name: "Run tests with phpunit/phpunit" + run: vendor/bin/phpunit --coverage-text diff --git a/.gitignore b/.gitignore index 6b44be9..09a9d69 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ -/phpunit.xml -composer.lock +/.idea/ /vendor/ -/build/ -.idea/ -docker-compose.yml +/.phpcs-cache +/composer.lock +/docker-compose.yml +/phpunit.xml diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index 39dd4c9..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,19 +0,0 @@ -filter: - excluded_paths: [vendor/*, Tests/*] - -before_commands: - - 'composer install --dev --prefer-source' - -tools: - external_code_coverage: true - php_mess_detector: true - php_code_sniffer: true - sensiolabs_security_checker: true - php_code_coverage: true - php_pdepend: true - php_loc: - enabled: true - excluded_dirs: [vendor, Tests] - php_cpd: - enabled: true - excluded_dirs: [vendor, Tests] diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index be44bc1..0000000 --- a/.travis.yml +++ /dev/null @@ -1,42 +0,0 @@ -language: php - -php: - - 5.6 - - 7.0 - - 7.1 - - 7.2 - -env: - - SYMFONY_VERSION="2.7.*" - - SYMFONY_VERSION="2.8.*" - - SYMFONY_VERSION="3.4.*" - - SYMFONY_VERSION="4.0.*" - -matrix: - exclude: - - php: 5.6 - env: SYMFONY_VERSION="4.0.*" # Symfony >= 4.0 PHP requirement is ^7.1.3 - - php: 7.0 - env: SYMFONY_VERSION="4.0.*" # Symfony >= 4.0 PHP requirement is ^7.1.3 - -sudo: false - -cache: - directories: - - $HOME/.composer/cache - -before_install: - - composer selfupdate - - if [ "$SYMFONY_VERSION" != "" ]; then composer require "symfony/symfony:${SYMFONY_VERSION}" --no-update; fi; - -install: composer update --prefer-dist --no-interaction $COMPOSER_FLAGS - -script: - - vendor/bin/phpunit -c phpunit.xml.dist --coverage-clover=coverage.clover - -after_script: - - wget https://scrutinizer-ci.com/ocular.phar - - php ocular.phar code-coverage:upload --format=php-clover coverage.clover - -notifications: - email: eugone.yann@gmail.com diff --git a/DependencyInjection/CompilerPass/ConventionedEnumCollectorCompilerPass.php b/DependencyInjection/CompilerPass/ConventionedEnumCollectorCompilerPass.php deleted file mode 100644 index ca6df01..0000000 --- a/DependencyInjection/CompilerPass/ConventionedEnumCollectorCompilerPass.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * @deprecated - */ -class ConventionedEnumCollectorCompilerPass implements CompilerPassInterface -{ - /** - * @inheritdoc - */ - public function process(ContainerBuilder $container) - { - $bundles = $container->getParameter('enum.register_bundles'); - - if (!$bundles) { - return; - } - - @trigger_error( - '"' . __CLASS__ . '" is deprecated since v2.2. Please use Symfony\'s PSR4 Service discovery instead.', - E_USER_DEPRECATED - ); - - if (true === $bundles) { - $bundles = $container->getParameter('kernel.bundles'); - } else { - $bundles = (array) $bundles; - } - - foreach ($bundles as $bundleClass) { - $declarativePass = new DeclarativeEnumCollectorCompilerPass($bundleClass); - $declarativePass->process($container); - } - } -} diff --git a/DependencyInjection/CompilerPass/DeclarativeEnumCollectorCompilerPass.php b/DependencyInjection/CompilerPass/DeclarativeEnumCollectorCompilerPass.php deleted file mode 100644 index 56f4a07..0000000 --- a/DependencyInjection/CompilerPass/DeclarativeEnumCollectorCompilerPass.php +++ /dev/null @@ -1,211 +0,0 @@ - - * - * @deprecated - */ -class DeclarativeEnumCollectorCompilerPass implements CompilerPassInterface -{ - /** - * @var string - */ - private $bundleDir; - - /** - * @var string - */ - private $bundleNamespace; - - /** - * @var string - */ - private $bundleName; - - /** - * @var string - */ - private $transDomain; - - /** - * @param string $bundle - * @param string|null $transDomain - */ - public function __construct($bundle, $transDomain = null) - { - $reflection = new ReflectionClass($bundle); - $this->bundleDir = dirname($reflection->getFileName()); - $this->bundleNamespace = $reflection->getNamespaceName(); - $this->bundleName = $reflection->getShortName(); - - $this->transDomain = $transDomain; - } - - /** - * @inheritdoc - */ - public function process(ContainerBuilder $container) - { - @trigger_error( - '"' . __CLASS__ . '" is deprecated since v2.2. Please use Symfony\'s PSR4 Service discovery instead.', - E_USER_DEPRECATED - ); - - if (!class_exists('Symfony\Component\Finder\Finder')) { - throw new RuntimeException('You need the symfony/finder component to register enums.'); - } - - $enumDir = $this->bundleDir . '/Enum'; - - if (!is_dir($enumDir)) { - return; - } - - $finder = new Finder(); - $finder->files()->name('*Enum.php')->in($enumDir); - - foreach ($finder as $file) { - /** @var SplFileInfo $file */ - $enumNamespace = $this->bundleNamespace . '\\Enum'; - if ($relativePath = $file->getRelativePath()) { - $enumNamespace .= '\\' . strtr($relativePath, '/', '\\'); - } - - $enumClass = $enumNamespace . '\\' . $file->getBasename('.php'); - $enumReflection = new ReflectionClass($enumClass); - - if (!$enumReflection->isSubclassOf(EnumInterface::class) || $enumReflection->isAbstract()) { - continue; //Not an enum or abstract enum - } - - $definition = null; - $requiredParameters = 0; - if ($enumReflection->getConstructor()) { - $requiredParameters = $enumReflection->getConstructor()->getNumberOfRequiredParameters(); - } - - if ($requiredParameters === 0) { - $definition = new Definition($enumClass); - } elseif ($requiredParameters === 2 && $enumReflection->isSubclassOf(AbstractTranslatedEnum::class)) { - if (class_exists('Symfony\Component\DependencyInjection\ChildDefinition')) { - // ChildDefinition was introduced as Symfony 3.3 - $definition = new ChildDefinition('enum.abstract_translated'); - } else { - // DefinitionDecorator was deprecated as Symfony 3.3 - $definition = new DefinitionDecorator('enum.abstract_translated'); - } - $definition->setClass($enumClass); - $definition->addArgument( - $this->getTransPattern($enumClass) - ); - - if ($this->transDomain) { - $definition->addMethodCall( - 'setTransDomain', - [ - $this->transDomain - ] - ); - } - } - - if (!$definition) { - continue; //Could not determine how to create definition for the enum - } - - $definition->addTag('enum'); - - $container->setDefinition( - $this->getServiceId($enumClass), - $definition - ); - } - } - - /** - * @param string $enumClass - * - * @return string - */ - private function getServiceId($enumClass) - { - $enumNamespace = $this->bundleNamespace.'\\Enum\\'; - - return sprintf('%s.enum.%s', - Container::underscore( - substr($this->bundleName, 0, -6) - ), - Container::underscore( - str_replace( - '\\', - '', - str_replace( - $enumNamespace, - '', - substr($enumClass, 0, -4) - ) - ) - ) - ); - } - - /** - * @param string $enumClass - * - * @return string - */ - private function getTransPattern($enumClass) - { - $parts = array_filter( - array_map( - [$this, 'underscore'], - explode( - '\\', - str_replace( - $this->bundleNamespace . '\\', - '', - $enumClass - ) - ) - ) - ); - - $enum = array_pop($parts); - - return implode('_', $parts) . '.' . $enum . '.label_%s'; - } - - /** - * @param string $input - * - * @return string - */ - private function underscore($input) - { - return strtolower( - preg_replace( - '~(?<=\\w)([A-Z])~', '_$1', - preg_replace( - '~(Enum|Bundle)~', - '', - $input - ) - ) - ); - } -} diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php deleted file mode 100644 index 6ceb101..0000000 --- a/DependencyInjection/Configuration.php +++ /dev/null @@ -1,40 +0,0 @@ - - */ -class Configuration implements ConfigurationInterface -{ - /** - * @inheritdoc - */ - public function getConfigTreeBuilder() - { - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('yokai_enum'); - - $rootNode - ->children() - ->variableNode('register_bundles') - ->info('[DEPRECATED] bundles for which to auto-register enums.') - ->defaultFalse() - ->end() - ->booleanNode('enum_autoconfiguration') - ->info('If set to true, all services that implements EnumInterface, will obtain the "enum" tag automatically.') - ->defaultTrue() - ->end() - ->booleanNode('enum_registry_autoconfigurable') - ->info('If set to true, add an alias for the enum registry so your service can ask for it via autoconfiguration.') - ->defaultTrue() - ->end() - ->end() - ; - - return $treeBuilder; - } -} diff --git a/DependencyInjection/EnumExtension.php b/DependencyInjection/EnumExtension.php deleted file mode 100644 index 889aa39..0000000 --- a/DependencyInjection/EnumExtension.php +++ /dev/null @@ -1,42 +0,0 @@ - - */ -class EnumExtension extends Extension -{ - /** - * @inheritdoc - */ - public function load(array $configs, ContainerBuilder $container) - { - $configuration = new Configuration(); - $config = $this->processConfiguration($configuration, $configs); - - $container->setParameter('enum.register_bundles', $config['register_bundles']); - - $xmlLoader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); - $xmlLoader->load('services.xml'); - $xmlLoader->load('forms.xml'); - $xmlLoader->load('validators.xml'); - $xmlLoader->load('twig.xml'); - - if ($config['enum_autoconfiguration'] && method_exists($container, 'registerForAutoconfiguration')) { - $container->registerForAutoconfiguration(EnumInterface::class) - ->addTag('enum'); - } - - if ($config['enum_registry_autoconfigurable']) { - $container->setAlias(EnumRegistryInterface::class, 'enum.registry'); - } - } -} diff --git a/README.md b/README.md index d11ed37..746cb77 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,16 @@ YokaiEnumBundle [![Code Coverage](https://scrutinizer-ci.com/g/yokai-php/enum-bundle/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/yokai-php/enum-bundle/?branch=master) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/596d2076-90ee-49d9-a8b2-e3bcbd390874/mini.png)](https://insight.sensiolabs.com/projects/596d2076-90ee-49d9-a8b2-e3bcbd390874) -This repository aims to provide simple enumeration implementation to Symfony : +This library aims to provide simple enumeration implementation for PHP projects. + +First created as a Symfony bundle only, the doc is focused on integration with it. +But if you love this library, feel free to propose a bridge for you framework. Installation ------------ -### Add the bundle as dependency with Composer +### Add the library as a dependency with Composer ``` bash $ composer require yokai/enum-bundle @@ -48,17 +51,17 @@ We first need to create the classes that will handle our enums : ```php 'Male', 'f' => 'Female']; } @@ -74,9 +77,10 @@ Adding validation to your model : ```php = 2.8 use Yokai\EnumBundle\Form\Type\EnumType; class MemberType extends AbstractType @@ -111,11 +114,8 @@ class MemberType extends AbstractType // Let the bundle guess the form type for you (requires that you configured the validation) ->add('gender') - // Manual form type binding for Symfony >= 2.8 + // Manual form type binding ->add('gender', EnumType::class, ['enum' => GenderEnum::class]) - - // Manual form type binding for Symfony 2.7 - ->add('gender', 'enum', ['enum' => GenderEnum::class]) ; } } @@ -131,8 +131,8 @@ Displaying the label for an enum value within a template : Recipes ------- -- Usage in [SonataAdminBundle](https://github.com/sonata-project/SonataAdminBundle) : see [doc](Resources/doc/sonata-admin.md) -- All the ways to declare [enums](Resources/doc/declaring-enum.md) or [translated enums](Resources/doc/declaring-translated-enum.md) +- Usage in [SonataAdminBundle](https://github.com/sonata-project/SonataAdminBundle) : see [doc](doc/sonata-admin.md) +- All the ways to declare [enums](doc/declaring-enum.md) or [translated enums](doc/declaring-translated-enum.md) MIT License diff --git a/Registry/EnumRegistryInterface.php b/Registry/EnumRegistryInterface.php deleted file mode 100644 index 270621f..0000000 --- a/Registry/EnumRegistryInterface.php +++ /dev/null @@ -1,40 +0,0 @@ - - */ -interface EnumRegistryInterface -{ - /** - * @param EnumInterface $enum - * - * @throws DuplicatedEnumException - */ - public function add(EnumInterface $enum); - - /** - * @param string $name - * - * @return EnumInterface - * @throws InvalidEnumException - */ - public function get($name); - - /** - * @param string $name - * - * @return bool - */ - public function has($name); - - /** - * @return EnumInterface[] - */ - public function all(); -} diff --git a/Resources/config/services.xml b/Resources/config/services.xml deleted file mode 100644 index 76f17cc..0000000 --- a/Resources/config/services.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/Tests/DependencyInjection/CompilerPass/ConventionedEnumCollectorCompilerPassTest.php b/Tests/DependencyInjection/CompilerPass/ConventionedEnumCollectorCompilerPassTest.php deleted file mode 100644 index 901566c..0000000 --- a/Tests/DependencyInjection/CompilerPass/ConventionedEnumCollectorCompilerPassTest.php +++ /dev/null @@ -1,89 +0,0 @@ - - */ -class ConventionedEnumCollectorCompilerPassTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getBundles - */ - public function testCollectThroughBundles($bundle, $prefix) - { - $container = $this->prophesize('Symfony\Component\DependencyInjection\ContainerBuilder'); - - $namespace = (new \ReflectionClass($bundle))->getNamespaceName(); - - $container->getParameter('enum.register_bundles') - ->shouldBeCalled() - ->willReturn([$bundle]); - - $container - ->setDefinition( - $prefix.'.enum.dummy', - Argument::allOf( - Argument::type('Symfony\Component\DependencyInjection\Definition'), - Argument::which('getTags', ['enum' => [[]]]), - Argument::which('getClass', $namespace.'\Enum\DummyEnum') - ) - ) - ->shouldBeCalled(); - $container - ->setDefinition( - $prefix.'.enum.customer_gender', - Argument::allOf( - Argument::type('Symfony\Component\DependencyInjection\Definition'), - Argument::which('getTags', ['enum' => [[]]]), - Argument::which('getClass', $namespace.'\Enum\Customer\GenderEnum') - ) - ) - ->shouldBeCalled(); - $class = 'Symfony\Component\DependencyInjection\DefinitionDecorator'; - if (class_exists('Symfony\Component\DependencyInjection\ChildDefinition')) { - $class = 'Symfony\Component\DependencyInjection\ChildDefinition'; - } - $container - ->setDefinition( - $prefix.'.enum.customer_state', - Argument::allOf( - Argument::type($class), - Argument::which('getTags', ['enum' => [[]]]), - Argument::which('getClass', $namespace.'\Enum\Customer\StateEnum'), - Argument::which( - 'getArguments', - [ - 'customer.state.label_%s' - ] - ), - Argument::which('getParent', 'enum.abstract_translated') - ) - ) - ->shouldBeCalled(); - - $compiler = new ConventionedEnumCollectorCompilerPass(); - $compiler->process($container->reveal()); - } - - public function getBundles() - { - return [ - ['AppBundle\AppBundle', 'app'], - ['Acme\AppBundle\AcmeAppBundle', 'acme_app'], - ['Acme\Bundle\AppBundle\AcmeAppBundle', 'acme_app'], - ]; - } -} diff --git a/Tests/DependencyInjection/EnumExtensionTest.php b/Tests/DependencyInjection/EnumExtensionTest.php deleted file mode 100644 index 2e14b23..0000000 --- a/Tests/DependencyInjection/EnumExtensionTest.php +++ /dev/null @@ -1,75 +0,0 @@ - - */ -class EnumExtensionTest extends \PHPUnit_Framework_TestCase -{ - /** - * @return EnumExtension - */ - public function extension() - { - return new EnumExtension(); - } - - /** - * @test - */ - public function it_register_enum_for_autoconfiguration_by_default() - { - $container = new ContainerBuilder(); - $this->extension()->load([[]], $container); - - if (method_exists($container, 'getAutoconfiguredInstanceof')) { - $autoconfigure = $container->getAutoconfiguredInstanceof(); - $this->assertArrayHasKey(EnumInterface::class, $autoconfigure); - $this->assertEquals(['enum' => [[]]], $autoconfigure[EnumInterface::class]->getTags()); - } else { - $this->assertTrue(true); // not in this version - } - } - - /** - * @test - */ - public function it_do_not_register_enum_for_autoconfiguration_if_asked_to() - { - $container = new ContainerBuilder(); - $this->extension()->load([['enum_autoconfiguration' => false]], $container); - - if (method_exists($container, 'getAutoconfiguredInstanceof')) { - $autoconfigure = $container->getAutoconfiguredInstanceof(); - $this->assertArrayNotHasKey(EnumInterface::class, $autoconfigure); - } else { - $this->assertTrue(true); // not in this version - } - } - - /** - * @test - */ - public function it_register_enum_registry_alias_for_autowire_by_default() - { - $container = new ContainerBuilder(); - $this->extension()->load([[]], $container); - $this->assertTrue($container->hasAlias(EnumRegistryInterface::class)); - } - - /** - * @test - */ - public function it_do_not_register_enum_registry_alias_for_autowire_if_asked_to() - { - $container = new ContainerBuilder(); - $this->extension()->load([['enum_registry_autoconfigurable' => false]], $container); - $this->assertFalse($container->hasAlias(EnumRegistryInterface::class)); - } -} diff --git a/Tests/Enum/ConfigurableEnumTest.php b/Tests/Enum/ConfigurableEnumTest.php deleted file mode 100644 index a5ab0db..0000000 --- a/Tests/Enum/ConfigurableEnumTest.php +++ /dev/null @@ -1,15 +0,0 @@ - 'FOO', 'bar' => 'BAR']); - $this->assertSame('foo', $fooEnum->getName()); - $this->assertSame(['foo' => 'FOO', 'bar' => 'BAR'], $fooEnum->getChoices()); - } -} diff --git a/Tests/Fixtures/Bundles/Acme/AppBundle/AcmeAppBundle.php b/Tests/Fixtures/Bundles/Acme/AppBundle/AcmeAppBundle.php deleted file mode 100644 index 8a2a29f..0000000 --- a/Tests/Fixtures/Bundles/Acme/AppBundle/AcmeAppBundle.php +++ /dev/null @@ -1,12 +0,0 @@ - - */ -class AcmeAppBundle extends Bundle -{ -} diff --git a/Tests/Fixtures/Bundles/Acme/AppBundle/Enum/Customer/GenderEnum.php b/Tests/Fixtures/Bundles/Acme/AppBundle/Enum/Customer/GenderEnum.php deleted file mode 100644 index 052e963..0000000 --- a/Tests/Fixtures/Bundles/Acme/AppBundle/Enum/Customer/GenderEnum.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ -class GenderEnum implements EnumInterface -{ - public function getChoices() - { - return ['male' => 'Male', 'female' => 'Female']; - } - - public function getName() - { - return 'gender'; - } -} diff --git a/Tests/Fixtures/Bundles/Acme/AppBundle/Enum/Customer/StateEnum.php b/Tests/Fixtures/Bundles/Acme/AppBundle/Enum/Customer/StateEnum.php deleted file mode 100644 index 87f313f..0000000 --- a/Tests/Fixtures/Bundles/Acme/AppBundle/Enum/Customer/StateEnum.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ -class StateEnum extends AbstractTranslatedEnum -{ - protected function getValues() - { - return ['new', 'validated', 'disabled']; - } - - public function getName() - { - return 'state'; - } -} diff --git a/Tests/Fixtures/Bundles/Acme/AppBundle/Enum/DummyEnum.php b/Tests/Fixtures/Bundles/Acme/AppBundle/Enum/DummyEnum.php deleted file mode 100644 index fded8fb..0000000 --- a/Tests/Fixtures/Bundles/Acme/AppBundle/Enum/DummyEnum.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ -class DummyEnum implements EnumInterface -{ - public function getChoices() - { - return ['foo' => 'Foo', 'bar' => 'Bar']; - } - - public function getName() - { - return 'dummy'; - } -} diff --git a/Tests/Fixtures/Bundles/Acme/Bundle/AppBundle/AcmeAppBundle.php b/Tests/Fixtures/Bundles/Acme/Bundle/AppBundle/AcmeAppBundle.php deleted file mode 100644 index f6c6259..0000000 --- a/Tests/Fixtures/Bundles/Acme/Bundle/AppBundle/AcmeAppBundle.php +++ /dev/null @@ -1,12 +0,0 @@ - - */ -class AcmeAppBundle extends Bundle -{ -} diff --git a/Tests/Fixtures/Bundles/Acme/Bundle/AppBundle/Enum/Customer/GenderEnum.php b/Tests/Fixtures/Bundles/Acme/Bundle/AppBundle/Enum/Customer/GenderEnum.php deleted file mode 100644 index 073110d..0000000 --- a/Tests/Fixtures/Bundles/Acme/Bundle/AppBundle/Enum/Customer/GenderEnum.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ -class GenderEnum implements EnumInterface -{ - public function getChoices() - { - return ['male' => 'Male', 'female' => 'Female']; - } - - public function getName() - { - return 'gender'; - } -} diff --git a/Tests/Fixtures/Bundles/Acme/Bundle/AppBundle/Enum/Customer/StateEnum.php b/Tests/Fixtures/Bundles/Acme/Bundle/AppBundle/Enum/Customer/StateEnum.php deleted file mode 100644 index cd2f134..0000000 --- a/Tests/Fixtures/Bundles/Acme/Bundle/AppBundle/Enum/Customer/StateEnum.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ -class StateEnum extends AbstractTranslatedEnum -{ - protected function getValues() - { - return ['new', 'validated', 'disabled']; - } - - public function getName() - { - return 'state'; - } -} diff --git a/Tests/Fixtures/Bundles/Acme/Bundle/AppBundle/Enum/DummyEnum.php b/Tests/Fixtures/Bundles/Acme/Bundle/AppBundle/Enum/DummyEnum.php deleted file mode 100644 index c1ef2e4..0000000 --- a/Tests/Fixtures/Bundles/Acme/Bundle/AppBundle/Enum/DummyEnum.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ -class DummyEnum implements EnumInterface -{ - public function getChoices() - { - return ['foo' => 'Foo', 'bar' => 'Bar']; - } - - public function getName() - { - return 'dummy'; - } -} diff --git a/Tests/Fixtures/Bundles/AppBundle/AppBundle.php b/Tests/Fixtures/Bundles/AppBundle/AppBundle.php deleted file mode 100644 index c38ab20..0000000 --- a/Tests/Fixtures/Bundles/AppBundle/AppBundle.php +++ /dev/null @@ -1,12 +0,0 @@ - - */ -class AppBundle extends Bundle -{ -} diff --git a/Tests/Fixtures/Bundles/AppBundle/Enum/Customer/GenderEnum.php b/Tests/Fixtures/Bundles/AppBundle/Enum/Customer/GenderEnum.php deleted file mode 100644 index 5d8eef2..0000000 --- a/Tests/Fixtures/Bundles/AppBundle/Enum/Customer/GenderEnum.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ -class GenderEnum implements EnumInterface -{ - public function getChoices() - { - return ['male' => 'Male', 'female' => 'Female']; - } - - public function getName() - { - return 'gender'; - } -} diff --git a/Tests/Fixtures/Bundles/AppBundle/Enum/Customer/StateEnum.php b/Tests/Fixtures/Bundles/AppBundle/Enum/Customer/StateEnum.php deleted file mode 100644 index 8c3c519..0000000 --- a/Tests/Fixtures/Bundles/AppBundle/Enum/Customer/StateEnum.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ -class StateEnum extends AbstractTranslatedEnum -{ - protected function getValues() - { - return ['new', 'validated', 'disabled']; - } - - public function getName() - { - return 'state'; - } -} diff --git a/Tests/Fixtures/Bundles/AppBundle/Enum/DummyEnum.php b/Tests/Fixtures/Bundles/AppBundle/Enum/DummyEnum.php deleted file mode 100644 index 24fafc0..0000000 --- a/Tests/Fixtures/Bundles/AppBundle/Enum/DummyEnum.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ -class DummyEnum implements EnumInterface -{ - public function getChoices() - { - return ['foo' => 'Foo', 'bar' => 'Bar']; - } - - public function getName() - { - return 'dummy'; - } -} diff --git a/Tests/bootstrap.php b/Tests/bootstrap.php deleted file mode 100644 index 8bf2add..0000000 --- a/Tests/bootstrap.php +++ /dev/null @@ -1,19 +0,0 @@ -= 4.0 support -if (class_exists('Symfony\Component\Validator\Test\ConstraintValidatorTestCase')) { - class_alias( - 'Symfony\Component\Validator\Test\ConstraintValidatorTestCase', - 'Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest' - ); -} diff --git a/Twig/Extension/EnumExtension.php b/Twig/Extension/EnumExtension.php deleted file mode 100644 index 60d8972..0000000 --- a/Twig/Extension/EnumExtension.php +++ /dev/null @@ -1,83 +0,0 @@ - - */ -class EnumExtension extends Twig_Extension -{ - /** - * @var EnumRegistryInterface - */ - private $registry; - - /** - * @param EnumRegistryInterface $registry - */ - public function __construct(EnumRegistryInterface $registry) - { - $this->registry = $registry; - } - - /** - * @inheritdoc - */ - public function getFunctions() - { - return [ - new Twig_SimpleFunction('enum_label', [$this, 'getLabel']), - new Twig_SimpleFunction('enum_choices', [$this, 'getChoices']), - ]; - } - - /** - * @inheritdoc - */ - public function getFilters() - { - return [ - new Twig_SimpleFilter('enum_label', [$this, 'getLabel']), - ]; - } - - /** - * @param string $value - * @param string $enum - * - * @return string - */ - public function getLabel($value, $enum) - { - $choices = $this->getChoices($enum); - - if (isset($choices[$value])) { - return $choices[$value]; - } - - return $value; - } - - /** - * @param string $enum - * - * @return array - */ - public function getChoices($enum) - { - return $this->registry->get($enum)->getChoices(); - } - - /** - * @inheritdoc - */ - public function getName() - { - return 'enum'; - } -} diff --git a/composer.json b/composer.json index 7bdcbe7..b89a2f7 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "yokai/enum-bundle", - "description": "Bring simple enumeration implementation to Symfony", + "description": "Simple enumeration system with Symfony integration", "type": "symfony-bundle", "license": "MIT", "authors": [ @@ -10,23 +10,36 @@ } ], "require": { - "php": ">=5.6", - "symfony/framework-bundle": "~2.7|~3.0|~4.0", - "symfony/form": "~2.7|~3.0|~4.0", - "symfony/validator": "~2.7|~3.0|~4.0", - "twig/twig": "~1.20|~2.0" + "php": "^7.1.3", + "symfony/framework-bundle": "^4.4|^5.0" }, "require-dev": { - "doctrine/annotations": "~1.3", - "phpunit/phpunit": "~5.0" + "doctrine/annotations": "^1.3", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.0", + "squizlabs/php_codesniffer": "^3.5", + "symfony/form": "^4.4|^5.0", + "symfony/framework-bundle": "^4.4|^5.0", + "symfony/twig-bundle": "^4.4|^5.0", + "symfony/validator": "^4.4|^5.0", + "twig/twig": "^2.0|^3.0" + }, + "suggest": { + "symfony/framework-bundle": "Integrate with Symfony Framework", + "symfony/form": "Add enum form type", + "symfony/validator": "Add enum validation", + "twig/twig": "Add enum util functions" }, "autoload": { - "psr-4": { "Yokai\\EnumBundle\\": "" } + "psr-4": { "Yokai\\EnumBundle\\": "src" } + }, + "autoload-dev": { + "psr-4": { "Yokai\\EnumBundle\\Tests\\": "tests" } }, "minimum-stability": "stable", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-master": "3.x-dev" } } } diff --git a/Resources/doc/declaring-enum.md b/doc/declaring-enum.md similarity index 66% rename from Resources/doc/declaring-enum.md rename to doc/declaring-enum.md index 14e7fe8..d1bd3f4 100644 --- a/Resources/doc/declaring-enum.md +++ b/doc/declaring-enum.md @@ -19,16 +19,16 @@ Create a new class, implement both `getName` & `getChoices` methods. namespace App\Enum; -use Yokai\EnumBundle\Enum\EnumInterface; +use Yokai\EnumBundle\EnumInterface; class GenderEnum implements EnumInterface { - public function getName() + public function getName(): string { return 'gender'; } - public function getChoices() + public function getChoices(): array { return ['m' => 'Male', 'f' => 'Female']; } @@ -56,14 +56,14 @@ Create a new class, use `EnumWithClassAsNameTrait` trait and implement `getChoic namespace App\Enum; -use Yokai\EnumBundle\Enum\EnumInterface; -use Yokai\EnumBundle\Enum\EnumWithClassAsNameTrait; +use Yokai\EnumBundle\EnumInterface; +use Yokai\EnumBundle\EnumWithClassAsNameTrait; class GenderEnum implements EnumInterface { use EnumWithClassAsNameTrait; - public function getChoices() + public function getChoices(): array { return ['m' => 'Male', 'f' => 'Female']; } @@ -89,7 +89,7 @@ No need for a class, just use the `ConfigurableEnum` class and define a new enum ```yaml services: enum.member.gender: - class: 'Yokai\EnumBundle\Enum\ConfigurableEnum' + class: 'Yokai\EnumBundle\ConfigurableEnum' public: false tags: ['enum'] arguments: @@ -97,3 +97,22 @@ services: - m: 'Male' f: 'Female' ``` + + +The configurable way extracting constant list +-------------------- + +Let say that you already have a list of constant that for the gender. +No need for a class, just use the `ConstantListEnum` class and define a new enum service. + +```yaml +services: + enum.member.gender: + class: 'Yokai\EnumBundle\ConstantListEnum' + public: false + tags: ['enum'] + arguments: + - '@enum.constant_extractor' + - 'App\\Model\\Person::GENDER_*' + - "gender" +``` diff --git a/Resources/doc/declaring-translated-enum.md b/doc/declaring-translated-enum.md similarity index 65% rename from Resources/doc/declaring-translated-enum.md rename to doc/declaring-translated-enum.md index 700ff70..8b5cab7 100644 --- a/Resources/doc/declaring-translated-enum.md +++ b/doc/declaring-translated-enum.md @@ -6,7 +6,7 @@ If you wish to use Symfony's Translator for your enum labels, this bundle provid There is a lot of way to create and declare such classes. > **Note :** It is pretty much like [declaring classic enums](declaring-enum.md), -> but with a `extends` instead of an `implements`. +> but with a dependency to `symfony/translator`. The classic way @@ -19,8 +19,8 @@ Create a new class, implement both `getName` & `getValues` methods and specify t namespace App\Enum; -use Symfony\Component\Translation\TranslatorInterface; -use Yokai\EnumBundle\Enum\AbstractTranslatedEnum; +use Symfony\Contracts\Translation\TranslatorInterface; +use Yokai\EnumBundle\AbstractTranslatedEnum; class GenderEnum extends AbstractTranslatedEnum { @@ -29,12 +29,12 @@ class GenderEnum extends AbstractTranslatedEnum parent::__construct($translator, 'enum.gender.%s'); } - public function getName() + public function getName(): string { return 'gender'; } - public function getValues() + public function getValues(): array { return ['m', 'f']; } @@ -64,9 +64,9 @@ Create a new class, use `EnumWithClassAsNameTrait` trait, implement `getValues` namespace App\Enum; -use Symfony\Component\Translation\TranslatorInterface; -use Yokai\EnumBundle\Enum\AbstractTranslatedEnum; -use Yokai\EnumBundle\Enum\EnumWithClassAsNameTrait; +use Symfony\Contracts\Translation\TranslatorInterface; +use Yokai\EnumBundle\AbstractTranslatedEnum; +use Yokai\EnumBundle\EnumWithClassAsNameTrait; class GenderEnum extends AbstractTranslatedEnum { @@ -77,7 +77,7 @@ class GenderEnum extends AbstractTranslatedEnum parent::__construct($translator, 'enum.gender.%s'); } - public function getValues() + public function getValues(): array { return ['m', 'f']; } @@ -105,7 +105,7 @@ No need for a class, just use the `ConfigurableTranslatedEnum` class and define ```yaml services: enum.member.gender: - class: 'Yokai\EnumBundle\Enum\ConfigurableTranslatedEnum' + class: 'Yokai\EnumBundle\ConfigurableTranslatedEnum' public: false tags: ['enum'] arguments: @@ -114,3 +114,23 @@ services: - "gender" - ['m', 'f'] ``` + +The configurable way extracting constant list +-------------------- + +Let say that you already have a list of constant that for the gender. +No need for a class, just use the `ConstantListTranslatedEnum` class and define a new enum service. + +```yaml +services: + enum.member.gender: + class: 'Yokai\EnumBundle\ConstantListTranslatedEnum' + public: false + tags: ['enum'] + arguments: + - '@yokai_enum.constant_extractor' + - 'App\\Model\\Person::GENDER_*' + - "@translator" + - "enum.gender.%s" + - "gender" +``` diff --git a/Resources/doc/sonata-admin.md b/doc/sonata-admin.md similarity index 89% rename from Resources/doc/sonata-admin.md rename to doc/sonata-admin.md index e07e106..9f259d1 100644 --- a/Resources/doc/sonata-admin.md +++ b/doc/sonata-admin.md @@ -20,7 +20,7 @@ use Yokai\EnumBundle\Form\Type\EnumType; class MemberAdmin extends AbstractAdmin { - protected function configureListFields(ListMapper $list) + protected function configureListFields(ListMapper $list): void { $list ->add('gender', null, [ @@ -30,7 +30,7 @@ class MemberAdmin extends AbstractAdmin ; } - protected function configureDatagridFilters(DatagridMapper $filter) + protected function configureDatagridFilters(DatagridMapper $filter): void { $filter ->add('gender', 'doctrine_orm_choice', [ @@ -43,7 +43,7 @@ class MemberAdmin extends AbstractAdmin ; } - protected function configureFormFields(FormMapper $form) + protected function configureFormFields(FormMapper $form): void { $form // Let the bundle guess the form type for you (requires that you configured the validation) @@ -52,7 +52,7 @@ class MemberAdmin extends AbstractAdmin ; } - protected function configureShowFields(ShowMapper $form) + protected function configureShowFields(ShowMapper $form): void { $form ->add('gender', null, [ diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..3d26c56 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,21 @@ + + + + + + + + + + + + src/ + + + + + + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 74ed0e5..dd1c914 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -7,24 +7,19 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" - syntaxCheck="false" colors="true" - bootstrap="Tests/bootstrap.php" + bootstrap="vendor/autoload.php" > - ./Tests + ./tests - ./ - - ./Tests - ./vendor - + ./src diff --git a/Enum/AbstractTranslatedEnum.php b/src/AbstractTranslatedEnum.php similarity index 80% rename from Enum/AbstractTranslatedEnum.php rename to src/AbstractTranslatedEnum.php index 4d629e8..7e9a0f3 100644 --- a/Enum/AbstractTranslatedEnum.php +++ b/src/AbstractTranslatedEnum.php @@ -1,8 +1,10 @@ getValues(), array_map( - function ($value) { + function (string $value): string { return $this->translator->trans( sprintf($this->transPattern, $value), [], @@ -62,7 +64,7 @@ function ($value) { /** * @param string $transDomain */ - public function setTransDomain($transDomain) + public function setTransDomain(string $transDomain): void { $this->transDomain = $transDomain; } @@ -70,5 +72,5 @@ public function setTransDomain($transDomain) /** * @return array */ - abstract protected function getValues(); + abstract protected function getValues(): array; } diff --git a/Enum/ConfigurableEnum.php b/src/ConfigurableEnum.php similarity index 73% rename from Enum/ConfigurableEnum.php rename to src/ConfigurableEnum.php index 1683071..73aee05 100644 --- a/Enum/ConfigurableEnum.php +++ b/src/ConfigurableEnum.php @@ -1,6 +1,8 @@ @@ -21,7 +23,7 @@ class ConfigurableEnum implements EnumInterface * @param string $name * @param array $choices */ - public function __construct($name, array $choices) + public function __construct(string $name, array $choices) { $this->name = $name; $this->choices = $choices; @@ -30,7 +32,7 @@ public function __construct($name, array $choices) /** * @inheritdoc */ - public function getName() + public function getName(): string { return $this->name; } @@ -38,7 +40,7 @@ public function getName() /** * @inheritdoc */ - public function getChoices() + public function getChoices(): array { return $this->choices; } diff --git a/Enum/ConfigurableTranslatedEnum.php b/src/ConfigurableTranslatedEnum.php similarity index 76% rename from Enum/ConfigurableTranslatedEnum.php rename to src/ConfigurableTranslatedEnum.php index 1b3b752..6056987 100644 --- a/Enum/ConfigurableTranslatedEnum.php +++ b/src/ConfigurableTranslatedEnum.php @@ -1,8 +1,10 @@ @@ -25,7 +27,7 @@ class ConfigurableTranslatedEnum extends AbstractTranslatedEnum * @param string $name * @param array $values */ - public function __construct(TranslatorInterface $translator, $transPattern, $name, array $values) + public function __construct(TranslatorInterface $translator, string $transPattern, string $name, array $values) { parent::__construct($translator, $transPattern); @@ -36,7 +38,7 @@ public function __construct(TranslatorInterface $translator, $transPattern, $nam /** * @inheritdoc */ - public function getName() + public function getName(): string { return $this->name; } @@ -44,7 +46,7 @@ public function getName() /** * @inheritdoc */ - public function getValues() + protected function getValues(): array { return $this->values; } diff --git a/src/ConstantExtractor.php b/src/ConstantExtractor.php new file mode 100644 index 0000000..cda8924 --- /dev/null +++ b/src/ConstantExtractor.php @@ -0,0 +1,83 @@ + + */ +class ConstantExtractor +{ + public function extract(string $pattern): array + { + [$class, $patternRegex] = $this->explode($pattern); + + return $this->filter( + $this->publicConstants($class), + $patternRegex, + $pattern + ); + } + + private function filter(array $constants, string $regexp, string $pattern): array + { + $matchingNames = preg_grep($regexp, array_keys($constants)); + + if (count($matchingNames) === 0) { + throw CannotExtractConstantsException::noConstantMatchingPattern($pattern); + } + + return array_values(array_intersect_key($constants, array_flip($matchingNames))); + } + + private function publicConstants(string $class): array + { + try { + $constants = (new ReflectionClass($class))->getReflectionConstants(); + } catch (ReflectionException $exception) { + throw CannotExtractConstantsException::classDoNotExists($class); + } + + $list = []; + foreach ($constants as $constant) { + if (!$constant->isPublic()) { + continue; + } + + $list[$constant->getName()] = $constant->getValue(); + } + + if (count($list) === 0) { + throw CannotExtractConstantsException::classHasNoPublicConstant($class); + } + + return $list; + } + + private function explode(string $pattern): array + { + if (substr_count($pattern, '::') !== 1) { + throw CannotExtractConstantsException::invalidPattern($pattern); + } + + [$class, $constantsNamePattern] = explode('::', $pattern); + + if (substr_count($constantsNamePattern, '*') === 0) { + throw CannotExtractConstantsException::invalidPattern($pattern); + } + + $constantsNameRegexp = sprintf( + '#^%s$#', + str_replace('*', '[0-9a-zA-Z_]+', $constantsNamePattern) + ); + + return [$class, $constantsNameRegexp]; + } +} diff --git a/src/ConstantListEnum.php b/src/ConstantListEnum.php new file mode 100644 index 0000000..f676d92 --- /dev/null +++ b/src/ConstantListEnum.php @@ -0,0 +1,20 @@ + + */ +class ConstantListEnum extends ConfigurableEnum +{ + /** + * @inheritDoc + */ + public function __construct(ConstantExtractor $extractor, string $constantsPattern, string $name) + { + $values = $extractor->extract($constantsPattern); + parent::__construct($name, array_combine($values, $values)); + } +} diff --git a/src/ConstantListTranslatedEnum.php b/src/ConstantListTranslatedEnum.php new file mode 100644 index 0000000..aec3509 --- /dev/null +++ b/src/ConstantListTranslatedEnum.php @@ -0,0 +1,26 @@ + + */ +class ConstantListTranslatedEnum extends ConfigurableTranslatedEnum +{ + /** + * @inheritDoc + */ + public function __construct( + ConstantExtractor $extractor, + string $constantsPattern, + TranslatorInterface $translator, + string $transPattern, + string $name + ) { + parent::__construct($translator, $transPattern, $name, $extractor->extract($constantsPattern)); + } +} diff --git a/DependencyInjection/CompilerPass/TaggedEnumCollectorCompilerPass.php b/src/DependencyInjection/CompilerPass/TaggedEnumCollectorCompilerPass.php similarity index 89% rename from DependencyInjection/CompilerPass/TaggedEnumCollectorCompilerPass.php rename to src/DependencyInjection/CompilerPass/TaggedEnumCollectorCompilerPass.php index 63863c7..11e6ce8 100644 --- a/DependencyInjection/CompilerPass/TaggedEnumCollectorCompilerPass.php +++ b/src/DependencyInjection/CompilerPass/TaggedEnumCollectorCompilerPass.php @@ -1,5 +1,7 @@ hasDefinition('enum.registry')) { return; diff --git a/src/DependencyInjection/EnumExtension.php b/src/DependencyInjection/EnumExtension.php new file mode 100644 index 0000000..081d7d6 --- /dev/null +++ b/src/DependencyInjection/EnumExtension.php @@ -0,0 +1,49 @@ + + */ +class EnumExtension extends Extension +{ + /** + * @inheritdoc + */ + public function load(array $configs, ContainerBuilder $container): void + { + $xmlLoader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); + $xmlLoader->load('enum.xml'); + + $requiresForm = interface_exists(FormInterface::class); + $requiresValidator = interface_exists(ValidatorInterface::class); + $requiresTwig = class_exists(TwigBundle::class); + + if ($requiresForm) { + $xmlLoader->load('form.xml'); + if (!$requiresValidator) { + $container->removeDefinition('form_extention.type_guesser.enum'); + } + } + if ($requiresValidator) { + $xmlLoader->load('validator.xml'); + } + if ($requiresTwig) { + $xmlLoader->load('twig.xml'); + } + + $container->registerForAutoconfiguration(EnumInterface::class) + ->addTag('enum'); + } +} diff --git a/Enum/EnumInterface.php b/src/EnumInterface.php similarity index 55% rename from Enum/EnumInterface.php rename to src/EnumInterface.php index 01e212e..3bb2b56 100644 --- a/Enum/EnumInterface.php +++ b/src/EnumInterface.php @@ -1,6 +1,8 @@ @@ -10,10 +12,10 @@ interface EnumInterface /** * @return array */ - public function getChoices(); + public function getChoices(): array; /** * @return string */ - public function getName(); + public function getName(): string; } diff --git a/Registry/EnumRegistry.php b/src/EnumRegistry.php similarity index 59% rename from Registry/EnumRegistry.php rename to src/EnumRegistry.php index a80afb2..762b224 100644 --- a/Registry/EnumRegistry.php +++ b/src/EnumRegistry.php @@ -1,15 +1,16 @@ */ -class EnumRegistry implements EnumRegistryInterface +class EnumRegistry { /** * @var EnumInterface[] @@ -17,9 +18,11 @@ class EnumRegistry implements EnumRegistryInterface private $enums; /** - * @inheritdoc + * @param EnumInterface $enum + * + * @throws DuplicatedEnumException */ - public function add(EnumInterface $enum) + public function add(EnumInterface $enum): void { if ($this->has($enum->getName())) { throw DuplicatedEnumException::alreadyRegistered($enum->getName()); @@ -29,9 +32,12 @@ public function add(EnumInterface $enum) } /** - * @inheritdoc + * @param string $name + * + * @return EnumInterface + * @throws InvalidEnumException */ - public function get($name) + public function get(string $name): EnumInterface { if (!$this->has($name)) { throw InvalidEnumException::nonexistent($name); @@ -41,17 +47,19 @@ public function get($name) } /** - * @inheritdoc + * @param string $name + * + * @return bool */ - public function has($name) + public function has(string $name): bool { return isset($this->enums[$name]); } /** - * @inheritDoc + * @return EnumInterface[] */ - public function all() + public function all(): array { return $this->enums; } diff --git a/Enum/EnumWithClassAsNameTrait.php b/src/EnumWithClassAsNameTrait.php similarity index 66% rename from Enum/EnumWithClassAsNameTrait.php rename to src/EnumWithClassAsNameTrait.php index 2e8898f..04e3879 100644 --- a/Enum/EnumWithClassAsNameTrait.php +++ b/src/EnumWithClassAsNameTrait.php @@ -1,6 +1,8 @@ @@ -10,7 +12,7 @@ trait EnumWithClassAsNameTrait /** * @return string */ - public function getName() + public function getName(): string { return static::class; } diff --git a/src/Exception/CannotExtractConstantsException.php b/src/Exception/CannotExtractConstantsException.php new file mode 100644 index 0000000..5b5bc72 --- /dev/null +++ b/src/Exception/CannotExtractConstantsException.php @@ -0,0 +1,35 @@ + + */ +class CannotExtractConstantsException extends InvalidArgumentException +{ + public static function invalidPattern(string $pattern): self + { + return new self( + "Constant extraction pattern must look like Fully\\Qualified\\ClassName::CONSTANT_*. Got $pattern." + ); + } + + public static function classDoNotExists(string $class): self + { + return new self("Class $class do not exists."); + } + + public static function classHasNoPublicConstant(string $class): self + { + return new self("Class $class has no public constant."); + } + + public static function noConstantMatchingPattern(string $pattern): self + { + return new self("Pattern $pattern matches no constant."); + } +} diff --git a/Exception/DuplicatedEnumException.php b/src/Exception/DuplicatedEnumException.php similarity index 80% rename from Exception/DuplicatedEnumException.php rename to src/Exception/DuplicatedEnumException.php index 9a87a83..39eff58 100644 --- a/Exception/DuplicatedEnumException.php +++ b/src/Exception/DuplicatedEnumException.php @@ -1,5 +1,7 @@ enumRegistry = $enumRegistry; - - if (method_exists(AbstractType::class, 'getBlockPrefix')) { - $this->enumFormType = EnumType::class; //Symfony 3.x support - } else { - $this->enumFormType = 'enum'; //Symfony 2.x support - } } /** * @inheritdoc */ - public function guessTypeForConstraint(Constraint $constraint) + public function guessTypeForConstraint(Constraint $constraint): ?TypeGuess { if (!$constraint instanceof Enum) { return null; } return new TypeGuess( - $this->enumFormType, + EnumType::class, [ 'enum' => $constraint->enum, 'multiple' => $constraint->multiple, @@ -66,7 +57,7 @@ public function guessTypeForConstraint(Constraint $constraint) /** * @inheritDoc */ - public function guessRequired($class, $property) + public function guessRequired($class, $property): ?ValueGuess { return null; //override parent : not able to guess } @@ -74,7 +65,7 @@ public function guessRequired($class, $property) /** * @inheritDoc */ - public function guessMaxLength($class, $property) + public function guessMaxLength($class, $property): ?ValueGuess { return null; //override parent : not able to guess } @@ -82,7 +73,7 @@ public function guessMaxLength($class, $property) /** * @inheritDoc */ - public function guessPattern($class, $property) + public function guessPattern($class, $property): ?ValueGuess { return null; //override parent : not able to guess } diff --git a/Form/Type/EnumType.php b/src/Form/Type/EnumType.php similarity index 59% rename from Form/Type/EnumType.php rename to src/Form/Type/EnumType.php index 4262897..fd9c075 100644 --- a/Form/Type/EnumType.php +++ b/src/Form/Type/EnumType.php @@ -1,12 +1,14 @@ @@ -14,14 +16,14 @@ class EnumType extends AbstractType { /** - * @var EnumRegistryInterface + * @var EnumRegistry */ private $enumRegistry; /** - * @param EnumRegistryInterface $enumRegistry + * @param EnumRegistry $enumRegistry */ - public function __construct(EnumRegistryInterface $enumRegistry) + public function __construct(EnumRegistry $enumRegistry) { $this->enumRegistry = $enumRegistry; } @@ -29,20 +31,19 @@ public function __construct(EnumRegistryInterface $enumRegistry) /** * @inheritdoc */ - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver ->setRequired('enum') - ->setDefault('choices_as_values', true) ->setAllowedValues( 'enum', - function ($name) { + function (string $name): bool { return $this->enumRegistry->has($name); } ) ->setDefault( 'choices', - function (Options $options) { + function (Options $options): array { return array_flip($this->enumRegistry->get($options['enum'])->getChoices()); } ) @@ -52,28 +53,16 @@ function (Options $options) { /** * @inheritdoc */ - public function getParent() + public function getParent(): string { - if (!method_exists(AbstractType::class, 'getBlockPrefix')) { - return 'choice'; //Symfony 2.x support - } - return ChoiceType::class; } /** * @inheritdoc */ - public function getBlockPrefix() + public function getBlockPrefix(): string { return 'enum'; } - - /** - * @inheritdoc - */ - public function getName() - { - return $this->getBlockPrefix(); - } } diff --git a/src/Resources/config/enum.xml b/src/Resources/config/enum.xml new file mode 100644 index 0000000..2a9775d --- /dev/null +++ b/src/Resources/config/enum.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/Resources/config/forms.xml b/src/Resources/config/form.xml similarity index 58% rename from Resources/config/forms.xml rename to src/Resources/config/form.xml index 0c88ac9..78bcd1f 100644 --- a/Resources/config/forms.xml +++ b/src/Resources/config/form.xml @@ -5,20 +5,16 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - - + + - - + - - + - diff --git a/Resources/config/twig.xml b/src/Resources/config/twig.xml similarity index 67% rename from Resources/config/twig.xml rename to src/Resources/config/twig.xml index f4e282c..47bd009 100644 --- a/Resources/config/twig.xml +++ b/src/Resources/config/twig.xml @@ -5,13 +5,10 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - - + + - diff --git a/Resources/config/validators.xml b/src/Resources/config/validator.xml similarity index 67% rename from Resources/config/validators.xml rename to src/Resources/config/validator.xml index 42f99bf..8c5e36d 100644 --- a/Resources/config/validators.xml +++ b/src/Resources/config/validator.xml @@ -5,13 +5,10 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - - + + - diff --git a/src/Twig/Extension/EnumExtension.php b/src/Twig/Extension/EnumExtension.php new file mode 100644 index 0000000..ba7f157 --- /dev/null +++ b/src/Twig/Extension/EnumExtension.php @@ -0,0 +1,71 @@ + + */ +class EnumExtension extends AbstractExtension +{ + /** + * @var EnumRegistry + */ + private $registry; + + /** + * @param EnumRegistry $registry + */ + public function __construct(EnumRegistry $registry) + { + $this->registry = $registry; + } + + /** + * @inheritdoc + */ + public function getFunctions(): array + { + return [ + new TwigFunction('enum_label', [$this, 'getLabel']), + new TwigFunction('enum_choices', [$this, 'getChoices']), + ]; + } + + /** + * @inheritdoc + */ + public function getFilters(): array + { + return [ + new TwigFilter('enum_label', [$this, 'getLabel']), + ]; + } + + /** + * @param string $value + * @param string $enum + * + * @return string + */ + public function getLabel(string $value, string $enum): string + { + return $this->getChoices($enum)[$value] ?? $value; + } + + /** + * @param string $enum + * + * @return array + */ + public function getChoices(string $enum): array + { + return $this->registry->get($enum)->getChoices(); + } +} diff --git a/Validator/Constraints/Enum.php b/src/Validator/Constraints/Enum.php similarity index 73% rename from Validator/Constraints/Enum.php rename to src/Validator/Constraints/Enum.php index 4373196..91bfbcc 100644 --- a/Validator/Constraints/Enum.php +++ b/src/Validator/Constraints/Enum.php @@ -1,5 +1,7 @@ @@ -14,14 +16,14 @@ class EnumValidator extends ChoiceValidator { /** - * @var EnumRegistryInterface + * @var EnumRegistry */ private $enumRegistry; /** - * @param EnumRegistryInterface $enumRegistry + * @param EnumRegistry $enumRegistry */ - public function __construct(EnumRegistryInterface $enumRegistry) + public function __construct(EnumRegistry $enumRegistry) { $this->enumRegistry = $enumRegistry; } @@ -29,10 +31,10 @@ public function __construct(EnumRegistryInterface $enumRegistry) /** * @inheritdoc */ - public function validate($value, Constraint $constraint) + public function validate($value, Constraint $constraint): void { if (!$constraint instanceof Enum) { - throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Enum'); + throw new UnexpectedTypeException($constraint, __NAMESPACE__ . '\Enum'); } $constraint->choices = null; diff --git a/YokaiEnumBundle.php b/src/YokaiEnumBundle.php similarity index 65% rename from YokaiEnumBundle.php rename to src/YokaiEnumBundle.php index a010f52..e798e6b 100644 --- a/YokaiEnumBundle.php +++ b/src/YokaiEnumBundle.php @@ -1,10 +1,11 @@ addCompilerPass(new ConventionedEnumCollectorCompilerPass()) - ->addCompilerPass(new TaggedEnumCollectorCompilerPass) + ->addCompilerPass(new TaggedEnumCollectorCompilerPass()) ; } /** * @inheritdoc */ - public function getContainerExtension() + public function getContainerExtension(): EnumExtension { - return new EnumExtension; + return new EnumExtension(); } } diff --git a/tests/ConfigurableEnumTest.php b/tests/ConfigurableEnumTest.php new file mode 100644 index 0000000..fcd6702 --- /dev/null +++ b/tests/ConfigurableEnumTest.php @@ -0,0 +1,18 @@ + + */ +class ConfigurableEnumTest extends TestCase +{ + public function testConfigurability(): void + { + $fooEnum = new ConfigurableEnum('foo', ['foo' => 'FOO', 'bar' => 'BAR']); + $this->assertSame('foo', $fooEnum->getName()); + $this->assertSame(['foo' => 'FOO', 'bar' => 'BAR'], $fooEnum->getChoices()); + } +} diff --git a/Tests/Enum/ConfigurableTranslatedEnumTest.php b/tests/ConfigurableTranslatedEnumTest.php similarity index 69% rename from Tests/Enum/ConfigurableTranslatedEnumTest.php rename to tests/ConfigurableTranslatedEnumTest.php index bef5223..3c2abdd 100644 --- a/Tests/Enum/ConfigurableTranslatedEnumTest.php +++ b/tests/ConfigurableTranslatedEnumTest.php @@ -1,27 +1,28 @@ - */ -class ConfigurableTranslatedEnumTest extends \PHPUnit_Framework_TestCase +class ConfigurableTranslatedEnumTest extends TestCase { - public function testConstructedWithInvalidPattern() + public function testConstructedWithInvalidPattern(): void { - $this->expectException('Yokai\EnumBundle\Exception\InvalidTranslatePatternException'); - $translator = $this->prophesize('Symfony\Component\Translation\TranslatorInterface'); + $this->expectException(InvalidTranslatePatternException::class); + $translator = $this->prophesize(TranslatorInterface::class); new ConfigurableTranslatedEnum($translator->reveal(), 'invalid.pattern', 'invalid', ['foo', 'bar']); } - public function testTranslatedChoices() + public function testTranslatedChoices(): void { /** @var ObjectProphecy|TranslatorInterface $translator */ - $translator = $this->prophesize('Symfony\Component\Translation\TranslatorInterface'); + $translator = $this->prophesize(TranslatorInterface::class); $translator->trans('choice.something.foo', [], 'messages', null)->shouldBeCalled()->willReturn('FOO translated'); $translator->trans('choice.something.bar', [], 'messages', null)->shouldBeCalled()->willReturn('BAR translated'); $type = new ConfigurableTranslatedEnum($translator->reveal(), 'choice.something.%s', 'something', ['foo', 'bar']); @@ -33,10 +34,10 @@ public function testTranslatedChoices() $this->assertEquals($expectedChoices, $type->getChoices()); } - public function testTranslatedWithDomainChoices() + public function testTranslatedWithDomainChoices(): void { /** @var ObjectProphecy|TranslatorInterface $translator */ - $translator = $this->prophesize('Symfony\Component\Translation\TranslatorInterface'); + $translator = $this->prophesize(TranslatorInterface::class); $translator->trans('choice.something.foo', [], 'messages', null)->shouldNotBeCalled(); $translator->trans('choice.something.bar', [], 'messages', null)->shouldNotBeCalled(); $translator->trans('something.foo', [], 'choices', null)->shouldBeCalled()->willReturn('FOO translated'); diff --git a/tests/ConstantExtractorTest.php b/tests/ConstantExtractorTest.php new file mode 100644 index 0000000..0c39755 --- /dev/null +++ b/tests/ConstantExtractorTest.php @@ -0,0 +1,123 @@ + + */ +class ConstantExtractorTest extends TestCase +{ + public function getExtractor(): ConstantExtractor + { + return new ConstantExtractor(); + } + + /** + * @dataProvider malformed + */ + public function testExtractMalformedPattern(string $pattern, string $exceptionMessage): void + { + $this->expectException(CannotExtractConstantsException::class); + $this->expectExceptionMessageMatches($exceptionMessage); + + $this->getExtractor()->extract($pattern); + } + + /** + * @dataProvider empty + */ + public function testExtractEmpty(string $pattern, string $exceptionMessage): void + { + $this->expectException(CannotExtractConstantsException::class); + $this->expectExceptionMessageMatches($exceptionMessage); + + $this->getExtractor()->extract($pattern); + } + + /** + * @dataProvider successful + */ + public function testExtractSuccessful(string $pattern, array $expectedList): void + { + self::assertEquals($expectedList, $this->getExtractor()->extract($pattern)); + } + + public function empty(): Generator + { + yield 'class without constant' => [ + ClassWithoutConstant::class.'::*', + '/Class .+ has no public constant/', + ]; + yield 'class without public constant' => [ + ClassWithNoPublicConstant::class.'::*', + '/Class .+ has no public constant/', + ]; + yield 'class with constant but no match' => [ + ClassWithConstant::class.'::NO_MATCH*', + '/Pattern .+ matches no constant/', + ]; + } + + public function malformed(): Generator + { + $invalidPatternRegexp = '/Constant extraction pattern must look like Fully\\\\Qualified\\\\ClassName::CONSTANT_\*\..+/'; + yield 'no class no constant pattern' => [ + 'not a pattern', + $invalidPatternRegexp, + ]; + yield 'class that do not exists' => [ + 'SomeClassThatDoNotExists::STATUS_*', + '/Class SomeClassThatDoNotExists do not exists\./', + ]; + yield 'missing constant pattern and separator' => [ + ClassWithConstant::class, + $invalidPatternRegexp, + ]; + yield 'missing constant pattern' => [ + ClassWithConstant::class.'::', + $invalidPatternRegexp, + ]; + yield 'two separator' => [ + ClassWithConstant::class.'::STATUS_*::', + $invalidPatternRegexp, + ]; + yield 'no * in pattern' => [ + ClassWithConstant::class.'::STATUS_ONLINE', + $invalidPatternRegexp, + ]; + } + + public function successful(): Generator + { + yield 'starting with status' => [ + ClassWithConstant::class.'::STATUS_*', + [ClassWithConstant::STATUS_ONLINE, ClassWithConstant::STATUS_DRAFT], + ]; + yield 'ending with online' => [ + ClassWithConstant::class.'::*_ONLINE', + [ClassWithConstant::STATUS_ONLINE], + ]; + } +} + +class ClassWithoutConstant +{ +} + +class ClassWithNoPublicConstant +{ + private const PROTECTED_CONST = 'protected'; + private const PRIVATE_CONST = 'private'; +} + +class ClassWithConstant +{ + public const STATUS_ONLINE = 'online'; + public const STATUS_DRAFT = 'draft'; + protected const STATUS_PROTECTED = 'protected'; + private const STATUS_PRIVATE = 'private'; +} diff --git a/tests/ConstantListEnumTest.php b/tests/ConstantListEnumTest.php new file mode 100644 index 0000000..e9d68d7 --- /dev/null +++ b/tests/ConstantListEnumTest.php @@ -0,0 +1,42 @@ + + */ +class ConstantListEnumTest extends TestCase +{ + public function getEnum(string $pattern, string $name): ConstantListEnum + { + return new ConstantListEnum(new ConstantExtractor(), $pattern, $name); + } + + public function testVehicleEnums(): void + { + $type = $this->getEnum(Vehicle::class.'::TYPE_*', 'vehicle.type'); + self::assertSame('vehicle.type', $type->getName()); + self::assertSame( + ['bike' => 'bike', 'car' => 'car', 'bus' => 'bus'], + $type->getChoices() + ); + + $engine = $this->getEnum(Vehicle::class.'::ENGINE_*', 'vehicle.engine'); + self::assertSame('vehicle.engine', $engine->getName()); + self::assertSame( + ['electic' => 'electic', 'combustion' => 'combustion'], + $engine->getChoices() + ); + + $brand = $this->getEnum(Vehicle::class.'::BRAND_*', 'vehicle.brand'); + self::assertSame('vehicle.brand', $brand->getName()); + self::assertSame( + ['renault' => 'renault', 'volkswagen' => 'volkswagen', 'toyota' => 'toyota'], + $brand->getChoices() + ); + } +} diff --git a/tests/ConstantListTranslatedEnumTest.php b/tests/ConstantListTranslatedEnumTest.php new file mode 100644 index 0000000..9588996 --- /dev/null +++ b/tests/ConstantListTranslatedEnumTest.php @@ -0,0 +1,68 @@ + + */ +class ConstantListTranslatedEnumTest extends TestCase +{ + /** + * @var TranslatorInterface|ObjectProphecy + */ + private $translator; + + protected function setUp(): void + { + $this->translator = $this->prophesize(TranslatorInterface::class); + } + + public function getEnum(string $pattern, string $name): ConstantListTranslatedEnum + { + return new ConstantListTranslatedEnum( + new ConstantExtractor(), + $pattern, + $this->translator->reveal(), + $name.'.%s', + $name + ); + } + + public function testVehicleEnums(): void + { + $type = $this->getEnum(Vehicle::class.'::TYPE_*', 'vehicle.type'); + $this->translator->trans('vehicle.type.bike', [], 'messages')->shouldBeCalledTimes(1)->willReturn('Moto'); + $this->translator->trans('vehicle.type.car', [], 'messages')->shouldBeCalledTimes(1)->willReturn('Voiture'); + $this->translator->trans('vehicle.type.bus', [], 'messages')->shouldBeCalledTimes(1)->willReturn('Bus'); + self::assertSame('vehicle.type', $type->getName()); + self::assertSame( + ['bike' => 'Moto', 'car' => 'Voiture', 'bus' => 'Bus'], + $type->getChoices() + ); + + $engine = $this->getEnum(Vehicle::class.'::ENGINE_*', 'vehicle.engine'); + $this->translator->trans('vehicle.engine.electic', [], 'messages')->shouldBeCalledTimes(1)->willReturn('Electrique'); + $this->translator->trans('vehicle.engine.combustion', [], 'messages')->shouldBeCalledTimes(1)->willReturn('Combustion'); + self::assertSame('vehicle.engine', $engine->getName()); + self::assertSame( + ['electic' => 'Electrique', 'combustion' => 'Combustion'], + $engine->getChoices() + ); + + $brand = $this->getEnum(Vehicle::class.'::BRAND_*', 'vehicle.brand'); + $this->translator->trans('vehicle.brand.renault', [], 'messages')->shouldBeCalledTimes(1)->willReturn('Renault'); + $this->translator->trans('vehicle.brand.volkswagen', [], 'messages')->shouldBeCalledTimes(1)->willReturn('Volkswagen'); + $this->translator->trans('vehicle.brand.toyota', [], 'messages')->shouldBeCalledTimes(1)->willReturn('Toyota'); + self::assertSame('vehicle.brand', $brand->getName()); + self::assertSame( + ['renault' => 'Renault', 'volkswagen' => 'Volkswagen', 'toyota' => 'Toyota'], + $brand->getChoices() + ); + } +} diff --git a/Tests/DependencyInjection/CompilerPass/TaggedEnumCollectorCompilerPassTest.php b/tests/DependencyInjection/CompilerPass/TaggedEnumCollectorCompilerPassTest.php similarity index 58% rename from Tests/DependencyInjection/CompilerPass/TaggedEnumCollectorCompilerPassTest.php rename to tests/DependencyInjection/CompilerPass/TaggedEnumCollectorCompilerPassTest.php index 7496a2f..8e6e31d 100644 --- a/Tests/DependencyInjection/CompilerPass/TaggedEnumCollectorCompilerPassTest.php +++ b/tests/DependencyInjection/CompilerPass/TaggedEnumCollectorCompilerPassTest.php @@ -1,50 +1,48 @@ - */ -class TaggedEnumCollectorCompilerPassTest extends \PHPUnit_Framework_TestCase +class TaggedEnumCollectorCompilerPassTest extends TestCase { /** * @var TaggedEnumCollectorCompilerPass */ private $compiler; - protected function setUp() + protected function setUp(): void { $this->compiler = new TaggedEnumCollectorCompilerPass; } - protected function tearDown() + public function testCollectWhenServiceNotAvailable(): void { - unset($this->compiler); - } - - public function testCollectWhenServiceNotAvailable() - { - $compiler = $this->prophesize('Symfony\Component\DependencyInjection\ContainerBuilder'); + $compiler = $this->prophesize(ContainerBuilder::class); $compiler->hasDefinition('enum.registry')->shouldBeCalled()->willReturn(false); $this->compiler->process($compiler->reveal()); } - public function testCollectEnums() + public function testCollectEnums(): void { - $registry = $this->prophesize('Symfony\Component\DependencyInjection\Definition'); + $registry = $this->prophesize(Definition::class); $registry->addMethodCall('add', [new Reference('enum.gender')])->shouldBeCalled(); $registry->addMethodCall('add', [new Reference('enum.type')])->shouldBeCalled(); - $compiler = $this->prophesize('Symfony\Component\DependencyInjection\ContainerBuilder'); + $compiler = $this->prophesize(ContainerBuilder::class); $compiler->hasDefinition('enum.registry')->shouldBeCalled()->willReturn(true); $compiler->getDefinition('enum.registry')->shouldBeCalled()->willReturn($registry); $compiler->findTaggedServiceIds('enum')->shouldBeCalled()->willReturn([ - 'enum.gender' => $this->prophesize('Symfony\Component\DependencyInjection\Definition')->reveal(), - 'enum.type' => $this->prophesize('Symfony\Component\DependencyInjection\Definition')->reveal(), + 'enum.gender' => $this->prophesize(Definition::class)->reveal(), + 'enum.type' => $this->prophesize(Definition::class)->reveal(), ]); $this->compiler->process($compiler->reveal()); diff --git a/tests/DependencyInjection/EnumExtensionTest.php b/tests/DependencyInjection/EnumExtensionTest.php new file mode 100644 index 0000000..92f7270 --- /dev/null +++ b/tests/DependencyInjection/EnumExtensionTest.php @@ -0,0 +1,45 @@ + + */ +class EnumExtensionTest extends TestCase +{ + public function extension(): EnumExtension + { + return new EnumExtension(); + } + + /** + * @test + */ + public function it_register_services(): void + { + $container = new ContainerBuilder(); + $this->extension()->load([[]], $container); + + $services = [ + 'yokai_enum.form_type.enum_type', + 'yokai_enum.form_extension.enum_type_guesser', + 'yokai_enum.validator_constraints.enum_validator', + 'yokai_enum.twig_extension.enum_extension', + ]; + foreach ($services as $service) { + self::assertTrue($container->has($service), sprintf('Service "%s" is registered', $service)); + } + + $autoconfigure = $container->getAutoconfiguredInstanceof(); + $this->assertArrayHasKey(EnumInterface::class, $autoconfigure); + $this->assertEquals(['enum' => [[]]], $autoconfigure[EnumInterface::class]->getTags()); + + $this->assertTrue($container->hasAlias(EnumRegistry::class)); + } +} diff --git a/Tests/Registry/EnumRegistryTest.php b/tests/EnumRegistryTest.php similarity index 67% rename from Tests/Registry/EnumRegistryTest.php rename to tests/EnumRegistryTest.php index 3a0192c..98b11f5 100644 --- a/Tests/Registry/EnumRegistryTest.php +++ b/tests/EnumRegistryTest.php @@ -1,8 +1,12 @@ - */ -class EnumRegistryTest extends \PHPUnit_Framework_TestCase +class EnumRegistryTest extends TestCase { /** * @var EnumRegistry */ private $registry; - protected function setUp() + protected function setUp(): void { $this->registry = new EnumRegistry; } - protected function tearDown() + public function testAddDuplicatedException(): void { - unset($this->registry); - } - - public function testAddDuplicatedException() - { - $this->expectException('Yokai\EnumBundle\Exception\DuplicatedEnumException'); + $this->expectException(DuplicatedEnumException::class); $this->registry->add(new GenderEnum); $this->registry->add(new GenderEnum); } - public function testGetInvalidException() + public function testGetInvalidException(): void { - $this->expectException('Yokai\EnumBundle\Exception\InvalidEnumException'); + $this->expectException(InvalidEnumException::class); $this->registry->add(new GenderEnum); $this->registry->get('type'); } - public function testAddNominal() + public function testAddNominal(): void { - $translator = $this->prophesize('Symfony\Component\Translation\TranslatorInterface')->reveal(); + /** @var TranslatorInterface|ObjectProphecy $translator */ + $translator = $this->prophesize(TranslatorInterface::class)->reveal(); $gender = new GenderEnum; $state = new StateEnum($translator); $subscription = new SubscriptionEnum($translator); diff --git a/Tests/Fixtures/GenderEnum.php b/tests/Fixtures/GenderEnum.php similarity index 62% rename from Tests/Fixtures/GenderEnum.php rename to tests/Fixtures/GenderEnum.php index d56042a..ab787f1 100644 --- a/Tests/Fixtures/GenderEnum.php +++ b/tests/Fixtures/GenderEnum.php @@ -1,9 +1,9 @@ - @@ -12,7 +12,7 @@ class GenderEnum implements EnumInterface { use EnumWithClassAsNameTrait; - public function getChoices() + public function getChoices(): array { return ['male' => 'Male', 'female' => 'Female']; } diff --git a/Tests/Fixtures/StateEnum.php b/tests/Fixtures/StateEnum.php similarity index 66% rename from Tests/Fixtures/StateEnum.php rename to tests/Fixtures/StateEnum.php index 465fe19..93cec36 100644 --- a/Tests/Fixtures/StateEnum.php +++ b/tests/Fixtures/StateEnum.php @@ -1,9 +1,9 @@ - @@ -18,12 +18,12 @@ public function __construct(TranslatorInterface $translator) parent::__construct($translator, 'choice.state.%s'); } - protected function getValues() + protected function getValues(): array { return ['new', 'validated', 'disabled']; } - public function getName() + public function getName(): string { return 'state'; } diff --git a/Tests/Fixtures/SubscriptionEnum.php b/tests/Fixtures/SubscriptionEnum.php similarity index 77% rename from Tests/Fixtures/SubscriptionEnum.php rename to tests/Fixtures/SubscriptionEnum.php index d42128b..26f18ca 100644 --- a/Tests/Fixtures/SubscriptionEnum.php +++ b/tests/Fixtures/SubscriptionEnum.php @@ -1,9 +1,9 @@ - diff --git a/Tests/Fixtures/TypeEnum.php b/tests/Fixtures/TypeEnum.php similarity index 80% rename from Tests/Fixtures/TypeEnum.php rename to tests/Fixtures/TypeEnum.php index 426a14c..de4bf65 100644 --- a/Tests/Fixtures/TypeEnum.php +++ b/tests/Fixtures/TypeEnum.php @@ -1,8 +1,8 @@ - diff --git a/tests/Fixtures/Vehicle.php b/tests/Fixtures/Vehicle.php new file mode 100644 index 0000000..a8e27e5 --- /dev/null +++ b/tests/Fixtures/Vehicle.php @@ -0,0 +1,17 @@ +enumRegistry = $this->prophesize('Yokai\EnumBundle\Registry\EnumRegistryInterface'); + $this->enumRegistry = $this->prophesize(EnumRegistry::class); $this->enumRegistry->has('state')->willReturn(false); $this->enumRegistry->has(GenderEnum::class)->willReturn(true); $this->enumRegistry->get(GenderEnum::class)->willReturn(new GenderEnum); $this->metadata = new ClassMetadata(self::TEST_CLASS); $this->metadata->addPropertyConstraint(self::TEST_PROPERTY, new Enum(['enum' => GenderEnum::class])); - $this->metadataFactory = $this->prophesize('Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface'); + $this->metadataFactory = $this->prophesize(MetadataFactoryInterface::class); $this->metadataFactory->getMetadataFor(self::TEST_CLASS) ->willReturn($this->metadata); @@ -64,10 +66,10 @@ protected function setUp() parent::setUp(); } - public function testGuessType() + public function testGuessType(): void { $guess = new TypeGuess( - $this->getEnumType(), + EnumType::class, [ 'enum' => GenderEnum::class, 'multiple' => false, @@ -78,53 +80,34 @@ public function testGuessType() $this->assertEquals($guess, $this->guesser->guessType(self::TEST_CLASS, self::TEST_PROPERTY)); } - public function testGuessRequired() + public function testGuessRequired(): void { $this->assertNull($this->guesser->guessRequired(self::TEST_CLASS, self::TEST_PROPERTY)); } - public function testGuessMaxLength() + public function testGuessMaxLength(): void { $this->assertNull($this->guesser->guessMaxLength(self::TEST_CLASS, self::TEST_PROPERTY)); } - public function testGuessPattern() + public function testGuessPattern(): void { $this->assertNull($this->guesser->guessPattern(self::TEST_CLASS, self::TEST_PROPERTY)); } - public function testCreateForm() + public function testCreateForm(): void { $class = self::TEST_CLASS; - $form = $this->factory->create($this->getFormType(), new $class, ['data_class' => $class]) + $form = $this->factory->create(FormType::class, new $class, ['data_class' => $class]) ->add(self::TEST_PROPERTY); - $this->assertEquals(['Male' => 'male', 'Female' => 'female'], $form->get(self::TEST_PROPERTY)->getConfig()->getOption('choices')); - } - - protected function getFormType() - { - if (method_exists(AbstractType::class, 'getBlockPrefix')) { - $name = FormType::class; //Symfony 3.x support - } else { - $name = 'form'; //Symfony 2.x support - } - - return $name; - } - - protected function getEnumType() - { - if (method_exists(AbstractType::class, 'getBlockPrefix')) { - $name = EnumType::class; //Symfony 3.x support - } else { - $name = 'enum'; //Symfony 2.x support - } - - return $name; + $this->assertEquals( + ['Male' => 'male', 'Female' => 'female'], + $form->get(self::TEST_PROPERTY)->getConfig()->getOption('choices') + ); } - protected function getExtensions() + protected function getExtensions(): array { return [ new TestExtension($this->enumRegistry->reveal(), $this->metadataFactory->reveal()), diff --git a/Tests/Form/TestExtension.php b/tests/Form/TestExtension.php similarity index 73% rename from Tests/Form/TestExtension.php rename to tests/Form/TestExtension.php index 4b49be8..3b1d8ce 100644 --- a/Tests/Form/TestExtension.php +++ b/tests/Form/TestExtension.php @@ -1,12 +1,12 @@ - @@ -14,7 +14,7 @@ class TestExtension extends AbstractExtension { /** - * @var EnumRegistryInterface + * @var EnumRegistry */ private $enumRegistry; @@ -24,10 +24,10 @@ class TestExtension extends AbstractExtension private $metadataFactory; /** - * @param EnumRegistryInterface $enumRegistry + * @param EnumRegistry $enumRegistry * @param MetadataFactoryInterface|null $metadataFactory */ - public function __construct(EnumRegistryInterface $enumRegistry, MetadataFactoryInterface $metadataFactory = null) + public function __construct(EnumRegistry $enumRegistry, MetadataFactoryInterface $metadataFactory = null) { $this->enumRegistry = $enumRegistry; $this->metadataFactory = $metadataFactory; @@ -36,7 +36,7 @@ public function __construct(EnumRegistryInterface $enumRegistry, MetadataFactory /** * @inheritdoc */ - protected function loadTypes() + protected function loadTypes(): array { return [ new EnumType($this->enumRegistry), @@ -46,7 +46,7 @@ protected function loadTypes() /** * @inheritdoc */ - protected function loadTypeGuesser() + protected function loadTypeGuesser(): ?EnumTypeGuesser { if ($this->metadataFactory === null) { return null; diff --git a/Tests/Form/Type/EnumTypeTest.php b/tests/Form/Type/EnumTypeTest.php similarity index 55% rename from Tests/Form/Type/EnumTypeTest.php rename to tests/Form/Type/EnumTypeTest.php index c6a0399..acade77 100644 --- a/Tests/Form/Type/EnumTypeTest.php +++ b/tests/Form/Type/EnumTypeTest.php @@ -1,9 +1,13 @@ -enumRegistry = $this->prophesize('Yokai\EnumBundle\Registry\EnumRegistryInterface'); + $this->enumRegistry = $this->prophesize(EnumRegistry::class); $this->enumRegistry->has('state')->willReturn(false); $this->enumRegistry->has(GenderEnum::class)->willReturn(true); $this->enumRegistry->get(GenderEnum::class)->willReturn(new GenderEnum); @@ -25,45 +31,39 @@ protected function setUp() parent::setUp(); } - public function testEnumOptionIsRequired() + public function testEnumOptionIsRequired(): void { - $this->expectException('Symfony\Component\OptionsResolver\Exception\MissingOptionsException'); + $this->expectException(MissingOptionsException::class); $this->createForm(); } - public function testEnumOptionIsInvalid() + public function testEnumOptionIsInvalid(): void { - $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); + $this->expectException(InvalidOptionsException::class); $this->createForm('state'); } - public function testEnumOptionValid() + public function testEnumOptionValid(): void { $form = $this->createForm(GenderEnum::class); $this->assertEquals(['Male' => 'male', 'Female' => 'female'], $form->getConfig()->getOption('choices')); } - protected function getExtensions() + protected function getExtensions(): array { return [ new TestExtension($this->enumRegistry->reveal()) ]; } - private function createForm($enum = null) + private function createForm($enum = null): FormInterface { $options = []; if ($enum) { $options['enum'] = $enum; } - if (method_exists(AbstractType::class, 'getBlockPrefix')) { - $name = EnumType::class; //Symfony 3.x support - } else { - $name = 'enum'; //Symfony 2.x support - } - - return $this->factory->create($name, null, $options); + return $this->factory->create(EnumType::class, null, $options); } } diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..2a4f28c --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,14 @@ + + */ +abstract class TestCase extends PHPUnitTestCase +{ + use ProphecyTrait; +} diff --git a/Tests/Twig/Extension/EnumExtensionTest.php b/tests/Twig/Extension/EnumExtensionTest.php similarity index 64% rename from Tests/Twig/Extension/EnumExtensionTest.php rename to tests/Twig/Extension/EnumExtensionTest.php index fdb50b7..a46809e 100644 --- a/Tests/Twig/Extension/EnumExtensionTest.php +++ b/tests/Twig/Extension/EnumExtensionTest.php @@ -1,35 +1,33 @@ - */ -class EnumExtensionTest extends \PHPUnit_Framework_TestCase +class EnumExtensionTest extends TestCase { /** - * @var EnumRegistryInterface|\Prophecy\Prophecy\ObjectProphecy + * @var EnumRegistry|ObjectProphecy */ private $registry; - protected function setUp() + protected function setUp(): void { - $this->registry = $this->prophesize('Yokai\EnumBundle\Registry\EnumRegistryInterface'); + $this->registry = $this->prophesize(EnumRegistry::class); } - protected function tearDown() + public function testEnumLabel(): void { - unset( - $this->registry - ); - } - - public function testEnumLabel() - { - $enum = $this->prophesize('Yokai\EnumBundle\Enum\EnumInterface'); + $enum = $this->prophesize(EnumInterface::class); $enum->getChoices() ->willReturn(['foo' => 'FOO', 'bar' => 'BAR']); @@ -57,9 +55,9 @@ public function testEnumLabel() ); } - public function testEnumChoices() + public function testEnumChoices(): void { - $enum = $this->prophesize('Yokai\EnumBundle\Enum\EnumInterface'); + $enum = $this->prophesize(EnumInterface::class); $enum->getChoices() ->willReturn(['foo' => 'FOO', 'bar' => 'BAR']); @@ -75,12 +73,12 @@ public function testEnumChoices() } /** - * @return \Twig_Environment + * @return Environment */ - protected function createEnvironment() + protected function createEnvironment(): Environment { - $loader = new \Twig_Loader_Array([]); - $twig = new \Twig_Environment($loader, ['debug' => true, 'cache' => false, 'autoescape' => false]); + $loader = new ArrayLoader([]); + $twig = new Environment($loader, ['debug' => true, 'cache' => false, 'autoescape' => false]); $twig->addExtension($this->createExtension()); return $twig; @@ -89,7 +87,7 @@ protected function createEnvironment() /** * @return EnumExtension */ - private function createExtension() + private function createExtension(): EnumExtension { return new EnumExtension($this->registry->reveal()); } diff --git a/Tests/Validator/Constraints/EnumValidatorTest.php b/tests/Validator/Constraints/EnumValidatorTest.php similarity index 60% rename from Tests/Validator/Constraints/EnumValidatorTest.php rename to tests/Validator/Constraints/EnumValidatorTest.php index 3f660c8..451a05c 100644 --- a/Tests/Validator/Constraints/EnumValidatorTest.php +++ b/tests/Validator/Constraints/EnumValidatorTest.php @@ -1,9 +1,14 @@ - */ -class EnumValidatorTest extends AbstractConstraintValidatorTest +class EnumValidatorTest extends ConstraintValidatorTestCase { + use ProphecyTrait; - protected function createValidator() + protected function createValidator(): EnumValidator { - $registry = $this->prophesize('Yokai\EnumBundle\Registry\EnumRegistryInterface'); + /** @var EnumRegistry|ObjectProphecy $registry */ + $registry = $this->prophesize(EnumRegistry::class); $registry->has('state')->willReturn(false); $registry->has(GenderEnum::class)->willReturn(true); $registry->has('type')->willReturn(true); @@ -26,39 +33,39 @@ protected function createValidator() return new EnumValidator($registry->reveal()); } - public function testAcceptOnlyEnum() + public function testAcceptOnlyEnum(): void { - $this->expectException('Symfony\Component\Validator\Exception\UnexpectedTypeException'); + $this->expectException(UnexpectedTypeException::class); $this->validator->validate(null, new Choice); } - public function testEnumIsRequired() + public function testEnumIsRequired(): void { - $this->expectException('Symfony\Component\Validator\Exception\ConstraintDefinitionException'); + $this->expectException(ConstraintDefinitionException::class); $this->validator->validate('foo', new Enum); } - public function testValidEnumIsRequired() + public function testValidEnumIsRequired(): void { - $this->expectException('Symfony\Component\Validator\Exception\ConstraintDefinitionException'); + $this->expectException(ConstraintDefinitionException::class); $this->validator->validate('foo', new Enum('state')); } - public function testNullIsValid() + public function testNullIsValid(): void { $this->validator->validate(null, new Enum('type')); $this->assertNoViolation(); } - public function testValidSingleEnum() + public function testValidSingleEnum(): void { $this->validator->validate('customer', new Enum('type')); $this->assertNoViolation(); } - public function testInvalidSingleEnum() + public function testInvalidSingleEnum(): void { $constraint = new Enum(['enum' => 'type', 'message' => 'myMessage']); @@ -67,10 +74,11 @@ public function testInvalidSingleEnum() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"foo"') ->setCode(Choice::NO_SUCH_CHOICE_ERROR) + ->setParameter('{{ choices }}', '"customer", "prospect"') ->assertRaised(); } - public function testValidMultipleEnum() + public function testValidMultipleEnum(): void { $constraint = new Enum(['enum' => 'type', 'multiple' => true]); @@ -79,7 +87,7 @@ public function testValidMultipleEnum() $this->assertNoViolation(); } - public function testInvalidMultipleEnum() + public function testInvalidMultipleEnum(): void { $constraint = new Enum(['enum' => 'type', 'multiple' => true, 'multipleMessage' => 'myMessage']); @@ -89,7 +97,7 @@ public function testInvalidMultipleEnum() ->setParameter('{{ value }}', '"foo"') ->setInvalidValue('foo') ->setCode(Choice::NO_SUCH_CHOICE_ERROR) + ->setParameter('{{ choices }}', '"customer", "prospect"') ->assertRaised(); } - }