diff --git a/composer.json b/composer.json index 3c28259b..d0a8aa52 100644 --- a/composer.json +++ b/composer.json @@ -92,6 +92,7 @@ "src/Functional/ReduceRight.php", "src/Functional/Reindex.php", "src/Functional/Reject.php", + "src/Functional/Repeat.php", "src/Functional/Retry.php", "src/Functional/Select.php", "src/Functional/SelectKeys.php", diff --git a/docs/functional-php.md b/docs/functional-php.md index 8dc3e5c7..37f327b5 100644 --- a/docs/functional-php.md +++ b/docs/functional-php.md @@ -65,6 +65,7 @@ - [const_function()](#const_function) - [id()](#id) - [tap()](#tap) + - [repeat()](#repeat) @@ -1024,7 +1025,7 @@ just a shortcut to `compare_on` as it composes the given key function with `spl_ # Miscellaneous ## concat() -Concatenates zero or more strings +Concatenates zero or more strings. ```php 1 ## id() -Proxy function, that do nothing, except returning its first argument +Proxy function that does nothing except returning its first argument. ```php login(); ``` +## repeat() + +Creates and returns a function that can be used to execute the given closure multiple times. + +```php + + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +namespace Functional; + +use Closure; +use Functional\Exceptions\InvalidArgumentException; + +/** + * Creates a function that can be used to repeat the execution of $callback. + * + * @param callable $callback + * + * @return Closure + */ +function repeat(callable $callback) +{ + return function ($times) use ($callback) { + InvalidArgumentException::assertPositiveInteger($times, __FUNCTION__, 1); + + for ($i = 0; $i < $times; $i++) { + $callback(); + } + }; +} diff --git a/tests/Functional/RepeatTest.php b/tests/Functional/RepeatTest.php new file mode 100644 index 00000000..3620c63c --- /dev/null +++ b/tests/Functional/RepeatTest.php @@ -0,0 +1,65 @@ + + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the 'Software'), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +namespace Functional\Tests; + +use Functional\Exceptions\InvalidArgumentException; +use function Functional\repeat; +use PHPUnit\Framework\MockObject\MockObject; + +class Repeated +{ + public function foo() + { + } +} + +class RepeatTest extends AbstractTestCase +{ + /** @var Repeated|MockObject */ + private $repeated; + + public function setUp() + { + parent::setUp(); + $this->repeated = $this->createMock(Repeated::class); + } + + public function test() + { + $this->repeated + ->expects($this->exactly(10)) + ->method('foo'); + + repeat([$this->repeated, 'foo'])(10); + } + + public function testNegativeRepeatedTimes() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( + 'Functional\{closure}() expects parameter 1 to be positive integer, negative integer given' + ); + + repeat([$this->repeated, 'foo'])(-1); + } +}