├── .coveralls.yml
├── .gitignore
├── .travis.yml
├── tests
├── bootstrap.php
├── TestAsset
│ └── Adapter
│ │ ├── Options
│ │ ├── WrongAdapterOptions.php
│ │ ├── FakeAdapterOptions.php
│ │ ├── AdapterWithOptionsOptions.php
│ │ ├── FakeOptionsMap.php
│ │ └── WrongOptionsMap.php
│ │ ├── ConvertNothing.php
│ │ ├── AdapterWithOptions.php
│ │ ├── WrongAdapter.php
│ │ └── FakeAdapter.php
├── Adapter
│ ├── AbstractOptionsEnabledAdapterTest.php
│ └── Options
│ │ └── OptionsMapTest.php
└── ConversionTest.php
├── library
├── Exception
│ ├── ExceptionInterface.php
│ ├── DomainException.php
│ ├── RuntimeException.php
│ └── InvalidArgumentException.php
├── ConversionAlgorithmInterface.php
├── Adapter
│ ├── AbstractOptionsEnabledAdapter.php
│ └── Options
│ │ └── OptionsMap.php
└── Conversion.php
├── LICENSE
├── phpunit.xml.dist
├── composer.json
├── README.md
└── CONTRIBUTING.md
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | src_dir: library
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .~lock.*
3 | *.ipr
4 | *.iws
5 | .idea
6 | .buildpath
7 | .project
8 | .settings
9 | build/
10 | vendor/
11 | composer.lock
12 | composer.phar
13 | phpunit.xml
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.6
5 | - 7.4
6 | - 8.0
7 |
8 | branches:
9 | only:
10 | - master
11 | - develop
12 |
13 | install:
14 | - composer self-update
15 | - composer install --dev --prefer-source
16 |
17 | before_script:
18 | - mkdir -p build/coverage
19 |
20 | script:
21 | - vendor/bin/phpunit
22 |
23 | after_script:
24 | - php vendor/bin/coveralls
25 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | interface ExceptionInterface
17 | {
18 | }
19 |
--------------------------------------------------------------------------------
/library/Exception/DomainException.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class DomainException extends \DomainException implements ExceptionInterface
17 | {
18 | }
19 |
--------------------------------------------------------------------------------
/library/Exception/RuntimeException.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class RuntimeException extends \RuntimeException implements ExceptionInterface
17 | {
18 | }
19 |
--------------------------------------------------------------------------------
/library/Exception/InvalidArgumentException.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
17 | {
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/tests/TestAsset/Adapter/ConvertNothing.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | Permission to use, copy, modify, and/or distribute this software for any
4 | purpose with or without fee is hereby granted, provided that the above
5 | copyright notice and this permission notice appear in all copies.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 |
--------------------------------------------------------------------------------
/tests/TestAsset/Adapter/AdapterWithOptions.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | tests
5 |
6 |
7 |
8 |
9 | library
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/library/ConversionAlgorithmInterface.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | interface ConversionAlgorithmInterface
17 | {
18 | /**
19 | * Convert $value with the defined settings
20 | *
21 | * @param string $value Data to decompress
22 | * @return string The converted data
23 | */
24 | public function convert($value);
25 |
26 | /**
27 | * Return the conversion adapter name
28 | *
29 | * @return string
30 | */
31 | public function getName();
32 | }
33 |
--------------------------------------------------------------------------------
/tests/TestAsset/Adapter/Options/FakeAdapterOptions.php:
--------------------------------------------------------------------------------
1 | fake;
29 | }
30 |
31 | /**
32 | * @param string $fake
33 | */
34 | public function setFake($fake)
35 | {
36 | $this->fake = $fake;
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "leodido/conversio",
3 | "description": "A library that provides a simple infrastructure to create your own converters and to perform any conversion you want",
4 | "minimum-stability": "stable",
5 | "license": "ISC",
6 | "authors": [
7 | {
8 | "name": "Leo Di Donato",
9 | "email": "leodidonato@gmail.com",
10 | "homepage": "http://github.com/leodido"
11 | }
12 | ],
13 | "require": {
14 | "php": ">=5.4",
15 | "laminas/laminas-filter": "2.*"
16 | },
17 | "require-dev": {
18 | "phpunit/phpunit": "~4.2"
19 | },
20 | "autoload": {
21 | "psr-4": {
22 | "Conversio\\": "library/",
23 | "ConversioTest\\": "tests/"
24 | }
25 | },
26 | "keywords": [
27 | "convert",
28 | "conversion",
29 | "converter",
30 | "library",
31 | "conversions",
32 | "converters"
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/tests/TestAsset/Adapter/Options/AdapterWithOptionsOptions.php:
--------------------------------------------------------------------------------
1 | opt2;
28 | }
29 |
30 | /**
31 | * @param mixed $opt2
32 | */
33 | public function setOpt2($opt2)
34 | {
35 | $this->opt2 = $opt2;
36 | }
37 |
38 | /**
39 | * @return mixed
40 | */
41 | public function getOpt1()
42 | {
43 | return $this->opt1;
44 | }
45 |
46 | /**
47 | * @param mixed $opt1
48 | */
49 | public function setOpt1($opt1)
50 | {
51 | $this->opt1 = $opt1;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tests/TestAsset/Adapter/WrongAdapter.php:
--------------------------------------------------------------------------------
1 | options = $options;
47 | return $this;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tests/TestAsset/Adapter/FakeAdapter.php:
--------------------------------------------------------------------------------
1 | options = $options;
47 | return $this;
48 | }
49 |
50 | /**
51 | * @return FakeAdapterOptions
52 | */
53 | public function getOptions()
54 | {
55 | return $this->options;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/tests/TestAsset/Adapter/Options/FakeOptionsMap.php:
--------------------------------------------------------------------------------
1 | [1, 2, 3],
20 | 'string' => ['1', '2', '3'],
21 | ];
22 |
23 | /**
24 | * @param $value
25 | * @return $this
26 | */
27 | public function setNumber($value)
28 | {
29 | $this->setOption('number', $value);
30 | return $this;
31 | }
32 |
33 | /**
34 | * @return int
35 | */
36 | public function getNumber()
37 | {
38 | return $this->getOption('number');
39 | }
40 |
41 | /**
42 | * @param $value
43 | * @return $this
44 | */
45 | public function setString($value)
46 | {
47 | $this->setOption('string', $value);
48 | return $this;
49 | }
50 |
51 | /**
52 | * @return string
53 | */
54 | public function getString()
55 | {
56 | return $this->getOption('string');
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/tests/TestAsset/Adapter/Options/WrongOptionsMap.php:
--------------------------------------------------------------------------------
1 | [1, 2, 3],
20 | 'string' => 'this value is not a list',
21 | ];
22 |
23 | /**
24 | * @param $value
25 | * @return $this
26 | */
27 | public function setNumber($value)
28 | {
29 | $this->setOption('number', $value);
30 | return $this;
31 | }
32 |
33 | /**
34 | * @return int
35 | */
36 | public function getNumber()
37 | {
38 | return $this->getOption('number');
39 | }
40 |
41 | /**
42 | * @param $value
43 | * @return $this
44 | */
45 | public function setNotSet($value)
46 | {
47 | $this->setOption('not_set', $value);
48 | return $this;
49 | }
50 |
51 | /**
52 | * @return string
53 | */
54 | public function getNotSet()
55 | {
56 | return $this->getOption('not_set');
57 | }
58 |
59 | /**
60 | * @param $value
61 | * @return $this
62 | */
63 | public function setString($value)
64 | {
65 | $this->setOption('string', $value);
66 | return $this;
67 | }
68 |
69 | /**
70 | * @return string
71 | */
72 | public function getString()
73 | {
74 | return $this->getOption('string');
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/library/Adapter/AbstractOptionsEnabledAdapter.php:
--------------------------------------------------------------------------------
1 | options = $options;
45 | return $this;
46 | }
47 |
48 | /**
49 | * Retrieve the adapter options instance
50 | *
51 | * @return AbstractOptions
52 | */
53 | public function getOptions()
54 | {
55 | if (!$this->options) {
56 | throw new Exception\RuntimeException(sprintf(
57 | 'No options instance set for the adapter "%s"',
58 | get_class($this)
59 | ));
60 | }
61 | return $this->options;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Conversio
2 | =========
3 |
4 | [](https://packagist.org/packages/leodido/conversio) [](https://travis-ci.org/leodido/conversio) [](https://coveralls.io/r/leodido/conversio)
5 |
6 | Conversio is a PHP library that provides a simple infrastructure to create your own converters and to perform any conversion.
7 |
8 | Explaination
9 | ------------
10 |
11 | The entry point for **conversion** is the class `Conversion`, that acts as a **filter** (i.e., `Zend\Filter\AbstractFilter`).
12 |
13 | To implement a conversion you have to create an **adapter** (that will be passed to `Conversion` class) that describes its process.
14 |
15 | The adapters must implement the `ConversionAlgorithmInterface` interface.
16 |
17 | Furthermore, adapters can have **options** too, in the form of a `Zend\Stdlib\AbstractOptions` subclass. Conversio library requires only that the options class of each adapter is called with the name of the adapter followed by the suffix "Options" (e.g., `LanguageCodeOptions` is the option class of `LanguageCode` adapter class).
18 |
19 | In this case your adapter can extend `AbstractOptionsEnabledAdapter` abstract class to take advantage of its options related methods.
20 |
21 | The `OptionsMap` class is an utility class aimed to create a option class starting from a configuration hash that describes the options (by name) and their admitted values.
22 |
23 | Installation
24 | ------------
25 |
26 | Add `leodido/conversio` to your `composer.json`.
27 |
28 | ```json
29 | {
30 | "require": {
31 | "leodido/conversio": "v0.3.0"
32 | }
33 | }
34 | ```
35 |
36 | Usage
37 | -----
38 |
39 | **WIP**
40 |
41 | Converters
42 | ----------
43 |
44 | Here will be listed the converters created using Conversio library.
45 |
46 | - [LangCode](https://github.com/leodido/langcode-conv)
47 |
48 | ---
49 |
50 | [](https://github.com/igrigorik/ga-beacon)
51 |
--------------------------------------------------------------------------------
/tests/Adapter/AbstractOptionsEnabledAdapterTest.php:
--------------------------------------------------------------------------------
1 | adapter = new AdapterWithOptions();
31 | }
32 |
33 | /**
34 | * @expectedException DomainException
35 | */
36 | public function testSetNotValidOptionsShouldThrowDomainException()
37 | {
38 | /** @var $abstractOptsMock AbstractOptions */
39 | $abstractOptsMock = $this->getMockForAbstractClass('Zend\Stdlib\AbstractOptions');
40 | $this->adapter->setOptions($abstractOptsMock);
41 | }
42 |
43 | /**
44 | * @expectedException RuntimeException
45 | */
46 | public function testGetNotSetOptionsShouldThrowRuntimeException()
47 | {
48 | $this->adapter->getOptions();
49 | }
50 |
51 | public function testOptions()
52 | {
53 | $opts = new AdapterWithOptionsOptions(['opt1' => 1, 'opt2' => 2]);
54 | $this->assertInstanceOf(
55 | 'ConversioTest\TestAsset\Adapter\AdapterWithOptions',
56 | $this->adapter->setOptions($opts)
57 | );
58 | $options = $this->adapter->getOptions();
59 | $this->assertInstanceOf('ConversioTest\TestAsset\Adapter\Options\AdapterWithOptionsOptions', $options);
60 | $this->assertAttributeEquals(1, 'opt1', $options);
61 | $this->assertAttributeEquals(2, 'opt2', $options);
62 | $options = $options->toArray();
63 | $this->assertArrayHasKey('opt1', $options);
64 | $this->assertArrayHasKey('opt2', $options);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/tests/Adapter/Options/OptionsMapTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf('Conversio\Adapter\Options\OptionsMap', $optsMap);
28 | $this->assertInstanceOf('Zend\Stdlib\AbstractOptions', $optsMap);
29 | }
30 |
31 | /**
32 | * @expectedException DomainException
33 | */
34 | public function testConstructWithoutValidConfigurationShouldThrowDomainException()
35 | {
36 | new OptionsMap();
37 | }
38 |
39 | /**
40 | * @expectedException InvalidArgumentException
41 | */
42 | public function testSetInvalidOptionsValueShouldThrowInvalidArgumentException()
43 | {
44 | $optsMap = new FakeOptionsMap;
45 | $optsMap->setNumber('string');
46 | }
47 |
48 | public function testSettersAndGetters()
49 | {
50 | $optsMap = new FakeOptionsMap(['number' => 1, 'string' => '1']);
51 | $this->assertInstanceOf('ConversioTest\TestAsset\Adapter\Options\FakeOptionsMap', $optsMap);
52 | $this->assertEquals(1, $optsMap->getNumber());
53 | $this->assertEquals('1', $optsMap->getString());
54 | }
55 |
56 | public function testToArray()
57 | {
58 | $optsMap = new FakeOptionsMap;
59 | $this->assertEmpty($optsMap->toArray());
60 |
61 | $optsMap->setNumber(3);
62 | $this->assertArrayHasKey('number', $optsMap->toArray());
63 | $this->assertArrayNotHasKey('string', $optsMap->toArray());
64 | $optsMap->setString('3');
65 | $this->assertArrayHasKey('string', $optsMap->toArray());
66 | }
67 |
68 | /**
69 | * @expectedException DomainException
70 | */
71 | public function testSetNonExistentOptionShouldThrowDomainException()
72 | {
73 | $optsMap = new WrongOptionsMap;
74 | $optsMap->setNotSet('try!');
75 | }
76 |
77 | /**
78 | * @expectedException RuntimeException
79 | */
80 | public function testGetNonExistentOptionShouldThrowRuntimeException()
81 | {
82 | $optsMap = new WrongOptionsMap;
83 | $optsMap->getNotSet();
84 | }
85 |
86 | /**
87 | * @expectedException DomainException
88 | */
89 | public function testSetNonListOptionsShouldThrowDomainException()
90 | {
91 | $optsMap = new WrongOptionsMap;
92 | $optsMap->setString('value');
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Contributions to **Conversio** library are always welcomed and encouraged.
4 |
5 | You make our lives easier by sending us your contributions through github pull requests.
6 |
7 | * Coding standard for the project is [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
8 |
9 | * Any contribution must provide tests for additional introduced conditions
10 |
11 | ## Team members
12 |
13 | The core team members are:
14 |
15 | | Name | Nickname |
16 | |:---------------:|:------------------------------------:|
17 | | Leo Di Donato | [leodido](http://github.com/leodido) |
18 |
19 | ## Got a question or problem?
20 |
21 | If you have questions about how to use Conversio library please write at .
22 |
23 | Other communication channels will be activated soon. In the mean time you can also contact us writing a [new issue](https://github.com/leodido/conversio/issues/new).
24 |
25 | Due to time constraints, we are not always able to respond as quickly as we would like. Please do not take delays personal and feel free to remind us.
26 |
27 | ## New features
28 |
29 | You can request a new feature by submitting an issue to our github repository. If you would like to implement a new feature then consider what kind of change it is:
30 |
31 | * **Major changes**
32 |
33 | This kind of contribution should be discussed first with us in issues. This way we can better coordinate our efforts, prevent duplication of work, and help you to craft the change so that it is successfully accepted into the project.
34 |
35 | * **Small changes**
36 |
37 | Can be crafted and submitted to the github repository as a pull request.
38 |
39 | ## Bug triage
40 |
41 | Bug triaging is managed via github [issues](https://github.com/leodido/conversio/issues).
42 |
43 | You can help report bugs by filing them [here](https://github.com/leodido/conversio/issues).
44 |
45 | Before submitting new bugs please verify that similar ones do not exists yet. This will help us to reduce the duplicates and the references between issues.
46 |
47 | Is desiderable that you provide reproducible behaviours attaching (failing) tests.
48 |
49 | ## Testing
50 |
51 | The PHPUnit version to be used is the one installed as a dev-dependency via [composer](https://getcomposer.org/):
52 |
53 | ```bash
54 | $ ./vendor/bin/phpunit
55 | ```
56 |
57 | ## Contributing process
58 |
59 | What branch to issue the pull request against?
60 |
61 | For **new features**, or fixes that introduce **new elements to the public API** (such as new public methods or properties), issue the pull request against the `develop` branch.
62 |
63 | For **hotfixes** against the stable release, issue the pull request against the `master` branch.
64 |
65 | 1. **Fork** the sphinxsearch [repository](https://github.com/leodido/conversio/fork)
66 |
67 | 2. **Checkout** the forked repository
68 |
69 | 3. Retrieve **dependencies** using [composer](https://getcomposer.org/)
70 |
71 | 4. Create your **local branch**, **commit** your code and **push** your local branch to your github fork
72 |
73 | 5. Send us a **pull request** as descripted for your changes to be included
74 |
75 | Please remember that **any contribution must provide tests** for additional introduced conditions. Accepted coverage for new contributions is 75%. Any contribution not satisfying this requirement won't be merged.
76 |
77 | Don't get discouraged!
--------------------------------------------------------------------------------
/library/Adapter/Options/OptionsMap.php:
--------------------------------------------------------------------------------
1 | config, false)) {
41 | throw new Exception\DomainException(sprintf(
42 | '"%s" expects that options map configuration property is an hash table',
43 | __METHOD__
44 | ));
45 | }
46 | parent::__construct($options);
47 | }
48 |
49 | /**
50 | * {@inheritdoc}
51 | */
52 | public function toArray()
53 | {
54 | return $this->options;
55 | }
56 |
57 | /**
58 | * Option setter with validation
59 | * If option can have the specified value then it is set, otherwise this method throws exception
60 | *
61 | * Tip: call it into your setter methods.
62 | *
63 | * @param $key
64 | * @param $value
65 | * @return $this
66 | * @throws Exception\DomainException
67 | * @throws Exception\InvalidArgumentException
68 | */
69 | protected function setOption($key, $value)
70 | {
71 | if (!isset($this->config[$key])) {
72 | throw new Exception\DomainException(
73 | sprintf(
74 | 'Option "%s" does not exist; available options are (%s)',
75 | $key,
76 | implode(
77 | ', ',
78 | array_map(
79 | function ($opt) {
80 | return '"' . $opt . '"';
81 | },
82 | array_keys($this->config)
83 | )
84 | )
85 | )
86 | );
87 | }
88 | if (!ArrayUtils::isList($this->config[$key], false)) {
89 | throw new Exception\DomainException(sprintf(
90 | 'Option "%s" does not have a list of allowed values',
91 | $key
92 | ));
93 | }
94 | if (!ArrayUtils::inArray($value, $this->config[$key], true)) {
95 | throw new Exception\InvalidArgumentException(sprintf(
96 | 'Option "%s" can not be set to value "%s"; allowed values are (%s)',
97 | $key,
98 | $value,
99 | implode(
100 | ', ',
101 | array_map(
102 | function ($val) {
103 | return '"' . $val . '"';
104 | },
105 | $this->config[$key]
106 | )
107 | )
108 | ));
109 | }
110 | $this->options[$key] = $value;
111 | return $this;
112 | }
113 |
114 | /**
115 | * Option getter with check
116 | *
117 | * Tip: call it into your getter methods.
118 | *
119 | * @param $key
120 | * @return mixed
121 | * @throws Exception\RuntimeException
122 | */
123 | protected function getOption($key)
124 | {
125 | if (!isset($this->options[$key])) {
126 | throw new Exception\RuntimeException(sprintf(
127 | 'Option "%s" not found',
128 | $key
129 | ));
130 | }
131 |
132 | return $this->options[$key];
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/library/Conversion.php:
--------------------------------------------------------------------------------
1 | setAdapter($params);
45 | break;
46 | case is_array($params):
47 | $this->setOptions($params);
48 | break;
49 | default:
50 | break;
51 | }
52 | }
53 |
54 | /**
55 | * Sets conversion adapter
56 | *
57 | * @param string|ConversionAlgorithmInterface $adapter Adapter to use
58 | * @return $this
59 | * @throws Exception\InvalidArgumentException
60 | * @throws Exception\RuntimeException
61 | */
62 | public function setAdapter($adapter)
63 | {
64 | if (!is_string($adapter) && !$adapter instanceof ConversionAlgorithmInterface) {
65 | throw new Exception\InvalidArgumentException(sprintf(
66 | '"%s" expects a string or an instance of ConversionAlgorithmInterface; received "%s"',
67 | __METHOD__,
68 | is_object($adapter) ? get_class($adapter) : gettype($adapter)
69 | ));
70 | }
71 | if (is_string($adapter)) {
72 | if (!class_exists($adapter)) {
73 | throw new Exception\RuntimeException(sprintf(
74 | '"%s" unable to load adapter; class "%s" not found',
75 | __METHOD__,
76 | $adapter
77 | ));
78 | }
79 | $adapter = new $adapter();
80 | if (!$adapter instanceof ConversionAlgorithmInterface) {
81 | throw new Exception\InvalidArgumentException(sprintf(
82 | '"%s" expects a string representing an instance of ConversionAlgorithmInterface; received "%s"',
83 | __METHOD__,
84 | is_object($adapter) ? get_class($adapter) : gettype($adapter)
85 | ));
86 | }
87 | }
88 | $this->adapter = $adapter;
89 |
90 | return $this;
91 | }
92 |
93 | /**
94 | * Returns the current adapter
95 | *
96 | * @return ConversionAlgorithmInterface
97 | * @throws Exception\RuntimeException
98 | */
99 | public function getAdapter()
100 | {
101 | if (!$this->adapter) {
102 | throw new Exception\RuntimeException(sprintf(
103 | '"%s" unable to load adapter; adapter not found',
104 | __METHOD__
105 | ));
106 | }
107 | if (method_exists($this->adapter, 'setOptions')) {
108 | $this->adapter->setOptions($this->getAdapterOptions());
109 | }
110 |
111 | return $this->adapter;
112 | }
113 |
114 | /**
115 | * Retrieve adapter name
116 | *
117 | * @return string
118 | */
119 | public function getAdapterName()
120 | {
121 | return $this->getAdapter()->getName();
122 | }
123 |
124 | /**
125 | * Set adapter options
126 | *
127 | * @param array|AbstractOptions $options
128 | * @return $this
129 | */
130 | public function setAdapterOptions($options)
131 | {
132 | if (!is_array($options) && !$options instanceof AbstractOptions) {
133 | throw new Exception\InvalidArgumentException(sprintf(
134 | '"%s" expects an array or a valid instance of "%s"; received "%s"',
135 | __METHOD__,
136 | 'Zend\Stdlib\AbstractOptions',
137 | is_object($options) ? get_class($options) : gettype($options)
138 | ));
139 | }
140 | $this->adapterOptions = $options;
141 | $this->options = $options;
142 |
143 | return $this;
144 | }
145 |
146 | /**
147 | * @return AbstractOptions
148 | */
149 | public function getAdapterOptions()
150 | {
151 | if (is_array($this->adapterOptions)) {
152 | $optClass = $this->getAbstractOptions();
153 | $this->adapterOptions = $optClass->setFromArray($this->adapterOptions);
154 | return $this->adapterOptions;
155 | }
156 | $wantedOptionsClass = self::getOptionsFullQualifiedClassName($this->adapter);
157 | if (get_class($this->adapterOptions) !== $wantedOptionsClass) {
158 | throw new Exception\DomainException(sprintf(
159 | '"%s" expects that options set are an array or a valid "%s" instance; received "%s"',
160 | __METHOD__,
161 | $wantedOptionsClass,
162 | get_class($this->adapterOptions)
163 | ));
164 | }
165 | $this->options = $this->adapterOptions->toArray();
166 | return $this->adapterOptions;
167 | }
168 |
169 | /**
170 | * Instantiate and retrieve the options of the current adapter
171 | *
172 | * It also checks that an appropriate options class exists.
173 | *
174 | * @return AbstractOptions
175 | */
176 | protected function getAbstractOptions()
177 | {
178 | $optClass = self::getOptionsFullQualifiedClassName($this->adapter);
179 | // Does the option class exist?
180 | if (!class_exists($optClass)) {
181 | throw new Exception\DomainException(
182 | sprintf(
183 | '"%s" expects that an options class ("%s") for the current adapter exists',
184 | __METHOD__,
185 | $optClass
186 | )
187 | );
188 | }
189 | $opts = new $optClass();
190 | if (!$opts instanceof AbstractOptions) {
191 | throw new Exception\DomainException(sprintf(
192 | '"%s" expects the options class to resolve to a valid "%s" instance; received "%s"',
193 | __METHOD__,
194 | 'Zend\Stdlib\AbstractOptions',
195 | $optClass
196 | ));
197 | }
198 | return $opts;
199 | }
200 |
201 | /**
202 | * Set filter options
203 | *
204 | * @param array|Traversable $options
205 | * @throws Exception\InvalidArgumentException if options is not an array or Traversable
206 | * @return $this
207 | */
208 | public function setOptions($options)
209 | {
210 | if (!is_array($options) && !$options instanceof Traversable) {
211 | throw new Exception\InvalidArgumentException(sprintf(
212 | '"%s" expects an array or Traversable; received "%s"',
213 | __METHOD__,
214 | is_object($options) ? get_class($options) : gettype($options)
215 | ));
216 | }
217 | foreach ($options as $key => $value) {
218 | if ($key === 'options') {
219 | $key = 'adapterOptions';
220 | }
221 | $method = 'set' . ucfirst($key);
222 | if (method_exists($this, $method)) {
223 | $this->$method($value);
224 | }
225 | }
226 | return $this;
227 | }
228 |
229 | /**
230 | * Get individual or all options from underlying adapter options object
231 | *
232 | * @param string|null $option
233 | * @return mixed|array
234 | */
235 | public function getOptions($option = null)
236 | {
237 | $this->getAdapterOptions();
238 | if ($option !== null) {
239 | if (!isset($this->options[$option])) {
240 | throw new Exception\RuntimeException(sprintf(
241 | 'Options "%s" not found',
242 | $option
243 | ));
244 | }
245 | return $this->options[$option];
246 | }
247 | return $this->options;
248 | }
249 |
250 | /**
251 | * {@inheritdoc}
252 | */
253 | public function filter($value)
254 | {
255 | if (!is_string($value)) {
256 | return $value;
257 | }
258 | return $this->getAdapter()->convert($value);
259 | }
260 |
261 | /**
262 | * Recreate the full qualified class name of options class for the supplied adapter
263 | *
264 | * @param ConversionAlgorithmInterface|null $adapter
265 | * @return string
266 | * @throws Exception\InvalidArgumentException
267 | */
268 | public static function getOptionsFullQualifiedClassName($adapter)
269 | {
270 | if (!$adapter) {
271 | throw new Exception\InvalidArgumentException(sprintf(
272 | '"%s" unable to load adapter; adapter not found',
273 | __METHOD__
274 | ));
275 | }
276 | if (!$adapter instanceof ConversionAlgorithmInterface) {
277 | throw new Exception\InvalidArgumentException(sprintf(
278 | '"%s" expects an instance of ConversionAlgorithmInterface; received "%s"',
279 | __METHOD__,
280 | is_object($adapter) ? get_class($adapter) : gettype($adapter)
281 | ));
282 | }
283 |
284 | $adapterClass = get_class($adapter);
285 | $namespace = substr($adapterClass, 0, strrpos($adapterClass, '\\'));
286 | return $namespace . '\\Options\\' . $adapter->getName() . 'Options';
287 | }
288 | }
289 |
--------------------------------------------------------------------------------
/tests/ConversionTest.php:
--------------------------------------------------------------------------------
1 | mock = $this->getMockBuilder(self::CLASSNAME)
30 | ->disableOriginalConstructor()
31 | ->getMock();
32 | }
33 |
34 | public function testConstructor()
35 | {
36 | // array input
37 | $input = [];
38 | $this->mock->expects($this->at(0))
39 | ->method('setOptions')
40 | ->with($this->equalTo($input));
41 |
42 | $class = new \ReflectionClass(self::CLASSNAME);
43 | $ctor = $class->getConstructor();
44 |
45 | $ctor->invoke($this->mock, $input);
46 |
47 | // traversable input
48 | $input = new \ArrayIterator([]);
49 | $this->mock->expects($this->at(0))
50 | ->method('setOptions')
51 | ->with($this->equalTo($input->getArrayCopy()));
52 |
53 | $ctor->invoke($this->mock, $input);
54 |
55 | // string input
56 | $input = 'adapter';
57 | $this->mock->expects($this->at(0))
58 | ->method('setAdapter')
59 | ->with($this->equalTo($input));
60 |
61 | $ctor->invoke($this->mock, $input);
62 |
63 | // adapter input
64 | $input = $this->getMockForAbstractClass('Conversio\ConversionAlgorithmInterface');
65 | $this->mock->expects($this->at(0))
66 | ->method('setAdapter')
67 | ->with($this->equalTo($input));
68 |
69 | $ctor->invoke($this->mock, $input);
70 |
71 | // null input
72 | $input = null;
73 | $this->mock->expects($this->never())
74 | ->method('setAdapter')
75 | ->with($this->equalTo($input));
76 | $this->mock->expects($this->never())
77 | ->method('setOptions')
78 | ->with($this->equalTo($input));
79 |
80 | $ctor->invoke($this->mock, $input);
81 | }
82 |
83 | public function testGetOptionsFQCNWithNotAConversionAlgorithmInterfaceShouldThrowInvalidArgumentException()
84 | {
85 | $this->setExpectedException('Conversio\Exception\InvalidArgumentException');
86 | Conversion::getOptionsFullQualifiedClassName(new \stdClass());
87 | }
88 |
89 | public function testGetAdapterNotSetShouldThrowRuntimeException()
90 | {
91 | $filter = new Conversion();
92 | $this->setExpectedException('Conversio\Exception\RuntimeException');
93 | $filter->getAdapter();
94 | }
95 |
96 | public function testSetInvalidTypeAdapterShouldThrowInvalidArgumentException()
97 | {
98 | $filter = new Conversion();
99 | $this->setExpectedException('Conversio\Exception\InvalidArgumentException');
100 | $filter->setAdapter(new \stdClass());
101 | }
102 |
103 | public function testSetNonExistentAdapterShouldThrowRuntimeException()
104 | {
105 | $filter = new Conversion();
106 | $this->setExpectedException('Conversio\Exception\RuntimeException');
107 | $filter->setAdapter('Conversio\Phantom\NonExistentAdapter');
108 |
109 | $this->setExpectedException('Conversio\Exception\RuntimeException');
110 | $filter->getAdapter();
111 | }
112 |
113 | public function testSetInvalidAdapterClassShouldThrowInvalidArgumentException()
114 | {
115 | $filter = new Conversion();
116 | $this->setExpectedException('Conversio\Exception\InvalidArgumentException');
117 | $filter->setAdapter('\ArrayIterator');
118 |
119 | $this->setExpectedException('Conversio\Exception\RuntimeException');
120 | $filter->getAdapter();
121 | }
122 |
123 | public function testSetAdapter()
124 | {
125 | $adapterClassName = 'ConversioTest\TestAsset\Adapter\ConvertNothing';
126 | $filter = new Conversion();
127 |
128 | // string param
129 | $filter->setAdapter($adapterClassName);
130 | $this->assertInstanceOf($adapterClassName, $filter->getAdapter());
131 | $this->assertEquals($filter->getAdapter()->getName(), $filter->getAdapterName());
132 |
133 | // instance param
134 | /** @var $adapterInstance \ConversioTest\TestAsset\Adapter\ConvertNothing */
135 | $adapterInstance = new $adapterClassName();
136 | $filter->setAdapter($adapterInstance);
137 | $this->assertInstanceOf($adapterClassName, $filter->getAdapter());
138 | $this->assertEquals($adapterInstance->getName(), $filter->getAdapterName());
139 | }
140 |
141 | public function testSetInvalidTypeOptionsShouldThrowInvalidArgumentException()
142 | {
143 | $filter = new Conversion();
144 | $this->setExpectedException('Conversio\Exception\InvalidArgumentException');
145 | $filter->setOptions('invalidoptions');
146 | }
147 |
148 | public function testSetOptions()
149 | {
150 | $onlyOpts = [
151 | 'options' => ['prop1' => 1, 'prop2' => 2]
152 | ];
153 |
154 | $this->mock->expects($this->at(0))
155 | ->method('setAdapterOptions')
156 | ->with($this->equalTo($onlyOpts['options']));
157 | $class = new \ReflectionClass(self::CLASSNAME);
158 | $setOptsMethod = $class->getMethod('setOptions');
159 |
160 | $setOptsMethod->invoke($this->mock, $onlyOpts);
161 |
162 | $opts = [
163 | 'adapter' => 'ConversioTest\TestAsset\Adapter\ConvertNothing',
164 | 'options' => ['prop1' => 1, 'prop2' => 2],
165 | ];
166 |
167 | $this->mock->expects($this->at(0))
168 | ->method('setAdapter')
169 | ->with($this->equalTo($opts['adapter']));
170 | $this->mock->expects($this->at(1))
171 | ->method('setAdapterOptions')
172 | ->with($this->equalTo($opts['options']));
173 | $class = new \ReflectionClass(self::CLASSNAME);
174 | $setOptsMethod = $class->getMethod('setOptions');
175 |
176 | $setOptsMethod->invoke($this->mock, $opts);
177 |
178 | $opts = [
179 | 'options' => ['prop1' => 1, 'prop2' => 2],
180 | 'adapter' => 'ConversioTest\TestAsset\Adapter\ConvertNothing',
181 | ];
182 |
183 | $this->mock->expects($this->at(0))
184 | ->method('setAdapterOptions')
185 | ->with($this->equalTo($opts['options']));
186 | $this->mock->expects($this->at(1))
187 | ->method('setAdapter')
188 | ->with($this->equalTo($opts['adapter']));
189 | $class = new \ReflectionClass(self::CLASSNAME);
190 | $setOptsMethod = $class->getMethod('setOptions');
191 |
192 | $setOptsMethod->invoke($this->mock, $opts);
193 | }
194 |
195 | public function testGetAdapterOptionsWhenAdapterHasNotBeenSpecifiedShouldThrowRuntimeException()
196 | {
197 | $onlyOpts = [
198 | 'options' => ['opt1' => 'O1', 'opt2' => 'O2'],
199 | ];
200 | $filter = new Conversion($onlyOpts);
201 | $this->setExpectedException('Conversio\Exception\InvalidArgumentException');
202 | $filter->getAdapterOptions();
203 | }
204 |
205 | public function testGetAdapterOptionsWhenAdapterAbstractOptionsClassDoesNotExistShouldThrowDomainException()
206 | {
207 | $onlyOpts = [
208 | 'options' => ['opt1' => 'O1', 'opt2' => 'O2'],
209 | 'adapter' => 'ConversioTest\TestAsset\Adapter\ConvertNothing',
210 | ];
211 | $filter = new Conversion($onlyOpts);
212 | $this->setExpectedException(
213 | 'Conversio\Exception\DomainException',
214 | sprintf(
215 | '%s::getAbstractOptions" expects that an options class ("%s") for the current adapter exists',
216 | self::CLASSNAME,
217 | 'ConversioTest\TestAsset\Adapter\Options\ConvertNothingOptions'
218 | )
219 | );
220 | $filter->getAdapterOptions();
221 | }
222 |
223 | public function testGetAdapterOptionsWhenAdapterOptionsAreNotAbstractOptionsShouldThrowDomainException()
224 | {
225 | $onlyOpts = [
226 | 'options' => ['opt1' => 'O1', 'opt2' => 'O2'],
227 | 'adapter' => 'ConversioTest\TestAsset\Adapter\WrongAdapter',
228 | ];
229 | $filter = new Conversion($onlyOpts);
230 | $this->setExpectedException(
231 | 'Conversio\Exception\DomainException',
232 | sprintf(
233 | '%s::getAbstractOptions" expects the options class to resolve to a valid "%s" instance; received "%s"',
234 | self::CLASSNAME,
235 | 'Zend\Stdlib\AbstractOptions',
236 | 'ConversioTest\TestAsset\Adapter\Options\WrongAdapterOptions'
237 | )
238 | );
239 | $filter->getAdapterOptions();
240 | }
241 |
242 | public function testGetAdapterOptionsWhenNotMachingAdapterOptionsWasSetShouldThrowDomainException()
243 | {
244 | /** @var $mockOptions \Zend\Stdlib\AbstractOptions */
245 | $mockOptions = $this->getMockForAbstractClass('\Zend\Stdlib\AbstractOptions');
246 | $filter = new Conversion();
247 | $filter->setAdapter(new FakeAdapter());
248 | $filter->setAdapterOptions($mockOptions);
249 | $this->setExpectedException('Conversio\Exception\DomainException');
250 | $filter->getAdapterOptions();
251 | }
252 |
253 | public function testGetAdapterOptionsAndOptions()
254 | {
255 | $fakeAdapterOpts = new FakeAdapterOptions();
256 | $filter = new Conversion();
257 | $filter->setAdapter(new FakeAdapter());
258 | $filter->setAdapterOptions($fakeAdapterOpts);
259 | $this->assertInstanceOf(get_class($fakeAdapterOpts), $filter->getAdapterOptions());
260 | $this->assertEquals($fakeAdapterOpts, $filter->getAdapterOptions());
261 | $this->assertEquals($fakeAdapterOpts->toArray(), $filter->getOptions());
262 |
263 | $params = [
264 | 'adapterOptions' => ['fake' => 'fake'],
265 | 'adapter' => 'ConversioTest\TestAsset\Adapter\FakeAdapter',
266 | ];
267 | $filter = new Conversion($params);
268 | $this->assertInstanceOf(get_class($fakeAdapterOpts), $filter->getAdapterOptions());
269 | $this->assertEquals(new FakeAdapterOptions($params['adapterOptions']), $filter->getAdapterOptions());
270 | $this->assertEquals($params['adapterOptions'], $filter->getOptions());
271 |
272 |
273 | $filter = new Conversion();
274 | $filter->setAdapter(new FakeAdapter());
275 | $this->assertEmpty($filter->getOptions());
276 | $this->assertEquals($fakeAdapterOpts, $filter->getAdapterOptions());
277 | $this->assertEquals($fakeAdapterOpts->toArray(), $filter->getOptions());
278 |
279 | $this->assertEquals($fakeAdapterOpts->getFake(), $filter->getOptions('fake'));
280 |
281 | $this->setExpectedException('Conversio\Exception\RuntimeException');
282 | $filter->getOptions('not_exists');
283 | }
284 |
285 | public function testSetAdapterOptionsWithInvalidTypeInputShouldThrowInvalidArgumentException()
286 | {
287 | $filter = new Conversion();
288 | $this->setExpectedException(
289 | 'Conversio\Exception\InvalidArgumentException',
290 | sprintf(
291 | '"%s::setAdapterOptions" expects an array or a valid instance of "%s"; received "%s"',
292 | self::CLASSNAME,
293 | 'Zend\Stdlib\AbstractOptions',
294 | 'string'
295 | )
296 | );
297 | $filter->setAdapterOptions('invalid');
298 | }
299 |
300 | public function testFilter()
301 | {
302 | // Filtering a non string return the non strig input
303 | $notAString = [];
304 | $filter = new Conversion();
305 | $this->assertEquals($notAString, $filter->filter($notAString));
306 |
307 | // Filtering
308 | $input = 'string';
309 |
310 | $adapterMock = $this->getMock('Conversio\ConversionAlgorithmInterface');
311 |
312 | $this->mock->expects($this->at(0))
313 | ->method('getAdapter')
314 | ->willReturn($adapterMock);
315 |
316 | $adapterMock->expects($this->at(0))
317 | ->method('convert')
318 | ->with($this->equalTo($input));
319 |
320 | $class = new \ReflectionClass(self::CLASSNAME);
321 | $filterMethod = $class->getMethod('filter');
322 | $filterMethod->invoke($this->mock, $input);
323 | }
324 |
325 |
326 | public function testGetAdapterThatHaveOptions()
327 | {
328 | $adapterOpts = new FakeAdapterOptions(['fake' => 'ABC']);
329 | $adapter = new FakeAdapter();
330 |
331 | $filter = new Conversion();
332 | $filter->setAdapter($adapter);
333 | $filter->setAdapterOptions($adapterOpts);
334 |
335 | $this->assertSame($adapter, $filter->getAdapter());
336 | $this->assertSame($adapterOpts, $adapter->getOptions());
337 | }
338 | }
339 |
--------------------------------------------------------------------------------