├── UPGRADE-2.1.md
├── tests
├── Fixtures
│ ├── FooInterface.php
│ ├── DummyEnum.php
│ ├── DummyWithSameValuesEnum.php
│ ├── FooEnum.php
│ └── AllEnum.php
├── Bridge
│ ├── Symfony
│ │ ├── Bundle
│ │ │ └── Greg0ireEnumBundleTest.php
│ │ ├── Validator
│ │ │ └── Constraint
│ │ │ │ ├── EnumTest.php
│ │ │ │ └── EnumValidatorTest.php
│ │ ├── Form
│ │ │ └── Type
│ │ │ │ └── EnumTypeTest.php
│ │ └── DependencyInjection
│ │ │ └── Greg0ireEnumExtensionTest.php
│ └── Twig
│ │ └── Extension
│ │ └── EnumExtensionTest.php
└── AbstractEnumTest.php
├── UPGRADE-2.0.md
├── UPGRADE-3.0.md
├── UPGRADE-4.0.md
├── src
├── Exception
│ ├── InvalidEnumName.php
│ └── InvalidEnumValue.php
├── Bridge
│ ├── Symfony
│ │ ├── Resources
│ │ │ └── config
│ │ │ │ ├── services.xml
│ │ │ │ └── twig.xml
│ │ ├── DependencyInjection
│ │ │ ├── Compiler
│ │ │ │ └── TranslatorCompilerPass.php
│ │ │ └── Greg0ireEnumExtension.php
│ │ ├── Bundle
│ │ │ └── Greg0ireEnumBundle.php
│ │ ├── Validator
│ │ │ └── Constraint
│ │ │ │ └── Enum.php
│ │ ├── Form
│ │ │ └── Type
│ │ │ │ └── EnumType.php
│ │ └── Translator
│ │ │ └── GetLabel.php
│ └── Twig
│ │ └── Extension
│ │ └── EnumExtension.php
└── AbstractEnum.php
├── UPGRADE-1.1.md
├── phpunit.xml.dist
├── CONTRIBUTING.md
├── composer.json
├── README.md
└── LICENSE
/UPGRADE-2.1.md:
--------------------------------------------------------------------------------
1 | Upgrade from 2.0 to 2.1
2 | =======================
3 |
4 | ### Enum class rename
5 |
6 | * `BaseEnum` is now deprecated in favor of `AbstractEnum`.
7 |
--------------------------------------------------------------------------------
/tests/Fixtures/FooInterface.php:
--------------------------------------------------------------------------------
1 | FooInterface::class,
13 | DummyEnum::class,
14 | ];
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/UPGRADE-3.0.md:
--------------------------------------------------------------------------------
1 | Upgrade from 2.x to 3.0
2 | =======================
3 |
4 | ## PHP support
5 |
6 | PHP 5.3, 5.4 and 5.5 versions are not supported anymore.
7 |
8 | ## Deprecations
9 |
10 | All the deprecated code introduced on 2.x is removed on 3.0.
11 |
12 | Please read 2.x upgrade guides for more information.
13 |
14 | See also the [diff code](https://github.com/greg0ire/enum/compare/v2.0.0...v3.0.0).
15 |
--------------------------------------------------------------------------------
/UPGRADE-4.0.md:
--------------------------------------------------------------------------------
1 | # Upgrade from 3.x to 4.0
2 |
3 | ## Dependencies
4 |
5 | Support for Symfony < 3.2 has been dropped.
6 | Support for Twig < 2.4.2 has been dropped.
7 | Support for PHP < 7.1 has been dropped.
8 |
9 | ## API
10 |
11 | Type hinting has been added to most classes.
12 |
13 | ### `Greg0ire\Enum\AbstractEnum`
14 |
15 | `getEnumTypes` may no longer return a string, it should always return an array
16 | now.
17 |
--------------------------------------------------------------------------------
/src/Exception/InvalidEnumName.php:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/Bridge/Symfony/Resources/config/twig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
16 |
17 | tests
18 |
19 |
20 |
21 |
22 | src
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/Bridge/Symfony/DependencyInjection/Compiler/TranslatorCompilerPass.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | final class TranslatorCompilerPass implements CompilerPassInterface
14 | {
15 | /**
16 | * {@inheritdoc}
17 | */
18 | public function process(ContainerBuilder $container): void
19 | {
20 | if (class_exists(AbstractExtension::class) && $container->hasDefinition('translator.default')) {
21 | $container->getDefinition('greg0ire_enum.symfony.translator.get_label')
22 | ->addArgument(new Reference('translator.default'));
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Bridge/Symfony/Bundle/Greg0ireEnumBundle.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | final class Greg0ireEnumBundle extends Bundle
14 | {
15 | /**
16 | * {@inheritdoc}
17 | */
18 | public function build(ContainerBuilder $container): void
19 | {
20 | parent::build($container);
21 |
22 | $container
23 | ->addCompilerPass(new TranslatorCompilerPass());
24 | }
25 |
26 | /**
27 | * {@inheritdoc}
28 | */
29 | protected function getContainerExtensionClass(): string
30 | {
31 | return Greg0ireEnumExtension::class;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Bridge/Symfony/DependencyInjection/Greg0ireEnumExtension.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | final class Greg0ireEnumExtension extends Extension
15 | {
16 | /**
17 | * {@inheritdoc}
18 | */
19 | public function load(array $configs, ContainerBuilder $container): void
20 | {
21 | $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
22 |
23 | if (class_exists(AbstractExtension::class)) {
24 | $loader->load('twig.xml');
25 | }
26 |
27 | $loader->load('services.xml');
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing
2 |
3 | Hi there! We're thrilled that you'd like to contribute to this project.
4 | Your help is essential for keeping it great.
5 |
6 | ## Submitting a pull request
7 |
8 | 1. [Fork](https://github.com/greg0ire/enum/fork) and clone the repository
9 | 2. Configure and install the dependencies: `composer install`
10 | 3. Make sure the tests pass on your machine via the section [Running the tests](#running-the-tests)
11 | 4. Create a new branch: `git checkout -b my-branch-name`
12 | 5. Make your change, add tests, and make sure the tests still pass
13 | 6. Push to your fork and [submit a pull request](https://github.com/greg0ire/enum/compare)
14 | 7. Pat yourself on the back and wait for your pull request to be reviewed and merged.
15 |
16 | ## Running the tests
17 |
18 | ### Prerequisites
19 |
20 | Install the development dependencies via [Composer](https://getcomposer.org/):
21 |
22 | ```bash
23 | composer install
24 | ```
25 |
26 | Then run:
27 |
28 | ```bash
29 | php vendor/bin/simple-phpunit
30 | ```
31 |
--------------------------------------------------------------------------------
/tests/Bridge/Symfony/Bundle/Greg0ireEnumBundleTest.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | final class Greg0ireEnumBundleTest extends AbstractContainerBuilderTestCase
13 | {
14 | /**
15 | * @var Greg0ireEnumBundle
16 | */
17 | private $bundle;
18 |
19 | /**
20 | * {@inheritdoc}
21 | */
22 | protected function setUp(): void
23 | {
24 | parent::setUp();
25 |
26 | $this->bundle = new Greg0ireEnumBundle();
27 | }
28 |
29 | public function testBuild()
30 | {
31 | $this->assertNull($this->bundle->build($this->container));
32 | }
33 |
34 | public function testGetContainerExtension()
35 | {
36 | $this->assertInstanceOf(Greg0ireEnumExtension::class, $this->bundle->getContainerExtension());
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tests/Bridge/Symfony/Validator/Constraint/EnumTest.php:
--------------------------------------------------------------------------------
1 | assertSame($enumClass, $enumConstraint->class);
23 | }
24 |
25 | public function getValidEnums()
26 | {
27 | return [
28 | [AllEnum::class],
29 | [DummyEnum::class],
30 | [FooEnum::class],
31 | ];
32 | }
33 |
34 | /**
35 | * @dataProvider getInvalidEnums
36 | */
37 | public function testInvalidEnumClasses($enumClass)
38 | {
39 | $this->expectException(ConstraintDefinitionException::class);
40 |
41 | new Enum($enumClass);
42 | }
43 |
44 | public function getInvalidEnums()
45 | {
46 | return [
47 | [FooInterface::class],
48 | [\stdClass::class],
49 | ['This\Does\Not\Exist\At\All'],
50 | ];
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Bridge/Symfony/Validator/Constraint/Enum.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | final class Enum extends Choice
18 | {
19 | /**
20 | * @var string
21 | */
22 | public $class;
23 |
24 | /**
25 | * @var bool
26 | */
27 | public $showKeys = false;
28 |
29 | /**
30 | * {@inheritdoc}
31 | */
32 | public function __construct($options = null)
33 | {
34 | parent::__construct($options);
35 |
36 | if (!is_a($this->class, AbstractEnum::class, true)) {
37 | throw new ConstraintDefinitionException(
38 | 'The option "class" must be a class that inherits from '.AbstractEnum::class
39 | );
40 | }
41 | $this->choices = call_user_func([$this->class, 'getConstants']);
42 |
43 | if ($this->showKeys) {
44 | $keysMessage = 'Valid '.$this->class.' constant keys are: {{ choices }}';
45 | $this->message .= ' '.$keysMessage;
46 | $this->multipleMessage .= ' '.$keysMessage;
47 | }
48 | }
49 |
50 | /**
51 | * {@inheritdoc}
52 | */
53 | public function validatedBy(): string
54 | {
55 | return ChoiceValidator::class;
56 | }
57 |
58 | /**
59 | * {@inheritdoc}
60 | */
61 | public function getDefaultOption(): string
62 | {
63 | return 'class';
64 | }
65 |
66 | /**
67 | * {@inheritdoc}
68 | */
69 | public function getRequiredOptions(): array
70 | {
71 | return ['class'];
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Bridge/Symfony/Form/Type/EnumType.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | final class EnumType extends AbstractType
16 | {
17 | /**
18 | * {@inheritdoc}
19 | */
20 | public function configureOptions(OptionsResolver $resolver): void
21 | {
22 | $resolver->setRequired('class');
23 | $resolver->setAllowedTypes('class', 'string');
24 | $resolver->setNormalizer('class', function (Options $options, string $class): string {
25 | if (!is_a($class, AbstractEnum::class, true)) {
26 | throw new LogicException('The option "class" must be a class that inherits from '.AbstractEnum::class);
27 | }
28 |
29 | return $class;
30 | });
31 |
32 | $resolver->setDefault('prefix_label_with_class', false);
33 | $resolver->setAllowedTypes('prefix_label_with_class', 'bool');
34 |
35 | $resolver->setDefault('choices', function (Options $options): array {
36 | $class = $options['class'];
37 |
38 | $keys = $options['prefix_label_with_class']
39 | ? call_user_func([$class, 'getClassPrefixedKeys'], 'strtolower')
40 | : call_user_func([$class, 'getKeys'], 'strtolower');
41 |
42 | return array_combine($keys, call_user_func([$class, 'getConstants']));
43 | });
44 | }
45 |
46 | /**
47 | * {@inheritdoc}
48 | */
49 | public function getBlockPrefix(): string
50 | {
51 | return 'enum';
52 | }
53 |
54 | /**
55 | * {@inheritdoc}
56 | */
57 | public function getParent(): string
58 | {
59 | return ChoiceType::class;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "greg0ire/enum",
3 | "type": "library",
4 | "description": "work around the missing enum type in php",
5 | "keywords": [
6 | "enum",
7 | "enumeration",
8 | "Symfony",
9 | "validator",
10 | "form"
11 | ],
12 | "license": "CC-BY-SA-3.0",
13 | "authors": [
14 | {
15 | "name": "Grégoire Paris",
16 | "email": "postmaster@greg0ire.fr"
17 | },
18 | {
19 | "name": "Sullivan Sénéchal",
20 | "email": "soullivaneuh@gmail.com"
21 | }
22 | ],
23 | "require": {
24 | "php": "^7.1 || ^8.0",
25 | "doctrine/inflector": "^1.4 || ^2.0"
26 | },
27 | "conflict": {
28 | "symfony/form": "<3.2 || >=7.0 <999",
29 | "symfony/validator": "<3.2 || >=7.0 <999"
30 | },
31 | "require-dev": {
32 | "matthiasnoback/symfony-dependency-injection-test": "^4.0",
33 | "symfony/config": "^4.2 || ^5.0 || ^6.0",
34 | "symfony/dependency-injection": "^4.2 || ^5.0 || ^6.0",
35 | "symfony/form": "^4.2.5 || ^5.0 || ^6.0",
36 | "symfony/framework-bundle": "^4.2 || ^5.0 || ^6.0",
37 | "symfony/http-kernel": "^4.2 || ^5.1.5 || ^6.0",
38 | "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0",
39 | "symfony/phpunit-bridge": "^5.0 || ^6.0",
40 | "symfony/translation": "^4.2 || ^5.0 || ^6.0",
41 | "symfony/translation-contracts": "^2.0",
42 | "symfony/twig-bundle": "^4.2 || ^5.0 || ^6.0",
43 | "symfony/validator": "^4.3.6 || ^5.0 || ^6.0",
44 | "twig/twig": "^2.4.2 || ^3.0"
45 | },
46 | "suggest": {
47 | "symfony/form": "To use enum form type",
48 | "symfony/validator": "To use enum validator"
49 | },
50 | "config": {
51 | "sort-packages": true
52 | },
53 | "extra": {
54 | "branch-alias": {
55 | "dev-legacy-stable": "3.x-dev",
56 | "dev-stable": "4.x-dev",
57 | "dev-unstable": "5.x-dev"
58 | }
59 | },
60 | "autoload": {
61 | "psr-4": {
62 | "Greg0ire\\Enum\\": "src"
63 | }
64 | },
65 | "autoload-dev": {
66 | "psr-4": {
67 | "Greg0ire\\Enum\\Tests\\": "tests"
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/tests/Bridge/Symfony/Form/Type/EnumTypeTest.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | final class EnumTypeTest extends TypeTestCase
15 | {
16 | public function testEnumChoices()
17 | {
18 | $view = $this->factory->create(EnumType::class, null, [
19 | 'class' => DummyEnum::class,
20 | ])->createView();
21 |
22 | $this->assertSame('42', $view->vars['choices'][0]->value);
23 | $this->assertSame('some_value', $view->vars['choices'][1]->value);
24 | $this->assertFalse($view->vars['is_selected']($view->vars['choices'][0], $view->vars['value']));
25 | $this->assertFalse($view->vars['is_selected']($view->vars['choices'][1], $view->vars['value']));
26 | $this->assertSame('first', $view->vars['choices'][0]->label);
27 | $this->assertSame('second', $view->vars['choices'][1]->label);
28 | }
29 |
30 | public function testEnumChoicesClassPrefix()
31 | {
32 | $view = $this->factory->create(EnumType::class, null, [
33 | 'class' => DummyEnum::class,
34 | 'prefix_label_with_class' => true,
35 | ])->createView();
36 |
37 | $this->assertSame('greg0ire_enum_tests_fixtures_dummy_enum_first', $view->vars['choices'][0]->label);
38 | $this->assertSame('greg0ire_enum_tests_fixtures_dummy_enum_second', $view->vars['choices'][1]->label);
39 | }
40 |
41 | /**
42 | * @dataProvider getInvalidEnums
43 | *
44 | * @param string $class
45 | */
46 | public function testInvalidEnums($class)
47 | {
48 | $this->expectException(
49 | LogicException::class,
50 | 'The option "class" must be a class that inherits from Greg0ire\Enum\AbstractEnum'
51 | );
52 |
53 | $this->factory->create(EnumType::class, null, [
54 | 'class' => $class,
55 | ]);
56 |
57 | $this->builder->getForm();
58 | }
59 |
60 | public function getInvalidEnums()
61 | {
62 | return [
63 | [FooInterface::class],
64 | [\stdClass::class],
65 | ['This\Does\Not\Exist\At\All'],
66 | ];
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Bridge/Symfony/Translator/GetLabel.php:
--------------------------------------------------------------------------------
1 | translator = $translator;
17 | }
18 |
19 | /**
20 | * Displays the label corresponding to a specific value of an enumeration.
21 | *
22 | * @param mixed $value Must exists in the enumeration class specified with $class
23 | * @param string $class The enum class name
24 | * @param string|bool $translationDomain the translation domain to use if the translator if available.
25 | * string: Use the specified one
26 | * null: Use the default one
27 | * false: Do not use the translator
28 | * @param bool $classPrefixed Prefix the label with the enum class. Defaults to true if the translator
29 | * is available and enabled, false otherwise.
30 | * @param string $namespaceSeparator namespace separator to use with the class prefix.
31 | * This takes effect only if $classPrefixed is true
32 | */
33 | public function __invoke(
34 | $value,
35 | string $class,
36 | $translationDomain = null,
37 | ?bool $classPrefixed = null,
38 | ?string $namespaceSeparator = null
39 | ): string {
40 | // Determine if the translator can be used or not.
41 | $useTranslation = $this->translator instanceof TranslatorInterface
42 | && (is_null($translationDomain) || is_string($translationDomain));
43 |
44 | // If not defined, guess the default behavior.
45 | if (is_null($classPrefixed)) {
46 | $classPrefixed = $useTranslation;
47 | }
48 |
49 | $label = array_search(
50 | $value,
51 | call_user_func([$class, 'getConstants'], 'strtolower', $classPrefixed, $namespaceSeparator)
52 | );
53 |
54 | if ($useTranslation) {
55 | $translatedLabel = $this->translator->trans($label, [], $translationDomain);
56 |
57 | return $translatedLabel ?: $label;
58 | }
59 |
60 | return $label;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/tests/Bridge/Symfony/DependencyInjection/Greg0ireEnumExtensionTest.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | final class Greg0ireEnumExtensionTest extends AbstractExtensionTestCase
16 | {
17 | protected $frameworkExtension;
18 |
19 | protected function setUp(): void
20 | {
21 | parent::setUp();
22 | $this->setParameter('kernel.debug', true);
23 | $this->setParameter('kernel.root_dir', sys_get_temp_dir());
24 | $this->setParameter('kernel.project_dir', sys_get_temp_dir());
25 | $this->setParameter('kernel.build_dir', sys_get_temp_dir());
26 | $this->setParameter('kernel.bundles_metadata', []);
27 | $this->setParameter('kernel.container_class', Container::class);
28 |
29 | // needed for legacy versions of symfony
30 | $this->setParameter('kernel.bundles', []);
31 | $this->setParameter('kernel.cache_dir', sys_get_temp_dir());
32 |
33 | $this->container->registerExtension($this->frameworkExtension = new FrameworkExtension());
34 | }
35 |
36 | public function testLoad()
37 | {
38 | $this->frameworkExtension->load(
39 | ['framework' => ['translator' => ['fallbacks' => ['en']]]],
40 | $this->container
41 | );
42 | $this->load();
43 |
44 | $this->assertContainerBuilderHasService(
45 | 'greg0ire_enum.twig.extension.enum',
46 | EnumExtension::class
47 | );
48 | }
49 |
50 | public function testLoadWithoutATranslator()
51 | {
52 | $this->frameworkExtension->load(
53 | ['framework' => ['translator' => ['enabled' => false]]],
54 | $this->container
55 | );
56 | $this->load();
57 |
58 | $this->assertContainerBuilderHasService(
59 | 'greg0ire_enum.twig.extension.enum',
60 | EnumExtension::class
61 | );
62 | }
63 |
64 | public function testLabelServiceHasTheTranslator()
65 | {
66 | $this->registerService('translator.logging.inner', \stdClass::class);
67 | $this->registerService('logger', \stdClass::class);
68 | $this->frameworkExtension->load(
69 | ['framework' => ['translator' => ['fallbacks' => ['en']]]],
70 | $this->container
71 | );
72 | $this->load();
73 | $this->compile();
74 | $compilerPass = new TranslatorCompilerPass();
75 | $compilerPass->process($this->container);
76 |
77 | $this->assertContainerBuilderHasServiceDefinitionWithArgument(
78 | 'greg0ire_enum.symfony.translator.get_label',
79 | 0,
80 | new Reference('translator.default')
81 | );
82 | }
83 |
84 | /**
85 | * {@inheritdoc}
86 | */
87 | protected function getContainerExtensions(): array
88 | {
89 | return [new Greg0ireEnumExtension()];
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/tests/Bridge/Symfony/Validator/Constraint/EnumValidatorTest.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class EnumValidatorTest extends ConstraintValidatorTestCase
17 | {
18 | public function testNullIsValid()
19 | {
20 | $this->validator->validate(null, new Enum([
21 | 'strict' => true,
22 | 'class' => DummyEnum::class,
23 | ]));
24 |
25 | $this->assertNoViolation();
26 | }
27 |
28 | public function testBlankButStringIsInvalid()
29 | {
30 | $this->validator->validate(' ', new Enum([
31 | 'strict' => true,
32 | 'class' => DummyEnum::class,
33 | ]));
34 |
35 | $this->buildViolation('The value you selected is not a valid choice.')
36 | ->setParameter('{{ value }}', '" "')
37 | ->setParameter('{{ choices }}', '42, "some_value"')
38 | ->setCode(Choice::NO_SUCH_CHOICE_ERROR)
39 | ->assertRaised();
40 | }
41 |
42 | public function testValidValues()
43 | {
44 | $this->validator->validate(DummyEnum::FIRST, new Enum([
45 | 'strict' => true,
46 | 'class' => DummyEnum::class,
47 | ]));
48 | $this->validator->validate(DummyEnum::SECOND, new Enum([
49 | 'strict' => true,
50 | 'class' => DummyEnum::class,
51 | ]));
52 |
53 | foreach (FooEnum::getConstants() as $value) {
54 | $this->validator->validate($value, new Enum([
55 | 'strict' => true,
56 | 'class' => FooEnum::class,
57 | ]));
58 | }
59 |
60 | foreach (AllEnum::getConstants() as $value) {
61 | $this->validator->validate($value, new Enum([
62 | 'strict' => true,
63 | 'class' => AllEnum::class,
64 | ]));
65 | }
66 |
67 | $this->assertNoViolation();
68 | }
69 |
70 | public function testInvalidValue()
71 | {
72 | $this->validator->validate(1337, new Enum([
73 | 'strict' => true,
74 | 'class' => DummyEnum::class,
75 | ]));
76 |
77 | $this->buildViolation('The value you selected is not a valid choice.')
78 | ->setParameter('{{ value }}', '1337')
79 | ->setParameter('{{ choices }}', '42, "some_value"')
80 | ->setCode(Choice::NO_SUCH_CHOICE_ERROR)
81 | ->assertRaised();
82 | }
83 |
84 | public function testInvalidValueWithShowKeys()
85 | {
86 | $this->validator->validate(1337, new Enum([
87 | 'strict' => true,
88 | 'class' => DummyEnum::class,
89 | 'showKeys' => true,
90 | ]));
91 |
92 | $this->buildViolation('The value you selected is not a valid choice. '
93 | .'Valid '.DummyEnum::class.' constant keys are: {{ choices }}')
94 | ->setParameter('{{ value }}', '1337')
95 | ->setParameter('{{ choices }}', '42, "some_value"')
96 | ->setCode(Choice::NO_SUCH_CHOICE_ERROR)
97 | ->assertRaised();
98 | }
99 |
100 | /**
101 | * {@inheritdoc}
102 | */
103 | protected function createValidator()
104 | {
105 | return new ChoiceValidator();
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/tests/Bridge/Twig/Extension/EnumExtensionTest.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | final class EnumExtensionTest extends TestCase
19 | {
20 | /**
21 | * @var TranslatorInterface|\PHPUnit_Framework_MockObject_MockObject
22 | */
23 | private $translator;
24 |
25 | /**
26 | * @var EnumExtension
27 | */
28 | private $extension;
29 |
30 | /**
31 | * {@inheritdoc}
32 | */
33 | protected function setUp(): void
34 | {
35 | $this->translator = $this->createMock(TranslatorInterface::class);
36 | $this->extension = new EnumExtension(new GetLabel($this->translator));
37 | }
38 |
39 | public function testEnvironment()
40 | {
41 | $twig = new Environment($this->createMock(LoaderInterface::class));
42 | $twig->addExtension($this->extension);
43 |
44 | $this->assertInstanceOf(TwigFilter::class, $twig->getFilter('enum_label'));
45 |
46 | $this->assertTrue($twig->hasExtension(EnumExtension::class));
47 | }
48 |
49 | /**
50 | * @dataProvider getLabels
51 | */
52 | public function testLabel($value, $class, $classPrefix, $separator, $expectedResult)
53 | {
54 | $this->assertSame(
55 | $expectedResult,
56 | $this->extension->label($value, $class, false, $classPrefix, $separator)
57 | );
58 | }
59 |
60 | public function getLabels()
61 | {
62 | return [
63 | [FooInterface::CHUCK, FooEnum::class, false, null, 'chuck'],
64 | [FooInterface::CHUCK, FooEnum::class, true, null, 'greg0ire_enum_tests_fixtures_foo_enum_chuck'],
65 | [FooInterface::CHUCK, FooEnum::class, true, '.', 'greg0ire.enum.tests.fixtures.foo_enum.chuck'],
66 | ];
67 | }
68 |
69 | public function testLabelWithTranslator()
70 | {
71 | $this->translator->expects($this->once())
72 | ->method('trans')->with('greg0ire_enum_tests_fixtures_foo_enum_chuck', [], 'test');
73 |
74 | $this->assertSame(
75 | 'greg0ire_enum_tests_fixtures_foo_enum_chuck',
76 | $this->extension->label(FooInterface::CHUCK, FooEnum::class, 'test'),
77 | 'Without any available translation, the filter should just return the key.'
78 | );
79 | }
80 |
81 | public function testGetConstants()
82 | {
83 | $this->assertSame(
84 | [
85 | 'GOD' => 'Dieu',
86 | 'CHUCK' => 'Chuck Norris',
87 | 'GUITRY' => 'Sacha Guitry',
88 | ],
89 | $this->extension->getConstants(FooEnum::class)
90 | );
91 | }
92 |
93 | public function testGetKeys()
94 | {
95 | $this->assertSame(
96 | [
97 | 'GOD',
98 | 'CHUCK',
99 | 'GUITRY',
100 | ],
101 | $this->extension->getKeys(FooEnum::class)
102 | );
103 | }
104 |
105 | public function testGetClassPrefixedKeys()
106 | {
107 | $this->assertSame(
108 | [
109 | 'greg0ire_enum_tests_fixtures_foo_enum_GOD',
110 | 'greg0ire_enum_tests_fixtures_foo_enum_CHUCK',
111 | 'greg0ire_enum_tests_fixtures_foo_enum_GUITRY',
112 | ],
113 | $this->extension->getClassPrefixedKeys(FooEnum::class)
114 | );
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/Bridge/Twig/Extension/EnumExtension.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | final class EnumExtension extends AbstractExtension
16 | {
17 | /**
18 | * @var GetLabel
19 | */
20 | private $label;
21 |
22 | public function __construct($label)
23 | {
24 | if ($label instanceof TranslatorInterface) {
25 | @trigger_error(sprintf(
26 | 'Providing a %s instance to %s is deprecated and will not be supported in 5.0. Please provide a %s instance instead.',
27 | TranslatorInterface::class,
28 | __METHOD__,
29 | GetLabel::class
30 | ), E_USER_DEPRECATED);
31 | $this->label = new GetLabel($label);
32 |
33 | return;
34 | }
35 | $this->label = $label;
36 | }
37 |
38 | /**
39 | * {@inheritdoc}
40 | */
41 | public function getFilters(): array
42 | {
43 | return [new TwigFilter('enum_label', [$this, 'label'])];
44 | }
45 |
46 | /**
47 | * {@inheritdoc}
48 | */
49 | public function getFunctions(): array
50 | {
51 | return [
52 | new TwigFunction('enum_get_constants', [$this, 'getConstants']),
53 | new TwigFunction('enum_get_keys', [$this, 'getKeys']),
54 | new TwigFunction('enum_get_class_prefixed_keys', [$this, 'getClassPrefixedKeys']),
55 | ];
56 | }
57 |
58 | /**
59 | * Displays the label corresponding to a specific value of an enumeration.
60 | *
61 | * @param mixed $value Must exists in the enumeration class specified with $class
62 | * @param string $class The enum class name
63 | * @param string|bool $translationDomain the translation domain to use if the translator if available.
64 | * string: Use the specified one
65 | * null: Use the default one
66 | * false: Do not use the translator
67 | * @param bool $classPrefixed Prefix the label with the enum class. Defaults to true if the translator
68 | * is available and enabled, false otherwise.
69 | * @param string $namespaceSeparator namespace separator to use with the class prefix.
70 | * This takes effect only if $classPrefixed is true
71 | */
72 | public function label(
73 | $value,
74 | string $class,
75 | $translationDomain = null,
76 | ?bool $classPrefixed = null,
77 | ?string $namespaceSeparator = null
78 | ): string {
79 | return ($this->label)($value, $class, $translationDomain, $classPrefixed, $namespaceSeparator);
80 | }
81 |
82 | /**
83 | * @see AbstractEnum::getConstants()
84 | */
85 | public function getConstants(
86 | string $class,
87 | ?callable $keysCallback = null,
88 | ?bool $classPrefixed = false,
89 | ?string $namespaceSeparator = null
90 | ): array {
91 | return call_user_func([$class, 'getConstants'], $keysCallback, $classPrefixed, $namespaceSeparator);
92 | }
93 |
94 | /**
95 | * @see AbstractEnum::getKeys()
96 | */
97 | public function getKeys(string $class, ?callable $callback = null)
98 | {
99 | return call_user_func([$class, 'getKeys'], $callback);
100 | }
101 |
102 | /**
103 | * @see AbstractEnum::getClassPrefixedKeys()
104 | */
105 | public function getClassPrefixedKeys(
106 | string $class,
107 | ?callable $callback = null,
108 | ?string $namespaceSeparator = null
109 | ): array {
110 | return call_user_func([$class, 'getClassPrefixedKeys'], $callback, $namespaceSeparator);
111 | }
112 |
113 | /**
114 | * {@inheritdoc}
115 | */
116 | public function getName(): string
117 | {
118 | return 'greg0ire_enum';
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/tests/AbstractEnumTest.php:
--------------------------------------------------------------------------------
1 | assertSame(
18 | [
19 | 'FIRST' => 42,
20 | 'SECOND' => 'some_value',
21 | ],
22 | DummyEnum::getConstants()
23 | );
24 | }
25 |
26 | public function testFooGetConstants()
27 | {
28 | $this->assertSame(
29 | [
30 | 'GOD' => 'Dieu',
31 | 'CHUCK' => 'Chuck Norris',
32 | 'GUITRY' => 'Sacha Guitry',
33 | ],
34 | FooEnum::getConstants()
35 | );
36 |
37 | $this->assertSame(
38 | [
39 | 'god' => 'Dieu',
40 | 'chuck' => 'Chuck Norris',
41 | 'guitry' => 'Sacha Guitry',
42 | ],
43 | FooEnum::getConstants('strtolower')
44 | );
45 |
46 | $this->assertSame(
47 | [
48 | 'greg0ire.enum.tests.fixtures.foo_enum.god' => 'Dieu',
49 | 'greg0ire.enum.tests.fixtures.foo_enum.chuck' => 'Chuck Norris',
50 | 'greg0ire.enum.tests.fixtures.foo_enum.guitry' => 'Sacha Guitry',
51 | ],
52 | FooEnum::getConstants('strtolower', true, '.')
53 | );
54 | }
55 |
56 | public function testAllGetConstants()
57 | {
58 | $this->assertSame(
59 | [
60 | 'originally.GOD' => 'Dieu',
61 | 'originally.CHUCK' => 'Chuck Norris',
62 | 'originally.GUITRY' => 'Sacha Guitry',
63 | 'Greg0ire\Enum\Tests\Fixtures\DummyEnum::FIRST' => 42,
64 | 'Greg0ire\Enum\Tests\Fixtures\DummyEnum::SECOND' => 'some_value',
65 | ],
66 | AllEnum::getConstants()
67 | );
68 |
69 | $this->assertSame(
70 | [
71 | 'originally.god' => 'Dieu',
72 | 'originally.chuck' => 'Chuck Norris',
73 | 'originally.guitry' => 'Sacha Guitry',
74 | 'greg0ire\enum\tests\fixtures\dummyenum::first' => 42,
75 | 'greg0ire\enum\tests\fixtures\dummyenum::second' => 'some_value',
76 | ],
77 | AllEnum::getConstants('strtolower')
78 | );
79 | }
80 |
81 | public function testFooGetKeys()
82 | {
83 | $this->assertSame(
84 | [
85 | 'GOD',
86 | 'CHUCK',
87 | 'GUITRY',
88 | ],
89 | FooEnum::getKeys()
90 | );
91 |
92 | $this->assertSame(
93 | [
94 | 'god',
95 | 'chuck',
96 | 'guitry',
97 | ],
98 | FooEnum::getKeys('strtolower')
99 | );
100 | }
101 |
102 | public function testFooGetClassPrefixedKeys()
103 | {
104 | $this->assertSame(
105 | [
106 | 'greg0ire_enum_tests_fixtures_foo_enum_GOD',
107 | 'greg0ire_enum_tests_fixtures_foo_enum_CHUCK',
108 | 'greg0ire_enum_tests_fixtures_foo_enum_GUITRY',
109 | ],
110 | FooEnum::getClassPrefixedKeys()
111 | );
112 |
113 | $this->assertSame(
114 | [
115 | 'greg0ire_enum_tests_fixtures_foo_enum_god',
116 | 'greg0ire_enum_tests_fixtures_foo_enum_chuck',
117 | 'greg0ire_enum_tests_fixtures_foo_enum_guitry',
118 | ],
119 | FooEnum::getClassPrefixedKeys('strtolower')
120 | );
121 |
122 | $this->assertSame(
123 | [
124 | 'greg0ire.enum.tests.fixtures.foo_enum.god',
125 | 'greg0ire.enum.tests.fixtures.foo_enum.chuck',
126 | 'greg0ire.enum.tests.fixtures.foo_enum.guitry',
127 | ],
128 | FooEnum::getClassPrefixedKeys('strtolower', '.')
129 | );
130 | }
131 |
132 | public function testsIsValidName()
133 | {
134 | $this->assertFalse(DummyEnum::isValidName('fiRsT'));
135 | $this->assertFalse(DummyEnum::isValidName('invalid'));
136 | }
137 |
138 | public function testAssertValidName()
139 | {
140 | $this->expectException(InvalidEnumName::class);
141 | $this->expectExceptionMessage(
142 | '"fiRsT" is not a valid name, valid names are: ("FIRST", "SECOND")'
143 | );
144 | DummyEnum::assertValidName('fiRsT');
145 | }
146 |
147 | public function testIsValidValue()
148 | {
149 | $this->assertTrue(DummyEnum::isValidValue(42));
150 | $this->assertFalse(DummyEnum::isValidValue('42'));
151 | }
152 |
153 | public function testAssertValidValue()
154 | {
155 | $this->expectException(InvalidEnumValue::class);
156 | $this->expectExceptionMessage(
157 | '"test" is not a valid value, valid values are: ("42", "some_value")'
158 | );
159 | DummyEnum::assertValidValue('test');
160 | }
161 |
162 | public function testSimpleKeyFromValue()
163 | {
164 | $this->assertSame('FIRST', DummyEnum::getKeysFromValue(42));
165 | }
166 |
167 | public function testMultiKeysFromValue()
168 | {
169 | $this->assertSame(['FIRST', 'SECOND'], DummyWithSameValuesEnum::getKeysFromValue(42));
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/src/AbstractEnum.php:
--------------------------------------------------------------------------------
1 |
12 | * @author Sullivan Senechal
13 | */
14 | abstract class AbstractEnum
15 | {
16 | /**
17 | * @var string
18 | */
19 | public static $defaultNamespaceSeparator = '_';
20 |
21 | private static $constCache = [];
22 |
23 | /**
24 | * @var Inflector
25 | */
26 | private static $inflector;
27 |
28 | /**
29 | * Uses reflection to find the constants defined in the class and cache
30 | * them in a local property for performance, before returning them.
31 | *
32 | * @param bool $classPrefixed true if you want the enum class prefix on each keys, false otherwise
33 | * @param string $namespaceSeparator only relevant if $classPrefixed is set to true
34 | *
35 | * @return array a hash with your constants and their value. Useful for
36 | * building a choice widget
37 | */
38 | final public static function getConstants(
39 | ?callable $keysCallback = null,
40 | bool $classPrefixed = false,
41 | string $namespaceSeparator = null
42 | ): array {
43 | $namespaceSeparator = $namespaceSeparator ?: static::$defaultNamespaceSeparator;
44 | $enumTypes = static::getEnumTypes();
45 | $enums = [];
46 |
47 | foreach ($enumTypes as $key => $enumType) {
48 | $cacheKey = is_int($key) ? $enumType : $key;
49 |
50 | if (!isset(self::$constCache[$cacheKey])) {
51 | $reflect = new \ReflectionClass($enumType);
52 | self::$constCache[$cacheKey] = $reflect->getConstants();
53 | }
54 | if (count($enumTypes) > 1) {
55 | foreach (self::$constCache[$cacheKey] as $subKey => $value) {
56 | $subKey = $cacheKey.(is_int($key) ? '::' : '.').$subKey;
57 | $enums[$subKey] = $value;
58 | }
59 | } else {
60 | $enums = self::$constCache[$cacheKey];
61 | }
62 | }
63 |
64 | if (is_callable($keysCallback) || $classPrefixed) {
65 | return array_combine(
66 | $classPrefixed
67 | ? static::getClassPrefixedKeys($keysCallback, $namespaceSeparator)
68 | : static::getKeys($keysCallback),
69 | $enums
70 | );
71 | }
72 |
73 | return $enums;
74 | }
75 |
76 | /**
77 | * Returns constants keys.
78 | *
79 | * @param callable|null $callback A callable function compatible with array_map
80 | *
81 | * @return string[]
82 | */
83 | final public static function getKeys($callback = null): array
84 | {
85 | $keys = array_keys(static::getConstants());
86 |
87 | if ($callback !== null) {
88 | return array_map($callback, $keys);
89 | }
90 |
91 | return $keys;
92 | }
93 |
94 | /**
95 | * @param callable|null $callback A callable function compatible with array_map
96 | * @param string|null $namespaceSeparator Choose which character should replace namespaces separation.
97 | * Example: With Foo\BarMagic enum class with '.' separator,
98 | * it will be converted to foo.bar_magic.YOUR_KEY
99 | *
100 | * @return string[]
101 | */
102 | final public static function getClassPrefixedKeys(
103 | ?callable $callback = null,
104 | ?string $namespaceSeparator = null
105 | ): array {
106 | $namespaceSeparator = $namespaceSeparator ?: static::$defaultNamespaceSeparator;
107 | $classKey = str_replace(
108 | '\\',
109 | $namespaceSeparator,
110 | self::inflector()->tableize(static::class)
111 | );
112 |
113 | $keys = static::getKeys(function ($key) use ($namespaceSeparator, $classKey) {
114 | return $classKey.$namespaceSeparator.$key;
115 | });
116 |
117 | if (is_callable($callback)) {
118 | return array_map($callback, $keys);
119 | }
120 |
121 | return $keys;
122 | }
123 |
124 | private static function inflector(): Inflector
125 | {
126 | if (!isset(self::$inflector)) {
127 | self::$inflector = InflectorFactory::create()->build();
128 | }
129 |
130 | return self::$inflector;
131 | }
132 |
133 | /**
134 | * Checks whether a constant with this name is defined.
135 | */
136 | final public static function isValidName(string $name): bool
137 | {
138 | return array_key_exists($name, self::getConstants());
139 | }
140 |
141 | /**
142 | * Asserts a constant with this name is defined.
143 | *
144 | * @throws InvalidEnumName
145 | */
146 | final public static function assertValidName(string $name): void
147 | {
148 | if (!self::isValidName($name)) {
149 | throw InvalidEnumName::fromName($name, self::getKeys());
150 | }
151 | }
152 |
153 | /**
154 | * Checks whether a constant with this value is defined.
155 | *
156 | * @param int|string $value the value to test
157 | * @param bool $strict check the types of the value in the values
158 | *
159 | * @return bool the result of the test
160 | */
161 | final public static function isValidValue($value, bool $strict = true): bool
162 | {
163 | $values = array_values(self::getConstants());
164 |
165 | return in_array($value, $values, $strict);
166 | }
167 |
168 | /**
169 | * Asserts a constant with this value is defined.
170 | *
171 | * @param int|string $value the value to test
172 | * @param bool $strict check the types of the value in the values
173 | *
174 | * @throws InvalidEnumValue
175 | */
176 | final public static function assertValidValue($value, bool $strict = true): void
177 | {
178 | if (!self::isValidValue($value, $strict)) {
179 | throw InvalidEnumValue::fromValue($value, self::getConstants());
180 | }
181 | }
182 |
183 | /**
184 | * Can be useful if you need to get constants from several classes/interfaces.
185 | *
186 | * @return string[]
187 | */
188 | protected static function getEnumTypes(): array
189 | {
190 | return [get_called_class()];
191 | }
192 |
193 | /**
194 | * Returns keys from a value.
195 | *
196 | * @param mixed $value
197 | *
198 | * @return string|string[]
199 | */
200 | public static function getKeysFromValue($value)
201 | {
202 | $keys = [];
203 |
204 | foreach (static::getConstants() as $key => $item) {
205 | if ($value === $item) {
206 | $keys[] = $key;
207 | }
208 | }
209 |
210 | return count($keys) === 1 ? current($keys) : $keys;
211 | }
212 | }
213 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Enums
2 |
3 | This package holds a simple class that may be used as an ancestor for your
4 | enum classes.
5 |
6 | [![Build Status][3]](https://travis-ci.org/greg0ire/enum)
7 |
8 | ## Installation
9 |
10 | composer require greg0ire/enum
11 |
12 | ## Usage
13 |
14 | ### Basic usage
15 |
16 | Extend the `Greg0ire\Enum\AbstractEnum`, define your enum key values as constants,
17 | and Bob's your uncle. You can make the class abstract or final, as you see fit.
18 |
19 | ```php
20 | use Greg0ire\Enum\AbstractEnum;
21 |
22 | final class DaysOfWeek extends AbstractEnum {
23 | const Sunday = 0;
24 | const Monday = 1;
25 | const Tuesday = 2;
26 | const Wednesday = 3;
27 | const Thursday = 4;
28 | const Friday = 5;
29 | const Saturday = 6;
30 | }
31 | ```
32 |
33 | Then, you may use the DaysOfWeek class for input validation:
34 |
35 | ```php
36 | DaysOfWeek::isValidName('Humpday'); // false
37 | DaysOfWeek::isValidName('Monday'); // true
38 | DaysOfWeek::isValidName('monday'); // false
39 | DaysOfWeek::isValidName(0); // false
40 |
41 | DaysOfWeek::isValidValue(0); // true
42 | DaysOfWeek::isValidValue(5); // true
43 | DaysOfWeek::isValidValue(7); // false
44 | DaysOfWeek::isValidValue('Friday'); // false
45 | ```
46 |
47 | Both methods have an `assert*` counterpart that will throw a
48 | `Greg0ire\Enum\Exception\InvalidEnumValue` exception:
49 |
50 | ```
51 | DaysOfWeek::assertValidName(0); // InvalidEnumName
52 | DaysOfWeek::assertValidValue('Friday'); // InvalidEnumValue
53 | ```
54 |
55 | Additionally, you may get all the constants in your class as a hash:
56 |
57 | ```php
58 | DaysOfWeek::getConstants();
59 | DaysOfWeek::getConstants('strtolower'); // Will combine your values with `DaysOfWeek::getKeys($callback)`.
60 | DaysOfWeek::getConstants('strtolower', true); // Values combine with `DaysOfWeek::getClassPrefixedKeys($callback)`.
61 | DaysOfWeek::getConstants('strtolower', true, '.'); // Same with `DaysOfWeek::getClassPrefixedKeys($callback, $separator)`.
62 | ```
63 |
64 | You may also get all the keys in your class as an array:
65 |
66 | ```php
67 | DaysOfWeek::getKeys();
68 | DaysOfWeek::getKeys('strtolower'); // Will call `array_map` with the given callback.
69 | ```
70 |
71 | Or the key with the enum class prefix:
72 |
73 | ```php
74 | DaysOfWeek::getClassPrefixedKeys();
75 | DaysOfWeek::getClassPrefixedKeys('strtolower'); // Will call `array_map` with the given callback.
76 | DaysOfWeek::getClassPrefixedKeys('strtolower', '.'); // Replace the namespace separator ('_' by default).
77 | ```
78 |
79 | If you would like to get the keys from a value:
80 |
81 | ```php
82 | $key = DaysOfWeek::getKeysFromValue(1); // Monday will be assigned to $key
83 | ```
84 |
85 | If you have many keys with the same value you will get an array, and a value otherwise.
86 |
87 | ### Advanced usage
88 |
89 | If you need to get the constants from a class you cannot modify, or from an
90 | interface, or even from several classes / interfaces, you may override
91 | `AbstractEnum::getEnumTypes()`.
92 |
93 | For example, if you have the following class and interface :
94 |
95 |
96 | ```php
97 | namespace Vendor\Namespace;
98 |
99 | class ClassFromAVendor
100 | {
101 | const SOMETHING = 'something';
102 | const SOMETHING_ELSE = 'something_else';
103 | }
104 | ```
105 |
106 | ```php
107 | namespace My\Namespace;
108 |
109 | interface SomeInterface
110 | {
111 | const ANOTHER_CONST = 'another_const';
112 | }
113 | ```
114 |
115 | You can get all three constants by creating this Enum :
116 |
117 | ```php
118 | use Greg0ire\Enum\AbstractEnum;
119 | use My\Namespace\SomeInterface;
120 | use Vendor\Namespace\ClassFromAVendor;
121 |
122 | final class MyEnum extends AbstractEnum
123 | {
124 | protected static function getEnumTypes()
125 | {
126 | return [ClassFromAVendor::class, SomeInterface::class];
127 | }
128 | }
129 | ```
130 |
131 | Alternatively, you can specify a prefix for each type to avoid getting FQCNs in
132 | the hash keys.
133 |
134 | ```php
135 | use Greg0ire\Enum\AbstractEnum;
136 | use My\Namespace\SomeInterface;
137 | use Vendor\Namespace\ClassFromAVendor;
138 |
139 | final class MyEnum extends AbstractEnum
140 | {
141 | protected static function getEnumTypes()
142 | {
143 | return [
144 | 'prefix1' => ClassFromAVendor::class,
145 | 'prefix2' => SomeInterface::class,
146 | ];
147 | }
148 | }
149 | ```
150 |
151 | ### Get label from a service
152 |
153 | If you want to get the constant label behind an enum value, you can instantiate
154 | the `GetLabel` class and invoke it.
155 |
156 | ```php
157 | use Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel;
158 |
159 | $label = new GetLabel();
160 | $label(Your\Enum\Class::VALUE, Your\Enum\Class::class);
161 | ```
162 |
163 | To enable translation, require the `symfony/translation` component
164 | and pass a `Symfony\Contracts\Translation\TranslationInterface` instance on the
165 | `GetLabel` constructor
166 |
167 | ```php
168 | use Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel;
169 | use Symfony\Contracts\Translation\TranslationInterface;
170 |
171 | $label = new GetLabel($translator);
172 | $label(Your\Enum\Class::VALUE, Your\Enum\Class::class);
173 | ```
174 |
175 | If you're using Symfony, alias the service and simply inject it.
176 | If translations are enabled, the `TranslatorInterface` will be automatically injected.
177 |
178 | ```yaml
179 | services:
180 | # ...
181 | Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel: "@greg0ire_enum.symfony.translator.get_label"
182 | ```
183 |
184 | ```php
185 | public function index(GetLabel $label)
186 | {
187 | $label(Your\Enum\Class::VALUE, Your\Enum\Class::class);
188 | $label(Your\Enum\Class::VALUE, Your\Enum\Class::class, 'another_domain'); // Change the translation domain
189 | $label(Your\Enum\Class::VALUE, Your\Enum\Class::class, false); // Disable translation. In this case the class prefix wont be added
190 | $label(Your\Enum\Class::VALUE, Your\Enum\Class::class, false, true); // Disable translation but keep class prefix
191 | $label(Your\Enum\Class::VALUE, Your\Enum\Class::class, false, true, '.'); // Disable translation but keep class prefix with a custom separator
192 | }
193 | ```
194 |
195 | ### Integration with other libraries
196 |
197 | `greg0ire/enum` integrates with other libraries. The list is available in the
198 | `suggest` section of the Composer dependency manifest.
199 |
200 | #### Symfony validator
201 |
202 | This package provides a "ready to use" symfony validator.
203 | You have to require the `symfony/validator` package to get it working.
204 |
205 | ```php
206 | use Greg0ire\Enum\Bridge\Symfony\Validator\Constraint\Enum;
207 | use Symfony\Component\Validator\Validation;
208 | use Your\Namespace\EnumClass;
209 |
210 | $validator = Validation::createValidator();
211 |
212 | $violations = $validator->validateValue(42, new Enum(EnumClass::class));
213 | // You can also show the constants keys on the error message:
214 | $violations = $validator->validateValue(42, new Enum(['class' => EnumClass::class, 'showKeys' => true]));
215 | // Enum constraint inherits from Choice constraint. You can use inherited options too:
216 | $violations = $validator->validateValue(42, new Enum(['class' => EnumClass::class, 'strict' => true]));
217 | ```
218 |
219 | Another example with annotations:
220 |
221 | ```php
222 | use Doctrine\Common\Annotations\AnnotationRegistry;
223 | use Greg0ire\Enum\Bridge\Symfony\Validator\Constraint\Enum as EnumAssert;
224 | use Symfony\Component\Validator\Validation;
225 |
226 | class MyClass
227 | {
228 | /**
229 | * @EnumAssert("Your\Namespace\EnumClass")
230 | */
231 | private $dummy;
232 |
233 | public function __construct($dummy)
234 | {
235 | $this->dummy = $dummy
236 | }
237 | }
238 |
239 | AnnotationRegistry::registerLoader('class_exists');
240 | $validator = Validation::createValidatorBuilder()
241 | ->enableAnnotationMapping()
242 | ->getValidator();
243 |
244 | $object = new MyClass(42);
245 |
246 | $violations = $validator->validate($object);
247 | ```
248 |
249 | Note: You will have to get `doctrine/annotations` and `doctrine/cache` packages to get it working.
250 |
251 | #### Symfony form
252 |
253 | This package provides a "ready to use" symfony form type.
254 | You have to require the `symfony/form` package to get it working.
255 |
256 | ```php
257 | use Greg0ire\Enum\Bridge\Symfony\Form\Type\EnumType;
258 | use Symfony\Component\Form\Forms;
259 | use Your\Namespace\EnumClass;
260 |
261 | $formFactory = Forms::createFormFactory();
262 |
263 | $view = $this->factory->create(EnumType::class, null, array(
264 | 'class' => EnumClass::class,
265 | ))->createView();
266 | ```
267 |
268 | #### Twig extension
269 |
270 | This package comes with an `EnumExtension` Twig class. It contains a filter and some functions.
271 | You have to require the `twig/twig` package to get it working.
272 |
273 | ##### Filter
274 |
275 | The `enum_label` filter will try to return the constant label corresponding to the given value.
276 |
277 | This filter relies on the `Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel` service.
278 |
279 | It will try to translate it if possible. To enable translation, require the `symfony/translation` component
280 | and pass a `Symfony\Contracts\Translation\TranslationInterface` instance on the `GetLabel` constructor.
281 | `GetLabel` instance will be injected on the `EnumExtension` constructor.
282 |
283 | If translation is not available, you will have the default label with class prefixing.
284 |
285 | Usage:
286 |
287 | ```twig
288 | {{ value|enum_label('Your\\Enum\\Class') }}
289 | {{ value|enum_label('Your\\Enum\\Class', 'another_domain') }} {# Change the translation domain #}
290 | {{ value|enum_label('Your\\Enum\\Class', false) }} {# Disable translation. In this case the class prefix wont be added #}
291 | {{ value|enum_label('Your\\Enum\\Class', false, true) }} {# Disable translation but keep class prefix #}
292 | {{ value|enum_label('Your\\Enum\\Class', false, true, '.') }} {# Disable translation but keep class prefix with a custom separator #}
293 | ```
294 |
295 | ##### Functions
296 |
297 | The 3 available twig functions are ports of some `AbstractEnum` methods that can be useful in a twig template:
298 |
299 | * `enum_get_constants` => `AbstractEnum::getConstants`
300 | * `enum_get_keys` => `AbstractEnum::getKeys`
301 | * `enum_get_class_prefixed_keys` => `AbstractEnum::getClassPrefixedKeys`
302 |
303 | The arguments are exactly the same except you have to specify the targeted class first (as `enum_label` filter).
304 |
305 | Here is a concrete example with `enum_get_constants` function:
306 |
307 | ```twig
308 | {% for enum_key, enum_value in enum_get_constants('Your\\Enum\\Class') %}
309 | {{ enum_key }} -> {{ enum_value }}
310 | {% endfor %}
311 | ```
312 |
313 | ##### Twig extension as a service
314 |
315 | On Symfony projects, the extension can be autoloaded.
316 | First, you have to require the `symfony/framework-bundle` and `symfony/twig-bundle` packages, or use Symfony fullstack.
317 |
318 | Then, register the bundle in the kernel of your application:
319 |
320 | ``` php
321 | // app/AppKernel.php
322 |
323 | public function registerBundles()
324 | {
325 | $bundles = [
326 | // ...
327 | new Greg0ire\Enum\Bridge\Symfony\Bundle\Greg0ireEnumBundle(),
328 | ];
329 |
330 | // ...
331 |
332 | return $bundles
333 | }
334 | ```
335 |
336 | That's all. You can now directly use the filter.
337 |
338 | ## Contributing
339 |
340 | see [CONTRIBUTING.md][1]
341 |
342 | ## Credits
343 |
344 | This is a shameless rip-off of [this Stack Overflow answer][0], with one
345 | modification: the `getConstants` method has been made public so that it is
346 | available for building choice widgets, for instance. If you want to give credit
347 | to someone for this, give it to [Brian Cline][2]
348 |
349 | [0]: http://stackoverflow.com/a/254543/353612
350 | [1]: ./CONTRIBUTING.md
351 | [2]: http://stackoverflow.com/users/32536/brian-cline
352 | [3]: https://travis-ci.org/greg0ire/enum.svg?branch=master
353 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | Attribution-ShareAlike 3.0 Unported
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
10 | DAMAGES RESULTING FROM ITS USE.
11 |
12 | License
13 |
14 | THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
15 | COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
16 | COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
17 | AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
18 |
19 | BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
20 | TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
21 | BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
22 | CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
23 | CONDITIONS.
24 |
25 | 1. Definitions
26 |
27 | a. "Adaptation" means a work based upon the Work, or upon the Work and
28 | other pre-existing works, such as a translation, adaptation,
29 | derivative work, arrangement of music or other alterations of a
30 | literary or artistic work, or phonogram or performance and includes
31 | cinematographic adaptations or any other form in which the Work may be
32 | recast, transformed, or adapted including in any form recognizably
33 | derived from the original, except that a work that constitutes a
34 | Collection will not be considered an Adaptation for the purpose of
35 | this License. For the avoidance of doubt, where the Work is a musical
36 | work, performance or phonogram, the synchronization of the Work in
37 | timed-relation with a moving image ("synching") will be considered an
38 | Adaptation for the purpose of this License.
39 | b. "Collection" means a collection of literary or artistic works, such as
40 | encyclopedias and anthologies, or performances, phonograms or
41 | broadcasts, or other works or subject matter other than works listed
42 | in Section 1(f) below, which, by reason of the selection and
43 | arrangement of their contents, constitute intellectual creations, in
44 | which the Work is included in its entirety in unmodified form along
45 | with one or more other contributions, each constituting separate and
46 | independent works in themselves, which together are assembled into a
47 | collective whole. A work that constitutes a Collection will not be
48 | considered an Adaptation (as defined below) for the purposes of this
49 | License.
50 | c. "Creative Commons Compatible License" means a license that is listed
51 | at http://creativecommons.org/compatiblelicenses that has been
52 | approved by Creative Commons as being essentially equivalent to this
53 | License, including, at a minimum, because that license: (i) contains
54 | terms that have the same purpose, meaning and effect as the License
55 | Elements of this License; and, (ii) explicitly permits the relicensing
56 | of adaptations of works made available under that license under this
57 | License or a Creative Commons jurisdiction license with the same
58 | License Elements as this License.
59 | d. "Distribute" means to make available to the public the original and
60 | copies of the Work or Adaptation, as appropriate, through sale or
61 | other transfer of ownership.
62 | e. "License Elements" means the following high-level license attributes
63 | as selected by Licensor and indicated in the title of this License:
64 | Attribution, ShareAlike.
65 | f. "Licensor" means the individual, individuals, entity or entities that
66 | offer(s) the Work under the terms of this License.
67 | g. "Original Author" means, in the case of a literary or artistic work,
68 | the individual, individuals, entity or entities who created the Work
69 | or if no individual or entity can be identified, the publisher; and in
70 | addition (i) in the case of a performance the actors, singers,
71 | musicians, dancers, and other persons who act, sing, deliver, declaim,
72 | play in, interpret or otherwise perform literary or artistic works or
73 | expressions of folklore; (ii) in the case of a phonogram the producer
74 | being the person or legal entity who first fixes the sounds of a
75 | performance or other sounds; and, (iii) in the case of broadcasts, the
76 | organization that transmits the broadcast.
77 | h. "Work" means the literary and/or artistic work offered under the terms
78 | of this License including without limitation any production in the
79 | literary, scientific and artistic domain, whatever may be the mode or
80 | form of its expression including digital form, such as a book,
81 | pamphlet and other writing; a lecture, address, sermon or other work
82 | of the same nature; a dramatic or dramatico-musical work; a
83 | choreographic work or entertainment in dumb show; a musical
84 | composition with or without words; a cinematographic work to which are
85 | assimilated works expressed by a process analogous to cinematography;
86 | a work of drawing, painting, architecture, sculpture, engraving or
87 | lithography; a photographic work to which are assimilated works
88 | expressed by a process analogous to photography; a work of applied
89 | art; an illustration, map, plan, sketch or three-dimensional work
90 | relative to geography, topography, architecture or science; a
91 | performance; a broadcast; a phonogram; a compilation of data to the
92 | extent it is protected as a copyrightable work; or a work performed by
93 | a variety or circus performer to the extent it is not otherwise
94 | considered a literary or artistic work.
95 | i. "You" means an individual or entity exercising rights under this
96 | License who has not previously violated the terms of this License with
97 | respect to the Work, or who has received express permission from the
98 | Licensor to exercise rights under this License despite a previous
99 | violation.
100 | j. "Publicly Perform" means to perform public recitations of the Work and
101 | to communicate to the public those public recitations, by any means or
102 | process, including by wire or wireless means or public digital
103 | performances; to make available to the public Works in such a way that
104 | members of the public may access these Works from a place and at a
105 | place individually chosen by them; to perform the Work to the public
106 | by any means or process and the communication to the public of the
107 | performances of the Work, including by public digital performance; to
108 | broadcast and rebroadcast the Work by any means including signs,
109 | sounds or images.
110 | k. "Reproduce" means to make copies of the Work by any means including
111 | without limitation by sound or visual recordings and the right of
112 | fixation and reproducing fixations of the Work, including storage of a
113 | protected performance or phonogram in digital form or other electronic
114 | medium.
115 |
116 | 2. Fair Dealing Rights. Nothing in this License is intended to reduce,
117 | limit, or restrict any uses free from copyright or rights arising from
118 | limitations or exceptions that are provided for in connection with the
119 | copyright protection under copyright law or other applicable laws.
120 |
121 | 3. License Grant. Subject to the terms and conditions of this License,
122 | Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
123 | perpetual (for the duration of the applicable copyright) license to
124 | exercise the rights in the Work as stated below:
125 |
126 | a. to Reproduce the Work, to incorporate the Work into one or more
127 | Collections, and to Reproduce the Work as incorporated in the
128 | Collections;
129 | b. to create and Reproduce Adaptations provided that any such Adaptation,
130 | including any translation in any medium, takes reasonable steps to
131 | clearly label, demarcate or otherwise identify that changes were made
132 | to the original Work. For example, a translation could be marked "The
133 | original work was translated from English to Spanish," or a
134 | modification could indicate "The original work has been modified.";
135 | c. to Distribute and Publicly Perform the Work including as incorporated
136 | in Collections; and,
137 | d. to Distribute and Publicly Perform Adaptations.
138 | e. For the avoidance of doubt:
139 |
140 | i. Non-waivable Compulsory License Schemes. In those jurisdictions in
141 | which the right to collect royalties through any statutory or
142 | compulsory licensing scheme cannot be waived, the Licensor
143 | reserves the exclusive right to collect such royalties for any
144 | exercise by You of the rights granted under this License;
145 | ii. Waivable Compulsory License Schemes. In those jurisdictions in
146 | which the right to collect royalties through any statutory or
147 | compulsory licensing scheme can be waived, the Licensor waives the
148 | exclusive right to collect such royalties for any exercise by You
149 | of the rights granted under this License; and,
150 | iii. Voluntary License Schemes. The Licensor waives the right to
151 | collect royalties, whether individually or, in the event that the
152 | Licensor is a member of a collecting society that administers
153 | voluntary licensing schemes, via that society, from any exercise
154 | by You of the rights granted under this License.
155 |
156 | The above rights may be exercised in all media and formats whether now
157 | known or hereafter devised. The above rights include the right to make
158 | such modifications as are technically necessary to exercise the rights in
159 | other media and formats. Subject to Section 8(f), all rights not expressly
160 | granted by Licensor are hereby reserved.
161 |
162 | 4. Restrictions. The license granted in Section 3 above is expressly made
163 | subject to and limited by the following restrictions:
164 |
165 | a. You may Distribute or Publicly Perform the Work only under the terms
166 | of this License. You must include a copy of, or the Uniform Resource
167 | Identifier (URI) for, this License with every copy of the Work You
168 | Distribute or Publicly Perform. You may not offer or impose any terms
169 | on the Work that restrict the terms of this License or the ability of
170 | the recipient of the Work to exercise the rights granted to that
171 | recipient under the terms of the License. You may not sublicense the
172 | Work. You must keep intact all notices that refer to this License and
173 | to the disclaimer of warranties with every copy of the Work You
174 | Distribute or Publicly Perform. When You Distribute or Publicly
175 | Perform the Work, You may not impose any effective technological
176 | measures on the Work that restrict the ability of a recipient of the
177 | Work from You to exercise the rights granted to that recipient under
178 | the terms of the License. This Section 4(a) applies to the Work as
179 | incorporated in a Collection, but this does not require the Collection
180 | apart from the Work itself to be made subject to the terms of this
181 | License. If You create a Collection, upon notice from any Licensor You
182 | must, to the extent practicable, remove from the Collection any credit
183 | as required by Section 4(c), as requested. If You create an
184 | Adaptation, upon notice from any Licensor You must, to the extent
185 | practicable, remove from the Adaptation any credit as required by
186 | Section 4(c), as requested.
187 | b. You may Distribute or Publicly Perform an Adaptation only under the
188 | terms of: (i) this License; (ii) a later version of this License with
189 | the same License Elements as this License; (iii) a Creative Commons
190 | jurisdiction license (either this or a later license version) that
191 | contains the same License Elements as this License (e.g.,
192 | Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible
193 | License. If you license the Adaptation under one of the licenses
194 | mentioned in (iv), you must comply with the terms of that license. If
195 | you license the Adaptation under the terms of any of the licenses
196 | mentioned in (i), (ii) or (iii) (the "Applicable License"), you must
197 | comply with the terms of the Applicable License generally and the
198 | following provisions: (I) You must include a copy of, or the URI for,
199 | the Applicable License with every copy of each Adaptation You
200 | Distribute or Publicly Perform; (II) You may not offer or impose any
201 | terms on the Adaptation that restrict the terms of the Applicable
202 | License or the ability of the recipient of the Adaptation to exercise
203 | the rights granted to that recipient under the terms of the Applicable
204 | License; (III) You must keep intact all notices that refer to the
205 | Applicable License and to the disclaimer of warranties with every copy
206 | of the Work as included in the Adaptation You Distribute or Publicly
207 | Perform; (IV) when You Distribute or Publicly Perform the Adaptation,
208 | You may not impose any effective technological measures on the
209 | Adaptation that restrict the ability of a recipient of the Adaptation
210 | from You to exercise the rights granted to that recipient under the
211 | terms of the Applicable License. This Section 4(b) applies to the
212 | Adaptation as incorporated in a Collection, but this does not require
213 | the Collection apart from the Adaptation itself to be made subject to
214 | the terms of the Applicable License.
215 | c. If You Distribute, or Publicly Perform the Work or any Adaptations or
216 | Collections, You must, unless a request has been made pursuant to
217 | Section 4(a), keep intact all copyright notices for the Work and
218 | provide, reasonable to the medium or means You are utilizing: (i) the
219 | name of the Original Author (or pseudonym, if applicable) if supplied,
220 | and/or if the Original Author and/or Licensor designate another party
221 | or parties (e.g., a sponsor institute, publishing entity, journal) for
222 | attribution ("Attribution Parties") in Licensor's copyright notice,
223 | terms of service or by other reasonable means, the name of such party
224 | or parties; (ii) the title of the Work if supplied; (iii) to the
225 | extent reasonably practicable, the URI, if any, that Licensor
226 | specifies to be associated with the Work, unless such URI does not
227 | refer to the copyright notice or licensing information for the Work;
228 | and (iv) , consistent with Ssection 3(b), in the case of an
229 | Adaptation, a credit identifying the use of the Work in the Adaptation
230 | (e.g., "French translation of the Work by Original Author," or
231 | "Screenplay based on original Work by Original Author"). The credit
232 | required by this Section 4(c) may be implemented in any reasonable
233 | manner; provided, however, that in the case of a Adaptation or
234 | Collection, at a minimum such credit will appear, if a credit for all
235 | contributing authors of the Adaptation or Collection appears, then as
236 | part of these credits and in a manner at least as prominent as the
237 | credits for the other contributing authors. For the avoidance of
238 | doubt, You may only use the credit required by this Section for the
239 | purpose of attribution in the manner set out above and, by exercising
240 | Your rights under this License, You may not implicitly or explicitly
241 | assert or imply any connection with, sponsorship or endorsement by the
242 | Original Author, Licensor and/or Attribution Parties, as appropriate,
243 | of You or Your use of the Work, without the separate, express prior
244 | written permission of the Original Author, Licensor and/or Attribution
245 | Parties.
246 | d. Except as otherwise agreed in writing by the Licensor or as may be
247 | otherwise permitted by applicable law, if You Reproduce, Distribute or
248 | Publicly Perform the Work either by itself or as part of any
249 | Adaptations or Collections, You must not distort, mutilate, modify or
250 | take other derogatory action in relation to the Work which would be
251 | prejudicial to the Original Author's honor or reputation. Licensor
252 | agrees that in those jurisdictions (e.g. Japan), in which any exercise
253 | of the right granted in Section 3(b) of this License (the right to
254 | make Adaptations) would be deemed to be a distortion, mutilation,
255 | modification or other derogatory action prejudicial to the Original
256 | Author's honor and reputation, the Licensor will waive or not assert,
257 | as appropriate, this Section, to the fullest extent permitted by the
258 | applicable national law, to enable You to reasonably exercise Your
259 | right under Section 3(b) of this License (right to make Adaptations)
260 | but not otherwise.
261 |
262 | 5. Representations, Warranties and Disclaimer
263 |
264 | UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
265 | OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
266 | KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
267 | INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
268 | FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
269 | LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
270 | WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
271 | OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
272 |
273 | 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
274 | LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
275 | ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
276 | ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
277 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
278 |
279 | 7. Termination
280 |
281 | a. This License and the rights granted hereunder will terminate
282 | automatically upon any breach by You of the terms of this License.
283 | Individuals or entities who have received Adaptations or Collections
284 | from You under this License, however, will not have their licenses
285 | terminated provided such individuals or entities remain in full
286 | compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
287 | survive any termination of this License.
288 | b. Subject to the above terms and conditions, the license granted here is
289 | perpetual (for the duration of the applicable copyright in the Work).
290 | Notwithstanding the above, Licensor reserves the right to release the
291 | Work under different license terms or to stop distributing the Work at
292 | any time; provided, however that any such election will not serve to
293 | withdraw this License (or any other license that has been, or is
294 | required to be, granted under the terms of this License), and this
295 | License will continue in full force and effect unless terminated as
296 | stated above.
297 |
298 | 8. Miscellaneous
299 |
300 | a. Each time You Distribute or Publicly Perform the Work or a Collection,
301 | the Licensor offers to the recipient a license to the Work on the same
302 | terms and conditions as the license granted to You under this License.
303 | b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
304 | offers to the recipient a license to the original Work on the same
305 | terms and conditions as the license granted to You under this License.
306 | c. If any provision of this License is invalid or unenforceable under
307 | applicable law, it shall not affect the validity or enforceability of
308 | the remainder of the terms of this License, and without further action
309 | by the parties to this agreement, such provision shall be reformed to
310 | the minimum extent necessary to make such provision valid and
311 | enforceable.
312 | d. No term or provision of this License shall be deemed waived and no
313 | breach consented to unless such waiver or consent shall be in writing
314 | and signed by the party to be charged with such waiver or consent.
315 | e. This License constitutes the entire agreement between the parties with
316 | respect to the Work licensed here. There are no understandings,
317 | agreements or representations with respect to the Work not specified
318 | here. Licensor shall not be bound by any additional provisions that
319 | may appear in any communication from You. This License may not be
320 | modified without the mutual written agreement of the Licensor and You.
321 | f. The rights granted under, and the subject matter referenced, in this
322 | License were drafted utilizing the terminology of the Berne Convention
323 | for the Protection of Literary and Artistic Works (as amended on
324 | September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
325 | Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
326 | and the Universal Copyright Convention (as revised on July 24, 1971).
327 | These rights and subject matter take effect in the relevant
328 | jurisdiction in which the License terms are sought to be enforced
329 | according to the corresponding provisions of the implementation of
330 | those treaty provisions in the applicable national law. If the
331 | standard suite of rights granted under applicable copyright law
332 | includes additional rights not granted under this License, such
333 | additional rights are deemed to be included in the License; this
334 | License is not intended to restrict the license of any rights under
335 | applicable law.
336 |
337 |
338 | Creative Commons Notice
339 |
340 | Creative Commons is not a party to this License, and makes no warranty
341 | whatsoever in connection with the Work. Creative Commons will not be
342 | liable to You or any party on any legal theory for any damages
343 | whatsoever, including without limitation any general, special,
344 | incidental or consequential damages arising in connection to this
345 | license. Notwithstanding the foregoing two (2) sentences, if Creative
346 | Commons has expressly identified itself as the Licensor hereunder, it
347 | shall have all rights and obligations of Licensor.
348 |
349 | Except for the limited purpose of indicating to the public that the
350 | Work is licensed under the CCPL, Creative Commons does not authorize
351 | the use by either party of the trademark "Creative Commons" or any
352 | related trademark or logo of Creative Commons without the prior
353 | written consent of Creative Commons. Any permitted use will be in
354 | compliance with Creative Commons' then-current trademark usage
355 | guidelines, as may be published on its website or otherwise made
356 | available upon request from time to time. For the avoidance of doubt,
357 | this trademark restriction does not form part of the License.
358 |
359 | Creative Commons may be contacted at http://creativecommons.org/.
360 |
--------------------------------------------------------------------------------