From e7f28c868b813eb03419cc29ff7b56fa680563b0 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 14 Jun 2017 16:31:45 +0100 Subject: [PATCH 01/13] Added invalid data test for Copy to complete code coverage. --- test/Integration/Mapper/Strategy/CopyTest.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test/Integration/Mapper/Strategy/CopyTest.php b/test/Integration/Mapper/Strategy/CopyTest.php index 71ea419..0b43d46 100644 --- a/test/Integration/Mapper/Strategy/CopyTest.php +++ b/test/Integration/Mapper/Strategy/CopyTest.php @@ -6,7 +6,7 @@ final class CopyTest extends \PHPUnit_Framework_TestCase { - public function testWalkFixedPath() + public function testFixedPath() { $copy = (new Copy('foo->bar', ['foo' => ['bar' => 'baz']])) ->setMapper(new Mapper); @@ -14,11 +14,22 @@ public function testWalkFixedPath() self::assertSame('baz', $copy([])); } - public function testWalkStrategyPath() + public function testStrategyPath() { $copy = (new Copy(new Copy('foo'), ['bar' => 'baz'])) ->setMapper(new Mapper); self::assertSame('baz', $copy(['foo' => 'bar'])); } + + /** + * Tests that null is returned when the data parameter does not resolve to an array type. + */ + public function testInvalidData() + { + $copy = (new Copy('foo', 'bar')) + ->setMapper(new Mapper); + + self::assertNull($copy(['foo' => 'bar'])); + } } From ddc6a99e2f09ddb47b98108f26d49b9791a14d1f Mon Sep 17 00:00:00 2001 From: Bilge Date: Wed, 1 Nov 2017 19:23:16 +0000 Subject: [PATCH 02/13] Added data parameter to TryCatch exception handler. --- .travis.yml | 1 + README.md | 8 +++---- src/Strategy/Replace.php | 2 +- src/Strategy/TryCatch.php | 2 +- .../Mapper/Strategy/TryCatchTest.php | 22 ++++++++++--------- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2a3fb2b..0f7776d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ php: - 5.6 - 7.0 - 7.1 + - 7.2 env: matrix: diff --git a/README.md b/README.md index 7726527..4ec1d06 100644 --- a/README.md +++ b/README.md @@ -733,11 +733,11 @@ Merge(Strategy|Mapping|array|mixed $first, Strategy|Mapping|array|mixed $second) ### Replace -Replaces all occurrences one or more substrings. +Replaces all occurrences of one or more substrings. Any number of searches and replacements can be specified. Searches and replacements are parsed in pairs. If no replacements are specified, all matches are removed instead of replaced. If fewer replacements than searches are specified, the last replacement will be used for the remaining searches. If more replacements than searches are specified, the extra replacements are ignored. -Searches can be specified as either string literals or wrapped in an `Expression` and treated as a regular expression. `Expression` and string searches can be mixed as desired. Regular expression replacements can reference sub-matches, e.g. `$1`. +Searches can be specified as either string literals or wrapped in an `Expression` and treated as a regular expression. `Expression` and string searches can be mixed as desired. Regular expression replacements can reference sub-matches, e.g. `$1` specifies the first capturing group. #### Signature @@ -830,7 +830,7 @@ TryCatch(Strategy $strategy, callable $handler, Strategy|Mapping|array|mixed $ex ``` 1. `$strategy` – Primary strategy. - 2. `$handler` – Exception handler that receives the thrown exception as its first argument. + 2. `$handler` – Exception handler that receives the thrown exception as its first argument and data as its second. 3. `$expression` – Fallback expression. #### Examples @@ -844,7 +844,7 @@ TryCatch(Strategy $strategy, callable $handler, Strategy|Mapping|array|mixed $ex throw new \DomainException; } ), - function (\Exception $exception) { + function (\Exception $exception, array $data) { if (!$exception instanceof \DomainException) { throw $exception; } diff --git a/src/Strategy/Replace.php b/src/Strategy/Replace.php index fac7ac2..a58c20e 100644 --- a/src/Strategy/Replace.php +++ b/src/Strategy/Replace.php @@ -5,7 +5,7 @@ use ScriptFUSION\Mapper\Mapping; /** - * Replaces one or more substrings. + * Replaces all occurrences of one or more substrings. */ class Replace extends Delegate { diff --git a/src/Strategy/TryCatch.php b/src/Strategy/TryCatch.php index 1fad209..7bba399 100644 --- a/src/Strategy/TryCatch.php +++ b/src/Strategy/TryCatch.php @@ -32,7 +32,7 @@ public function __invoke($data, $context = null) try { return parent::__invoke($data, $context); } catch (\Exception $exception) { - call_user_func($this->handler, $exception); + call_user_func($this->handler, $exception, $data); return $this->delegate($this->expression, $data, $context); } diff --git a/test/Integration/Mapper/Strategy/TryCatchTest.php b/test/Integration/Mapper/Strategy/TryCatchTest.php index 9cb81ea..95959b0 100644 --- a/test/Integration/Mapper/Strategy/TryCatchTest.php +++ b/test/Integration/Mapper/Strategy/TryCatchTest.php @@ -11,7 +11,7 @@ final class TryCatchTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $this->callback = new Callback(function ($data) { + $this->callback = new Callback(function (array $data) { if ($data[0] instanceof \Exception) { throw $data[0]; } @@ -26,9 +26,11 @@ public function testTryCatch() $tryCatch = ( new TryCatch( $this->callback, - function (\Exception $e) { - if (!$e instanceof \DomainException) { - throw $e; + function (\Exception $exception, array $data) { + self::assertNotEmpty($data); + + if (!$exception instanceof \DomainException) { + throw $exception; } }, $fallback = 'bar' @@ -49,16 +51,16 @@ public function testNestedTryCatch() new TryCatch( new TryCatch( $this->callback, - function (\Exception $e) { - if (!$e instanceof \DomainException) { - throw $e; + function (\Exception $exception) { + if (!$exception instanceof \DomainException) { + throw $exception; } }, $innerFallback = 'bar' ), - function (\Exception $e) { - if (!$e instanceof \LogicException) { - throw $e; + function (\Exception $exception) { + if (!$exception instanceof \LogicException) { + throw $exception; } }, $outerFallback = 'baz' From 1b8326e238876db9e5f86533ef924f003316a433 Mon Sep 17 00:00:00 2001 From: Bilge Date: Sat, 2 Mar 2019 12:05:43 +0000 Subject: [PATCH 03/13] Added support for single expressions returning an array in Join stragegy. --- .travis.yml | 9 +++++---- README.md | 13 +++++++++++-- src/Strategy/Join.php | 13 ++++++++++--- test/Functional/DocumentationTest.php | 8 ++++++++ test/Integration/Mapper/Strategy/JoinTest.php | 19 +++++++++++++++++++ 5 files changed, 53 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0f7776d..00f4984 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ php: - 7.0 - 7.1 - 7.2 + - 7.3 env: matrix: @@ -21,16 +22,16 @@ matrix: cache: directories: - - .composer/cache + - vendor install: - alias composer=composer\ -n && composer selfupdate - composer validate - - composer update $DEPENDENCIES + - composer update --no-progress --no-suggest $DEPENDENCIES script: - composer test -- --coverage-clover=build/logs/clover.xml after_success: - - composer require satooshi/php-coveralls - - vendor/bin/coveralls -v + - composer require php-coveralls/php-coveralls:^2 + - vendor/bin/php-coveralls -v diff --git a/README.md b/README.md index 4ec1d06..903723d 100644 --- a/README.md +++ b/README.md @@ -682,7 +682,7 @@ $data = ['foo' => 'foo']; ### Join -Joins sub-string expressions together with a glue string. +Joins expressions together with a glue string. #### Signature @@ -691,7 +691,7 @@ Join(string $glue, array ...$expressions) ``` 1. `$glue` – Glue. - 2. `$expressions` – Sub-string expressions. + 2. `$expressions` – Expressions to join or a single expression that resolves to an array to join. #### Example @@ -704,6 +704,15 @@ Join(string $glue, array ...$expressions) > 'foo-bar' +```php +(new Mapper)->map( + ['foo' => ['bar', 'baz']], + new Join('-', new Copy('foo')) +); +``` + +> 'bar-baz' + ### Merge Merges two data sets together giving precedence to the latter if string keys collide; integer keys never collide. For more information see [array_merge](http://php.net/manual/en/function.array-merge.php). diff --git a/src/Strategy/Join.php b/src/Strategy/Join.php index 228ad9e..840deef 100644 --- a/src/Strategy/Join.php +++ b/src/Strategy/Join.php @@ -9,10 +9,10 @@ class Join extends Delegate private $glue; /** - * Initializes this instance with the specified glue to join the specified sub-strings together. + * Initializes this instance with the specified glue to join the specified expressions together. * * @param string $glue Glue. - * @param array ...$expressions Sub-string expressions. + * @param array ...$expressions Expressions to join or a single expression that resolves to an array to join. */ public function __construct($glue, ...$expressions) { @@ -23,6 +23,13 @@ public function __construct($glue, ...$expressions) public function __invoke($data, $context = null) { - return implode($this->glue, parent::__invoke($data, $context)); + $pieces = parent::__invoke($data, $context); + + if (count($pieces) === 1 && is_array($pieces[0])) { + // Unwrap. + $pieces = $pieces[0]; + } + + return implode($this->glue, $pieces); } } diff --git a/test/Functional/DocumentationTest.php b/test/Functional/DocumentationTest.php index 1e00b6c..0200e35 100644 --- a/test/Functional/DocumentationTest.php +++ b/test/Functional/DocumentationTest.php @@ -281,6 +281,14 @@ public function testJoin() new Join('-', new Copy('foo'), 'bar') ) ); + + self::assertSame( + 'bar-baz', + (new Mapper)->map( + ['foo' => ['bar', 'baz']], + new Join('-', new Copy('foo')) + ) + ); } public function testMerge() diff --git a/test/Integration/Mapper/Strategy/JoinTest.php b/test/Integration/Mapper/Strategy/JoinTest.php index d5e32e7..a0eb198 100644 --- a/test/Integration/Mapper/Strategy/JoinTest.php +++ b/test/Integration/Mapper/Strategy/JoinTest.php @@ -1,12 +1,18 @@ setMapper(new Mapper); + + self::assertSame('foo-bar', $join([])); + } } From 370029a0dd956c01bc3c9f46808fe044df372c66 Mon Sep 17 00:00:00 2001 From: Bilge Date: Sun, 29 Dec 2019 16:51:13 +0000 Subject: [PATCH 04/13] Added support for Enumeration v6. Removed support for PHP < 7.1. --- .travis.yml | 3 +-- composer.json | 6 +++--- src/DataType.php | 12 ++++++------ 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 00f4984..ee1d719 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,11 +6,10 @@ sudo: false language: php php: - - 5.6 - - 7.0 - 7.1 - 7.2 - 7.3 + - 7.4 env: matrix: diff --git a/composer.json b/composer.json index 30e6f6e..a366e21 100644 --- a/composer.json +++ b/composer.json @@ -9,14 +9,14 @@ ], "license": "LGPL-3.0", "require": { - "php": "^5.6|^7", + "php": "^7.1", "scriptfusion/array-walker": "^1", - "eloquent/enumeration": "^5" + "eloquent/enumeration": "^5|^6" }, "require-dev": { "scriptfusion/static-class": "^1", "phpunit/phpunit": "^4.8", - "mockery/mockery": "^0.9.4" + "mockery/mockery": "^1.3" }, "autoload": { "psr-4": { diff --git a/src/DataType.php b/src/DataType.php index be60995..7755bfc 100644 --- a/src/DataType.php +++ b/src/DataType.php @@ -6,12 +6,12 @@ /** * Specifies a PHP data type. * - * @method static BOOLEAN() - * @method static INTEGER() - * @method static FLOAT() - * @method static STRING() - * @method static MAP() - * @method static OBJECT() + * @method static self BOOLEAN + * @method static self INTEGER + * @method static self FLOAT + * @method static self STRING + * @method static self MAP + * @method static self OBJECT */ final class DataType extends AbstractEnumeration { From a00668116b907223df6627ef750fb0ddb2fd1717 Mon Sep 17 00:00:00 2001 From: Bilge Date: Wed, 16 Dec 2020 00:33:12 +0000 Subject: [PATCH 05/13] Added PHP 8 support. Dropped PHP 7.1 support. --- .travis.yml | 4 ++-- composer.json | 6 +++--- test/Functional/DocumentationTest.php | 3 ++- test/Functional/KeyPropagationTest.php | 3 ++- test/Functional/StrategyBasedMappingTest.php | 3 ++- test/Integration/Mapper/CollectionMapperTest.php | 7 ++++--- test/Integration/Mapper/MapperTest.php | 7 ++++--- test/Integration/Mapper/MappingTest.php | 5 +++-- test/Integration/Mapper/Strategy/CollectionTest.php | 3 ++- test/Integration/Mapper/Strategy/ContextTest.php | 3 ++- test/Integration/Mapper/Strategy/CopyTest.php | 3 ++- test/Integration/Mapper/Strategy/DebugTest.php | 7 ++++--- test/Integration/Mapper/Strategy/EitherTest.php | 5 +++-- test/Integration/Mapper/Strategy/IfElseTest.php | 7 ++++--- test/Integration/Mapper/Strategy/IfExistsTest.php | 3 ++- test/Integration/Mapper/Strategy/JoinTest.php | 3 ++- test/Integration/Mapper/Strategy/TryCatchTest.php | 9 +++++---- test/Unit/Mapper/DecoratorTest.php | 3 ++- test/Unit/Mapper/MapperAwareTraitTest.php | 3 ++- test/Unit/Mapper/Strategy/CallbackTest.php | 3 ++- test/Unit/Mapper/Strategy/CopyContextTest.php | 3 ++- test/Unit/Mapper/Strategy/CopyKeyTest.php | 3 ++- test/Unit/Mapper/Strategy/CopyTest.php | 3 ++- test/Unit/Mapper/Strategy/FilterTest.php | 3 ++- test/Unit/Mapper/Strategy/FlattenTest.php | 3 ++- test/Unit/Mapper/Strategy/MergeTest.php | 3 ++- test/Unit/Mapper/Strategy/ReplaceTest.php | 3 ++- test/Unit/Mapper/Strategy/TakeFirstTest.php | 3 ++- test/Unit/Mapper/Strategy/ToListTest.php | 5 +++-- test/Unit/Mapper/Strategy/TypeTest.php | 3 ++- test/Unit/Mapper/Strategy/UniqueTest.php | 3 ++- 31 files changed, 77 insertions(+), 48 deletions(-) diff --git a/.travis.yml b/.travis.yml index ee1d719..ce8ad05 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,10 @@ sudo: false language: php php: - - 7.1 - 7.2 - 7.3 - 7.4 + - 8.0 env: matrix: @@ -29,7 +29,7 @@ install: - composer update --no-progress --no-suggest $DEPENDENCIES script: - - composer test -- --coverage-clover=build/logs/clover.xml + - XDEBUG_MODE=coverage composer test -- --coverage-clover=build/logs/clover.xml after_success: - composer require php-coveralls/php-coveralls:^2 diff --git a/composer.json b/composer.json index a366e21..1332708 100644 --- a/composer.json +++ b/composer.json @@ -9,14 +9,14 @@ ], "license": "LGPL-3.0", "require": { - "php": "^7.1", + "php": "^7.2|^8", "scriptfusion/array-walker": "^1", "eloquent/enumeration": "^5|^6" }, "require-dev": { "scriptfusion/static-class": "^1", - "phpunit/phpunit": "^4.8", - "mockery/mockery": "^1.3" + "phpunit/phpunit": "^8.5|^9", + "mockery/mockery": "^1.3.3" }, "autoload": { "psr-4": { diff --git a/test/Functional/DocumentationTest.php b/test/Functional/DocumentationTest.php index 0200e35..ece8a3a 100644 --- a/test/Functional/DocumentationTest.php +++ b/test/Functional/DocumentationTest.php @@ -1,6 +1,7 @@ mapper = new CollectionMapper; } @@ -20,7 +21,7 @@ protected function setUp() */ public function testMapInvalidCollection() { - $this->setExpectedException(InvalidRecordException::class); + $this->expectException(InvalidRecordException::class); $this->mapper->mapCollection(new \ArrayIterator(['foo']))->valid(); } diff --git a/test/Integration/Mapper/MapperTest.php b/test/Integration/Mapper/MapperTest.php index 660fb8a..f4e888f 100644 --- a/test/Integration/Mapper/MapperTest.php +++ b/test/Integration/Mapper/MapperTest.php @@ -2,6 +2,7 @@ namespace ScriptFUSIONTest\Integration\Mapper; use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; +use PHPUnit\Framework\TestCase; use ScriptFUSION\Mapper\AnonymousMapping; use ScriptFUSION\Mapper\InvalidExpressionException; use ScriptFUSION\Mapper\Mapper; @@ -9,14 +10,14 @@ use ScriptFUSION\Mapper\Strategy\Copy; use ScriptFUSION\Mapper\Strategy\Strategy; -final class MapperTest extends \PHPUnit_Framework_TestCase +final class MapperTest extends TestCase { use MockeryPHPUnitIntegration; /** @var Mapper */ private $mapper; - protected function setUp() + protected function setUp(): void { $this->mapper = new Mapper; } @@ -75,7 +76,7 @@ public function testMapNull() public function testMapInvalidObject() { - $this->setExpectedException(InvalidExpressionException::class); + $this->expectException(InvalidExpressionException::class); $this->mapper->map([], (object)[]); } diff --git a/test/Integration/Mapper/MappingTest.php b/test/Integration/Mapper/MappingTest.php index 5135516..19ae5e6 100644 --- a/test/Integration/Mapper/MappingTest.php +++ b/test/Integration/Mapper/MappingTest.php @@ -1,12 +1,13 @@ setExpectedException(InvalidMappingException::class); + $this->expectException(InvalidMappingException::class); new AnonymousMapping(\Mockery::mock(Mapping::class)); } diff --git a/test/Integration/Mapper/Strategy/CollectionTest.php b/test/Integration/Mapper/Strategy/CollectionTest.php index ca9ecaa..791a9da 100644 --- a/test/Integration/Mapper/Strategy/CollectionTest.php +++ b/test/Integration/Mapper/Strategy/CollectionTest.php @@ -1,12 +1,13 @@ either = (new Either($this->strategy = \Mockery::spy(Strategy::class), 'bar'))->setMapper(new Mapper); } diff --git a/test/Integration/Mapper/Strategy/IfElseTest.php b/test/Integration/Mapper/Strategy/IfElseTest.php index 46eef53..5465743 100644 --- a/test/Integration/Mapper/Strategy/IfElseTest.php +++ b/test/Integration/Mapper/Strategy/IfElseTest.php @@ -1,15 +1,16 @@ condition = function ($data) { return array_key_exists('baz', $data) && $data['baz'] === 'qux'; @@ -36,7 +37,7 @@ public function testOnlyIf() public function testStrictness() { - $this->setExpectedException(InvalidConditionException::class); + $this->expectException(InvalidConditionException::class); $ifElse = (new IfElse( function () { diff --git a/test/Integration/Mapper/Strategy/IfExistsTest.php b/test/Integration/Mapper/Strategy/IfExistsTest.php index 804770d..a77932e 100644 --- a/test/Integration/Mapper/Strategy/IfExistsTest.php +++ b/test/Integration/Mapper/Strategy/IfExistsTest.php @@ -2,11 +2,12 @@ namespace ScriptFUSIONTest\Integration\Mapper\Strategy; use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; +use PHPUnit\Framework\TestCase; use ScriptFUSION\Mapper\Mapper; use ScriptFUSION\Mapper\Strategy\IfExists; use ScriptFUSION\Mapper\Strategy\Strategy; -final class IfExistsTest extends \PHPUnit_Framework_TestCase +final class IfExistsTest extends TestCase { use MockeryPHPUnitIntegration; diff --git a/test/Integration/Mapper/Strategy/JoinTest.php b/test/Integration/Mapper/Strategy/JoinTest.php index a0eb198..b957469 100644 --- a/test/Integration/Mapper/Strategy/JoinTest.php +++ b/test/Integration/Mapper/Strategy/JoinTest.php @@ -2,11 +2,12 @@ namespace ScriptFUSIONTest\Integration\Mapper\Strategy; use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; +use PHPUnit\Framework\TestCase; use ScriptFUSION\Mapper\Mapper; use ScriptFUSION\Mapper\Strategy\Join; use ScriptFUSION\Mapper\Strategy\Strategy; -final class JoinTest extends \PHPUnit_Framework_TestCase +final class JoinTest extends TestCase { use MockeryPHPUnitIntegration; diff --git a/test/Integration/Mapper/Strategy/TryCatchTest.php b/test/Integration/Mapper/Strategy/TryCatchTest.php index 95959b0..4597cea 100644 --- a/test/Integration/Mapper/Strategy/TryCatchTest.php +++ b/test/Integration/Mapper/Strategy/TryCatchTest.php @@ -1,15 +1,16 @@ callback = new Callback(function (array $data) { if ($data[0] instanceof \Exception) { @@ -40,7 +41,7 @@ function (\Exception $exception, array $data) { self::assertSame($data = ['foo'], $tryCatch($data)); self::assertSame($fallback, $tryCatch([new \DomainException])); - $this->setExpectedException(\RuntimeException::class); + $this->expectException(\RuntimeException::class); $tryCatch([new \RuntimeException]); } @@ -71,7 +72,7 @@ function (\Exception $exception) { self::assertSame($innerFallback, $tryCatch([new \DomainException])); self::assertSame($outerFallback, $tryCatch([new \LogicException])); - $this->setExpectedException(\RuntimeException::class); + $this->expectException(\RuntimeException::class); $tryCatch([new \RuntimeException]); } } diff --git a/test/Unit/Mapper/DecoratorTest.php b/test/Unit/Mapper/DecoratorTest.php index 79fffd9..81a88ec 100644 --- a/test/Unit/Mapper/DecoratorTest.php +++ b/test/Unit/Mapper/DecoratorTest.php @@ -1,10 +1,11 @@ toList = new ToList(null); } diff --git a/test/Unit/Mapper/Strategy/TypeTest.php b/test/Unit/Mapper/Strategy/TypeTest.php index ca8b4af..d363c89 100644 --- a/test/Unit/Mapper/Strategy/TypeTest.php +++ b/test/Unit/Mapper/Strategy/TypeTest.php @@ -2,12 +2,13 @@ namespace ScriptFUSIONTest\Unit\Mapper\Strategy; use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; +use PHPUnit\Framework\TestCase; use ScriptFUSION\Mapper\DataType; use ScriptFUSION\Mapper\Strategy\Strategy; use ScriptFUSION\Mapper\Strategy\Type; use ScriptFUSIONTest\MockFactory; -final class TypeTest extends \PHPUnit_Framework_TestCase +final class TypeTest extends TestCase { use MockeryPHPUnitIntegration; diff --git a/test/Unit/Mapper/Strategy/UniqueTest.php b/test/Unit/Mapper/Strategy/UniqueTest.php index 719421d..0728465 100644 --- a/test/Unit/Mapper/Strategy/UniqueTest.php +++ b/test/Unit/Mapper/Strategy/UniqueTest.php @@ -2,10 +2,11 @@ namespace ScriptFUSIONTest\Unit\Mapper\Strategy; use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; +use PHPUnit\Framework\TestCase; use ScriptFUSION\Mapper\Strategy\Unique; use ScriptFUSIONTest\MockFactory; -final class UniqueTest extends \PHPUnit_Framework_TestCase +final class UniqueTest extends TestCase { use MockeryPHPUnitIntegration; From 4cb4adffdefe4d93f55de6cce83e92afb8a31c40 Mon Sep 17 00:00:00 2001 From: Bilge Date: Sat, 6 Mar 2021 00:38:37 +0000 Subject: [PATCH 06/13] Added Trim strategy. --- .travis.yml | 2 -- src/Strategy/Delegate.php | 2 +- src/Strategy/Trim.php | 12 +++++++++++ test/Integration/Mapper/Strategy/TrimTest.php | 21 +++++++++++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 src/Strategy/Trim.php create mode 100644 test/Integration/Mapper/Strategy/TrimTest.php diff --git a/.travis.yml b/.travis.yml index ce8ad05..111e875 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,6 @@ notifications: email: false -sudo: false - language: php php: diff --git a/src/Strategy/Delegate.php b/src/Strategy/Delegate.php index 0f05425..c53d347 100644 --- a/src/Strategy/Delegate.php +++ b/src/Strategy/Delegate.php @@ -26,6 +26,6 @@ public function __invoke($data, $context = null) protected function delegate($strategy, $data, $context, $key = null) { - return $this->mapper->map($data, $strategy, $context, $key !== null ? $key : $this->key); + return $this->mapper->map($data, $strategy, $context, $key ?? $this->key); } } diff --git a/src/Strategy/Trim.php b/src/Strategy/Trim.php new file mode 100644 index 0000000..a79b505 --- /dev/null +++ b/src/Strategy/Trim.php @@ -0,0 +1,12 @@ +setMapper(new Mapper()); + + self::assertSame('foo', $trim([])); + } +} From 39f97208c6c4302b08ba7aaf9aea287d807557e9 Mon Sep 17 00:00:00 2001 From: Bilge Date: Thu, 26 Sep 2024 10:58:19 +0100 Subject: [PATCH 07/13] Upgraded PHPUnit 9 -> 10 and fixed tests. Removed Travis config in lieu of GHA. Dropped support for PHP < 8.1. --- .github/workflows/Tests.yaml | 55 +++++++++++++++++++++++ .gitignore | 1 + .travis.yml | 34 -------------- README.md | 10 ++--- composer.json | 4 +- src/Strategy/Copy.php | 2 +- src/Strategy/Replace.php | 4 +- test/Integration/Mapper/MapperTest.php | 2 +- test/Unit/Mapper/Strategy/ReplaceTest.php | 2 +- test/Unit/Mapper/Strategy/ToListTest.php | 8 ++-- test/phpunit.xml | 19 ++++---- 11 files changed, 82 insertions(+), 59 deletions(-) create mode 100644 .github/workflows/Tests.yaml delete mode 100644 .travis.yml diff --git a/.github/workflows/Tests.yaml b/.github/workflows/Tests.yaml new file mode 100644 index 0000000..765443a --- /dev/null +++ b/.github/workflows/Tests.yaml @@ -0,0 +1,55 @@ +name: Tests + +on: + push: + pull_request: + workflow_dispatch: + schedule: + - cron: 0 6 * * * + +jobs: + Test: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + php: + - 8.1 + - 8.2 + - 8.3 + dependencies: + - hi + - lo + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: xdebug + + - name: Validate composer.json + run: composer validate + + - name: Cache dependencies + id: composer-cache + uses: actions/cache@v4 + with: + path: vendor + key: php-${{ matrix.php }}-${{ matrix.dependencies }}-${{ hashFiles('composer.json') }} + restore-keys: php-${{ matrix.php }}-${{ matrix.dependencies }}- + + - name: Install dependencies ${{ matrix.dependencies == 'lo' && '(lowest)' || '' }} + run: composer update --no-interaction --no-progress + ${{ matrix.dependencies == 'lo' && '--prefer-lowest' || '' }} + + - name: Run test suite with coverage + run: composer test -- --coverage-clover=build/logs/clover.xml + + - name: Upload test coverage + uses: codecov/codecov-action@v4 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_ORG_TOKEN }} diff --git a/.gitignore b/.gitignore index 9b5d08a..f4469f0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.*/ +!/.github/ /vendor/ /composer.lock diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 111e875..0000000 --- a/.travis.yml +++ /dev/null @@ -1,34 +0,0 @@ -notifications: - email: false - -language: php - -php: - - 7.2 - - 7.3 - - 7.4 - - 8.0 - -env: - matrix: - - - - DEPENDENCIES=--prefer-lowest - -matrix: - fast_finish: true - -cache: - directories: - - vendor - -install: - - alias composer=composer\ -n && composer selfupdate - - composer validate - - composer update --no-progress --no-suggest $DEPENDENCIES - -script: - - XDEBUG_MODE=coverage composer test -- --coverage-clover=build/logs/clover.xml - -after_success: - - composer require php-coveralls/php-coveralls:^2 - - vendor/bin/php-coveralls -v diff --git a/README.md b/README.md index 903723d..e946807 100644 --- a/README.md +++ b/README.md @@ -926,7 +926,7 @@ Debug(Strategy|Mapping|array|mixed $expression) Requirements ------------ - - [PHP 5.6](http://php.net/) + - [PHP 8.1](http://php.net/) - [Composer](https://getcomposer.org/) Limitations @@ -947,10 +947,10 @@ in this document can be found in `DocumentationTest`. [Version image]: https://poser.pugx.org/scriptfusion/mapper/version "Latest version" [Downloads]: https://packagist.org/packages/scriptfusion/mapper [Downloads image]: https://poser.pugx.org/scriptfusion/mapper/downloads "Total downloads" - [Build]: http://travis-ci.org/ScriptFUSION/Mapper - [Build image]: https://travis-ci.org/ScriptFUSION/Mapper.svg?branch=master "Build status" - [Coverage]: https://coveralls.io/github/ScriptFUSION/Mapper - [Coverage image]: https://coveralls.io/repos/ScriptFUSION/Mapper/badge.svg "Test coverage" + [Build]: https://github.com/ScriptFUSION/Mapper/actions/workflows/Tests.yaml + [Build image]: https://github.com/ScriptFUSION/Mapper/actions/workflows/Tests.yaml/badge.svg "Build status" + [Coverage]: https://codecov.io/github/ScriptFUSION/Mapper + [Coverage image]: https://codecov.io/github/ScriptFUSION/Mapper/graph/badge.svg "Test coverage" [Style]: https://styleci.io/repos/59734709 [Style image]: https://styleci.io/repos/59734709/shield?style=flat "Code style" diff --git a/composer.json b/composer.json index 1332708..3f71ac6 100644 --- a/composer.json +++ b/composer.json @@ -9,13 +9,13 @@ ], "license": "LGPL-3.0", "require": { - "php": "^7.2|^8", + "php": "^8.1", "scriptfusion/array-walker": "^1", "eloquent/enumeration": "^5|^6" }, "require-dev": { "scriptfusion/static-class": "^1", - "phpunit/phpunit": "^8.5|^9", + "phpunit/phpunit": "^10.5", "mockery/mockery": "^1.3.3" }, "autoload": { diff --git a/src/Strategy/Copy.php b/src/Strategy/Copy.php index 9db6cae..47cb5a1 100644 --- a/src/Strategy/Copy.php +++ b/src/Strategy/Copy.php @@ -44,7 +44,7 @@ public function __invoke($record, $context = null) // Resolve the path expression. Path will always be an array after this block. if (!is_array($path = parent::__invoke($record, $context))) { // If it's not an array treat it as a delimited string; implicitly casts other scalar types. - $path = explode(self::PATH_SEPARATOR, $path); + $path = explode(self::PATH_SEPARATOR, (string)$path); } // Overwrite record with resolved data expression if set and ensure it is an array. diff --git a/src/Strategy/Replace.php b/src/Strategy/Replace.php index a58c20e..a3818a1 100644 --- a/src/Strategy/Replace.php +++ b/src/Strategy/Replace.php @@ -19,7 +19,7 @@ class Replace extends Delegate * Any number of searches and replacements can be specified. Searches and replacements are parsed in pairs. If no * replacements are specified, all matches are removed instead of replaced. If fewer replacements than searches are * specified, the last replacement will be used for the remaining searches. If more replacements than searches are - * specified, the extra replacements are ignored. + * specified, the extra replacements will be ignored. * * @param Strategy|Mapping|array|mixed $expression Expression to search in. * @param $searches string|Expression|array Search string(s). @@ -40,7 +40,7 @@ public function __invoke($data, $context = null) $replace = null; foreach ($this->searches as $search) { - $replace = count($replacements) ? array_shift($replacements) : $replace; + $replace = count($replacements) ? array_shift($replacements) : (string)$replace; if ($search instanceof Expression) { $output = preg_replace($search, $replace, $output); diff --git a/test/Integration/Mapper/MapperTest.php b/test/Integration/Mapper/MapperTest.php index f4e888f..41c88de 100644 --- a/test/Integration/Mapper/MapperTest.php +++ b/test/Integration/Mapper/MapperTest.php @@ -53,7 +53,7 @@ public function testMapScalars($scalar) self::assertSame($scalar, $mapped); } - public function provideScalars() + public static function provideScalars() { return [ 'string empty' => [''], diff --git a/test/Unit/Mapper/Strategy/ReplaceTest.php b/test/Unit/Mapper/Strategy/ReplaceTest.php index 0610edd..345fa74 100644 --- a/test/Unit/Mapper/Strategy/ReplaceTest.php +++ b/test/Unit/Mapper/Strategy/ReplaceTest.php @@ -21,7 +21,7 @@ public function testReplace($input, $search, $replace, $output) self::assertSame($output, $replace([])); } - public function provideReplacements() + public static function provideReplacements() { return [ 'Single removal' => ['foo', 'o', null, 'f'], diff --git a/test/Unit/Mapper/Strategy/ToListTest.php b/test/Unit/Mapper/Strategy/ToListTest.php index 77511ac..332bf1b 100644 --- a/test/Unit/Mapper/Strategy/ToListTest.php +++ b/test/Unit/Mapper/Strategy/ToListTest.php @@ -26,7 +26,7 @@ public function testMap() ] )); - self::assertSame([$map], $this->toList($map)); + self::assertSame([$map], $this->toList()); } public function testList() @@ -38,18 +38,18 @@ public function testList() ] )); - self::assertSame($map, $this->toList($map)); + self::assertSame($map, $this->toList()); } public function testScalar() { $this->toList->setMapper(MockFactory::mockMapper('foo')); - $this->toList(['foo']); + self::assertSame(['foo'], $this->toList()); } private function toList() { - return call_user_func_array($this->toList, func_get_args()); + return ($this->toList)([]); } } diff --git a/test/phpunit.xml b/test/phpunit.xml index 9d80b47..0652353 100644 --- a/test/phpunit.xml +++ b/test/phpunit.xml @@ -1,12 +1,13 @@ - - . - - - - ../src - - + + . + + + + + ../src + + From dafee01503742d9d90f684aa93014ff884123cf1 Mon Sep 17 00:00:00 2001 From: Bilge Date: Thu, 26 Sep 2024 19:58:18 +0100 Subject: [PATCH 08/13] Added Regex strategy. --- .gitignore | 1 + README.md | 52 ++++++++++++------- src/Strategy/Join.php | 5 +- src/Strategy/Regex.php | 22 ++++++++ .../BarBucketAddressToAddresesMapping.php | 20 ++----- test/Functional/DocumentationTest.php | 16 ++++++ test/MockFactory.php | 19 ++----- test/Unit/Mapper/Strategy/RegexTest.php | 25 +++++++++ 8 files changed, 109 insertions(+), 51 deletions(-) create mode 100644 src/Strategy/Regex.php create mode 100644 test/Unit/Mapper/Strategy/RegexTest.php diff --git a/.gitignore b/.gitignore index f4469f0..622a957 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ !/.github/ /vendor/ /composer.lock +*.cache diff --git a/README.md b/README.md index e946807..88ca6c3 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Contents 1. [IfExists](#ifexists) 1. [Join](#join) 1. [Merge](#merge) + 1. [Regex](#regex) 1. [Replace](#replace) 1. [TakeFirst](#takefirst) 1. [ToList](#tolist) @@ -227,16 +228,8 @@ class BarBucketAddressToAddresesMapping extends Mapping { return [ 'line1' => new Copy('Addresses->0->1'), - 'city' => new Callback( - function (array $data) { - return $this->extractCity($data['Addresses'][0][2]); - } - ), - 'postcode' => new Callback( - function (array $data) { - return $this->extractZipCode($data['Addresses'][0][2]); - } - ), + 'city' => new Callback(fn (array $data) => $this->extractCity($data['Addresses'][0][2])), + 'postcode' => new Regex(new Copy('Addresses->0->2'), '[.*\b(\d{5})]', 1), 'country' => 'US', ]; } @@ -245,19 +238,12 @@ class BarBucketAddressToAddresesMapping extends Mapping { return explode(',', $line, 2)[0]; } - - private function extractZipCode($line) - { - if (preg_match('[.*\b(\d{5})]', $line, $matches)) { - return $matches[1]; - } - } } ``` *Line1* can be copied straight from the input data and *country* can be hard-coded with a constant value because we assume it does not change. -City and postcode must be extracted from the last line of the address. For this we use `Callback` strategies that indirectly point to private methods of our mapping. Callbacks are only necessary because there are currently no included strategies to perform string splitting or regular expression matching. +City and postcode must be extracted from the last line of the address. For _city_, we use the `Callback` strategy that points to a private method of our mapping. A callback is necessary because there are currently no included strategies to perform string splitting. For _postcode_, we can use the [`Regex`](#regex) strategy. The anonymous function wrapper picks the relevant part of the input data to pass to our methods. The weakness of this solution is dereferencing non-existent values will cause PHP to generate *undefined index* notices whereas injecting `Copy` strategies would gracefully resolve to `null` if any part of the path does not exist. Therefore, the most elegant solution would be to create custom strategies to promote code reuse and avoid errors, but is beyond the scope of this demonstration. For more information see [writing strategies](#writing-strategies). @@ -302,6 +288,7 @@ The following strategies ship with Mapper and provide a suite of commonly used f - [IfExists](#ifexists) – Delegates to one expression or another depending on whether the specified condition maps to null. - [Join](#join) – Joins sub-string expressions together with a glue string. - [Merge](#merge) – Merges two data sets together giving precedence to the latter if keys collide. + - [Regex](#regex) – Captures a portion of a string using regular expression matching. - [Replace](#replace) – Replaces one or more substrings. - [TakeFirst](#takefirst) – Takes the first value from a collection one or more times. - [ToList](#tolist) – Converts data to a single-element list unless it is already a list. @@ -740,6 +727,35 @@ Merge(Strategy|Mapping|array|mixed $first, Strategy|Mapping|array|mixed $second) > [1, 2, 3, 3, 4, 5] +### Regex + +Captures a portion of a string using regular expression matching. + +#### Signature + +```php +Regex(Strategy|Mapping|array|mixed $expression, string $regex, int $capturingGroup = 0) +``` + +1. `$expression` – Expression to search in. +2. `$regex` – Regular expression, including delimiters. +3. `$capturingGroup` – Optional. Capturing group index to return. Defaults to whole matched expression. + +#### Example + +```php +(new Mapper)->map( + ['foo bar baz'], + new Replace( + new Copy(0), + '[\h(.+)\h]', + 1, + ) +) +``` + +> 'bar' + ### Replace Replaces all occurrences of one or more substrings. diff --git a/src/Strategy/Join.php b/src/Strategy/Join.php index 840deef..0f910c1 100644 --- a/src/Strategy/Join.php +++ b/src/Strategy/Join.php @@ -1,6 +1,8 @@ regex, parent::__invoke($data, $context), $matches)) { + return $matches[$this->capturingGroup]; + } + } +} diff --git a/test/Fixture/BarBucketAddressToAddresesMapping.php b/test/Fixture/BarBucketAddressToAddresesMapping.php index a38f096..54d8b95 100644 --- a/test/Fixture/BarBucketAddressToAddresesMapping.php +++ b/test/Fixture/BarBucketAddressToAddresesMapping.php @@ -4,6 +4,7 @@ use ScriptFUSION\Mapper\Mapping; use ScriptFUSION\Mapper\Strategy\Callback; use ScriptFUSION\Mapper\Strategy\Copy; +use ScriptFUSION\Mapper\Strategy\Regex; class BarBucketAddressToAddresesMapping extends Mapping { @@ -11,16 +12,8 @@ protected function createMapping() { return [ 'line1' => new Copy('Addresses->0->1'), - 'city' => new Callback( - function (array $data) { - return $this->extractCity($data['Addresses'][0][2]); - } - ), - 'postcode' => new Callback( - function (array $data) { - return $this->extractZipCode($data['Addresses'][0][2]); - } - ), + 'city' => new Callback(fn (array $data) => $this->extractCity($data['Addresses'][0][2])), + 'postcode' => new Regex(new Copy('Addresses->0->2'), '[.*\b(\d{5})]', 1), 'country' => 'US', ]; } @@ -29,11 +22,4 @@ private function extractCity($line) { return explode(',', $line, 2)[0]; } - - private function extractZipCode($line) - { - if (preg_match('[.*\b(\d{5})]', $line, $matches)) { - return $matches[1]; - } - } } diff --git a/test/Functional/DocumentationTest.php b/test/Functional/DocumentationTest.php index ece8a3a..5870969 100644 --- a/test/Functional/DocumentationTest.php +++ b/test/Functional/DocumentationTest.php @@ -18,6 +18,7 @@ use ScriptFUSION\Mapper\Strategy\IfExists; use ScriptFUSION\Mapper\Strategy\Join; use ScriptFUSION\Mapper\Strategy\Merge; +use ScriptFUSION\Mapper\Strategy\Regex; use ScriptFUSION\Mapper\Strategy\Replace; use ScriptFUSION\Mapper\Strategy\TakeFirst; use ScriptFUSION\Mapper\Strategy\ToList; @@ -306,6 +307,21 @@ public function testMerge() ); } + public function testRegex(): void + { + self::assertSame( + 'bar', + (new Mapper)->map( + ['foo bar baz'], + new Regex( + new Copy(0), + '[\h(.+)\h]', + 1, + ) + ) + ); + } + public function testReplace() { self::assertSame( diff --git a/test/MockFactory.php b/test/MockFactory.php index b44c0a5..a225dbf 100644 --- a/test/MockFactory.php +++ b/test/MockFactory.php @@ -3,30 +3,19 @@ use Mockery\MockInterface; use ScriptFUSION\Mapper\Mapper; -use ScriptFUSION\Mapper\Strategy\Strategy; use ScriptFUSION\StaticClass; final class MockFactory { use StaticClass; - /** - * @param mixed $data - * - * @return Strategy|MockInterface - */ - public static function mockStrategy($data) + public static function mockMapper(mixed $data): Mapper&MockInterface { - return \Mockery::mock(Strategy::class)->shouldReceive('__invoke')->andReturn($data)->getMock(); + return \Mockery::mock(Mapper::class)->shouldReceive('map')->andReturn($data)->getMock(); } - /** - * @param mixed $data - * - * @return Mapper|MockInterface - */ - public static function mockMapper($data) + public static function mockMapperEcho(): Mapper&MockInterface { - return \Mockery::mock(Mapper::class)->shouldReceive('map')->andReturn($data)->getMock(); + return \Mockery::mock(Mapper::class)->expects('map')->andReturnArg(1)->getMock(); } } diff --git a/test/Unit/Mapper/Strategy/RegexTest.php b/test/Unit/Mapper/Strategy/RegexTest.php new file mode 100644 index 0000000..a731afd --- /dev/null +++ b/test/Unit/Mapper/Strategy/RegexTest.php @@ -0,0 +1,25 @@ +setMapper(MockFactory::mockMapperEcho()); + + self::assertSame('Beta', $regex([])); + } + + public function testRegexNonMatch(): void + { + $regex = (new Regex('Alfa', '[Beta]'))->setMapper(MockFactory::mockMapperEcho()); + + self::assertNull($regex([])); + } +} From 45aa5540d5d08f5fe24d08b97d17a5466dd3cf71 Mon Sep 17 00:00:00 2001 From: Bilge Date: Mon, 25 Nov 2024 20:37:23 +0000 Subject: [PATCH 09/13] Added PHP 8.4 compatibility. BC: Changed DataType from AbstractEnumeration -> native enum. --- .github/workflows/Tests.yaml | 1 + README.md | 2 +- composer.json | 3 +-- src/DataType.php | 26 +++++++------------------- src/Strategy/Type.php | 2 +- test/Functional/DocumentationTest.php | 2 +- test/Unit/Mapper/Strategy/TypeTest.php | 2 +- 7 files changed, 13 insertions(+), 25 deletions(-) diff --git a/.github/workflows/Tests.yaml b/.github/workflows/Tests.yaml index 765443a..f2947d8 100644 --- a/.github/workflows/Tests.yaml +++ b/.github/workflows/Tests.yaml @@ -18,6 +18,7 @@ jobs: - 8.1 - 8.2 - 8.3 + - 8.4 dependencies: - hi - lo diff --git a/README.md b/README.md index 88ca6c3..2b1497e 100644 --- a/README.md +++ b/README.md @@ -897,7 +897,7 @@ Type(DataType $type, Strategy $strategy) #### Example ```php -(new Mapper)->map(['foo' => 123], new Type(DataType::STRING(), new Copy('foo'))); +(new Mapper)->map(['foo' => 123], new Type(DataType::String, new Copy('foo'))); ``` > '123' diff --git a/composer.json b/composer.json index 3f71ac6..028a282 100644 --- a/composer.json +++ b/composer.json @@ -10,8 +10,7 @@ "license": "LGPL-3.0", "require": { "php": "^8.1", - "scriptfusion/array-walker": "^1", - "eloquent/enumeration": "^5|^6" + "scriptfusion/array-walker": "^1" }, "require-dev": { "scriptfusion/static-class": "^1", diff --git a/src/DataType.php b/src/DataType.php index 7755bfc..d25e934 100644 --- a/src/DataType.php +++ b/src/DataType.php @@ -1,24 +1,12 @@ type"); + settype($data, $this->type->name); return $data; } diff --git a/test/Functional/DocumentationTest.php b/test/Functional/DocumentationTest.php index 5870969..37fe934 100644 --- a/test/Functional/DocumentationTest.php +++ b/test/Functional/DocumentationTest.php @@ -390,7 +390,7 @@ public function testType() { self::assertSame( '123', - (new Mapper)->map(['foo' => 123], new Type(DataType::STRING(), new Copy('foo'))) + (new Mapper)->map(['foo' => 123], new Type(DataType::String, new Copy('foo'))) ); } diff --git a/test/Unit/Mapper/Strategy/TypeTest.php b/test/Unit/Mapper/Strategy/TypeTest.php index d363c89..243aeab 100644 --- a/test/Unit/Mapper/Strategy/TypeTest.php +++ b/test/Unit/Mapper/Strategy/TypeTest.php @@ -14,7 +14,7 @@ final class TypeTest extends TestCase public function test() { - $type = new Type(DataType::INTEGER(), \Mockery::mock(Strategy::class)); + $type = new Type(DataType::Integer, \Mockery::mock(Strategy::class)); $type->setMapper(MockFactory::mockMapper('123')); self::assertSame(123, $type([])); From 7292fdba95e2a942cc3d988613bc0b4a66aff3ad Mon Sep 17 00:00:00 2001 From: Bilge Date: Sat, 14 Dec 2024 21:44:55 +0000 Subject: [PATCH 10/13] Fixed trimming null raising TypeError; now returns empty string. --- src/Strategy/Trim.php | 2 +- test/Integration/Mapper/Strategy/TrimTest.php | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Strategy/Trim.php b/src/Strategy/Trim.php index a79b505..5b590c7 100644 --- a/src/Strategy/Trim.php +++ b/src/Strategy/Trim.php @@ -7,6 +7,6 @@ class Trim extends Delegate { public function __invoke($data, $context = null) { - return trim(parent::__invoke($data, $context)); + return trim((string)parent::__invoke($data, $context)); } } diff --git a/test/Integration/Mapper/Strategy/TrimTest.php b/test/Integration/Mapper/Strategy/TrimTest.php index 678f209..dbd9bc6 100644 --- a/test/Integration/Mapper/Strategy/TrimTest.php +++ b/test/Integration/Mapper/Strategy/TrimTest.php @@ -18,4 +18,14 @@ public function test(): void self::assertSame('foo', $trim([])); } + + /** + * Tests that trimming null returns the empty string. + */ + public function testNull(): void + { + $trim = (new Trim(null))->setMapper(new Mapper()); + + self::assertSame('', $trim([])); + } } From 3812cf8f111996094e35e418aac6a1e3b4bcc771 Mon Sep 17 00:00:00 2001 From: Bilge Date: Sun, 10 Aug 2025 09:53:20 +0100 Subject: [PATCH 11/13] Added support for multiple capturing groups to Regex strategy. --- src/Strategy/Regex.php | 11 +++++++++-- test/Unit/Mapper/Strategy/RegexTest.php | 7 +++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Strategy/Regex.php b/src/Strategy/Regex.php index 7457983..3eaef5c 100644 --- a/src/Strategy/Regex.php +++ b/src/Strategy/Regex.php @@ -8,14 +8,21 @@ */ final class Regex extends Delegate { - public function __construct($expression, private readonly string $regex, private readonly int $capturingGroup = 0) - { + public function __construct( + $expression, + private readonly string $regex, + private readonly int|array $capturingGroup = 0, + ) { parent::__construct($expression); } public function __invoke($data, $context = null) { if (preg_match($this->regex, parent::__invoke($data, $context), $matches)) { + if (is_array($this->capturingGroup)) { + return array_values(array_intersect_key($matches, array_flip($this->capturingGroup))); + } + return $matches[$this->capturingGroup]; } } diff --git a/test/Unit/Mapper/Strategy/RegexTest.php b/test/Unit/Mapper/Strategy/RegexTest.php index a731afd..d38b835 100644 --- a/test/Unit/Mapper/Strategy/RegexTest.php +++ b/test/Unit/Mapper/Strategy/RegexTest.php @@ -22,4 +22,11 @@ public function testRegexNonMatch(): void self::assertNull($regex([])); } + + public function testRegexMatchArray(): void + { + $regex = (new Regex('Alfa Beta Charlie', '[(A\w+).+?(C\w+)]', [1, 2]))->setMapper(MockFactory::mockMapperEcho()); + + self::assertSame(['Alfa', 'Charlie'], $regex([])); + } } From d5944a30ff75ae1e042c1b609da2437245a5c65a Mon Sep 17 00:00:00 2001 From: Bilge Date: Sun, 7 Sep 2025 22:41:02 +0100 Subject: [PATCH 12/13] Fixed PHP 8.4 deprecation warning. --- src/Strategy/Filter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Strategy/Filter.php b/src/Strategy/Filter.php index 5ecde12..e6321f9 100644 --- a/src/Strategy/Filter.php +++ b/src/Strategy/Filter.php @@ -18,7 +18,7 @@ class Filter extends Delegate * @param callable|null $callback Callback function that receives the current value as its first argument, the * current key as its second argument and context as its third argument. */ - public function __construct($expression, callable $callback = null) + public function __construct($expression, ?callable $callback = null) { parent::__construct($expression); From 5a27bcd21b8cbb94627e55e2785e351387d587cd Mon Sep 17 00:00:00 2001 From: Bilge Date: Sat, 13 Dec 2025 13:31:11 +0000 Subject: [PATCH 13/13] Fixed TypeError in Regex strategy when expression evaluates to `null`. --- src/Strategy/Regex.php | 2 +- test/Unit/Mapper/Strategy/RegexTest.php | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Strategy/Regex.php b/src/Strategy/Regex.php index 3eaef5c..3d37842 100644 --- a/src/Strategy/Regex.php +++ b/src/Strategy/Regex.php @@ -18,7 +18,7 @@ public function __construct( public function __invoke($data, $context = null) { - if (preg_match($this->regex, parent::__invoke($data, $context), $matches)) { + if (preg_match($this->regex, parent::__invoke($data, $context) ?? '', $matches)) { if (is_array($this->capturingGroup)) { return array_values(array_intersect_key($matches, array_flip($this->capturingGroup))); } diff --git a/test/Unit/Mapper/Strategy/RegexTest.php b/test/Unit/Mapper/Strategy/RegexTest.php index d38b835..ccd2457 100644 --- a/test/Unit/Mapper/Strategy/RegexTest.php +++ b/test/Unit/Mapper/Strategy/RegexTest.php @@ -29,4 +29,11 @@ public function testRegexMatchArray(): void self::assertSame(['Alfa', 'Charlie'], $regex([])); } + + public function testRegexMatchNullExpression(): void + { + $regex = (new Regex(null, '[Alfa]'))->setMapper(MockFactory::mockMapperEcho()); + + self::assertNull($regex([])); + } }