From 03cb7985fa0b1e861c666b93a19ab22560c5ef47 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Fri, 10 Dec 2021 00:49:16 +0100 Subject: [PATCH 01/10] tests: Update phpunit Otherwise it will not execute on PHP 7.4+. --- composer.json | 2 +- tests/NsplTest/A/LazyTest.php | 12 +- tests/NsplTest/ATest.php | 74 ++--- tests/NsplTest/ArgsTest.php | 324 ++++++++-------------- tests/NsplTest/{DSTest.php => DsTest.php} | 10 +- tests/NsplTest/FTest.php | 8 +- tests/NsplTest/NsplTest.php | 2 +- tests/NsplTest/OpTest.php | 2 +- tests/NsplTest/RndTest.php | 22 +- 9 files changed, 162 insertions(+), 294 deletions(-) rename tests/NsplTest/{DSTest.php => DsTest.php} (94%) diff --git a/composer.json b/composer.json index 8abec4e..e826c29 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ "files": ["autoload.php"] }, "require-dev": { - "phpunit/phpunit": "~5.0" + "phpunit/phpunit": "~7.0 || ~8.0 || ~9.0" }, "require": { "php": ">=7.1.0" diff --git a/tests/NsplTest/A/LazyTest.php b/tests/NsplTest/A/LazyTest.php index 30ca222..76aca92 100644 --- a/tests/NsplTest/A/LazyTest.php +++ b/tests/NsplTest/A/LazyTest.php @@ -38,7 +38,7 @@ use const \nspl\op\sum; -class LazyTest extends \PHPUnit_Framework_TestCase +class LazyTest extends \PHPUnit\Framework\TestCase { public function testMap() { @@ -148,14 +148,14 @@ public function testFilter() { $this->assertInstanceOf(\Generator::class, filter('is_numeric', ['a', 1, 'b', 2, 'c', 3])); - $this->assertEquals([1, 2, 3], iterator_to_array(filter('is_numeric', ['a', 1, 'b', 2, 'c', 3])), '', 0, 10, true); + $this->assertEqualsCanonicalizing([1, 2, 3], iterator_to_array(filter('is_numeric', ['a', 1, 'b', 2, 'c', 3]))); $this->assertEquals( array('b' => 2), iterator_to_array(filter(function($v) { return $v % 2 === 0; }, array('a' => 1, 'b' => 2, 'c' => 3))) ); $this->assertEquals([], iterator_to_array(filter('is_int', []))); - $this->assertEquals([1, 2, 3], iterator_to_array(call_user_func(filter, 'is_numeric', ['a', 1, 'b', 2, 'c', 3])), '', 0, 10, true); + $this->assertEqualsCanonicalizing([1, 2, 3], iterator_to_array(call_user_func(filter, 'is_numeric', ['a', 1, 'b', 2, 'c', 3]))); $this->assertEquals('\nspl\a\lazy\filter', filter); } @@ -163,14 +163,14 @@ public function testFilterNot() { $this->assertInstanceOf(\Generator::class, filterNot('is_numeric', ['a', 1, 'b', 2, 'c', 3])); - $this->assertEquals(['a', 'b', 'c'], iterator_to_array(filterNot('is_numeric', ['a', 1, 'b', 2, 'c', 3])), '', 0, 10, true); + $this->assertEqualsCanonicalizing(['a', 'b', 'c'], iterator_to_array(filterNot('is_numeric', ['a', 1, 'b', 2, 'c', 3]))); $this->assertEquals( array('a' => 1, 'c' => 3), iterator_to_array(filterNot(function($v) { return $v % 2 === 0; }, array('a' => 1, 'b' => 2, 'c' => 3))) ); $this->assertEquals([], iterator_to_array(filterNot('is_int', []))); - $this->assertEquals(['a', 'b', 'c'], iterator_to_array(call_user_func(filterNot, 'is_numeric', ['a', 1, 'b', 2, 'c', 3])), '', 0, 10, true); + $this->assertEqualsCanonicalizing(['a', 'b', 'c'], iterator_to_array(call_user_func(filterNot, 'is_numeric', ['a', 1, 'b', 2, 'c', 3]))); $this->assertEquals('\nspl\a\lazy\filterNot', filterNot); } @@ -234,7 +234,7 @@ public function testPartition() { $result = partition('is_numeric', ['a', 1, 'b', 2, 'c', 3]); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); $this->assertArrayHasKey(0, $result); $this->assertArrayHasKey(1, $result); $this->assertInstanceOf(\Generator::class, $result[0]); diff --git a/tests/NsplTest/ATest.php b/tests/NsplTest/ATest.php index 1b5be1b..11bf621 100644 --- a/tests/NsplTest/ATest.php +++ b/tests/NsplTest/ATest.php @@ -91,7 +91,7 @@ use const \nspl\a\getByKey; //endregion -class ATest extends \PHPUnit_Framework_TestCase +class ATest extends \PHPUnit\Framework\TestCase { public function testAll() { @@ -481,19 +481,15 @@ public function testFirst() $this->assertEquals('\nspl\a\first', first); } - /** - * @expectedException \InvalidArgumentException - */ public function testFirstForEmptySequence() { + $this->expectException(\InvalidArgumentException::class); first([]); } - /** - * @expectedException \InvalidArgumentException - */ public function testFirstForEmptyIterator() { + $this->expectException(\InvalidArgumentException::class); first(new \ArrayIterator([])); } @@ -507,11 +503,9 @@ public function testSecond() $this->assertEquals('\nspl\a\second', second); } - /** - * @expectedException \InvalidArgumentException - */ public function testFirstForSequenceWithOnlyOneItem() { + $this->expectException(\InvalidArgumentException::class); second([1]); } @@ -556,11 +550,9 @@ public function testLast() $this->assertEquals('\nspl\a\last', last); } - /** - * @expectedException \InvalidArgumentException - */ public function testLastForEmptyList() { + $this->expectException(\InvalidArgumentException::class); last([]); } @@ -574,27 +566,21 @@ public function testReorder() $this->assertEquals('\nspl\a\reorder', reorder); } - /** - * @expectedException \InvalidArgumentException - */ public function testReorderToInNotList() { + $this->expectException(\InvalidArgumentException::class); reorder(array(1 => 'a', 2 => 'b', 3 => 'c'), 1, 2); } - /** - * @expectedException \InvalidArgumentException - */ public function testReorderToInvalidPosition() { + $this->expectException(\InvalidArgumentException::class); reorder([0, 1, 2], 0, 3); } - /** - * @expectedException \InvalidArgumentException - */ public function testReorderFromInvalidPosition() { + $this->expectException(\InvalidArgumentException::class); reorder([0, 1, 2], 3, 0); } @@ -632,15 +618,15 @@ public function testDiff() $object1 = arrayobject(1, 2, 3); $object2 = arrayobject(2, 3, 4); - $this->assertEquals([1], diff($array1, $array2), '', 0, 10, true); - $this->assertEquals([1], diff($array1, $object2), '', 0, 10, true); - $this->assertEquals([1], diff($object1, $array2), '', 0, 10, true); - $this->assertEquals([1], diff($object1, $object2), '', 0, 10, true); + $this->assertEqualsCanonicalizing([1], diff($array1, $array2)); + $this->assertEqualsCanonicalizing([1], diff($array1, $object2)); + $this->assertEqualsCanonicalizing([1], diff($object1, $array2)); + $this->assertEqualsCanonicalizing([1], diff($object1, $object2)); - $this->assertEquals([4], call_user_func(diff, $array2, $array1), '', 0, 10, true); - $this->assertEquals([4], call_user_func(diff, $array2, $object1), '', 0, 10, true); - $this->assertEquals([4], call_user_func(diff, $object2, $array1), '', 0, 10, true); - $this->assertEquals([4], call_user_func(diff, $object2, $object1), '', 0, 10, true); + $this->assertEqualsCanonicalizing([4], call_user_func(diff, $array2, $array1)); + $this->assertEqualsCanonicalizing([4], call_user_func(diff, $array2, $object1)); + $this->assertEqualsCanonicalizing([4], call_user_func(diff, $object2, $array1)); + $this->assertEqualsCanonicalizing([4], call_user_func(diff, $object2, $object1)); $this->assertEquals('\nspl\a\diff', diff); } @@ -651,15 +637,15 @@ public function testIntersect() $object1 = arrayobject(1, 2, 3); $object2 = arrayobject(2, 3, 4); - $this->assertEquals([2, 3], intersect($array1, $array2), '', 0, 10, true); - $this->assertEquals([2, 3], intersect($array1, $object2), '', 0, 10, true); - $this->assertEquals([2, 3], intersect($object1, $array2), '', 0, 10, true); - $this->assertEquals([2, 3], intersect($object1, $object2), '', 0, 10, true); + $this->assertEqualsCanonicalizing([2, 3], intersect($array1, $array2)); + $this->assertEqualsCanonicalizing([2, 3], intersect($array1, $object2)); + $this->assertEqualsCanonicalizing([2, 3], intersect($object1, $array2)); + $this->assertEqualsCanonicalizing([2, 3], intersect($object1, $object2)); - $this->assertEquals([2, 3], call_user_func(intersect, $array2, $array1), '', 0, 10, true); - $this->assertEquals([2, 3], call_user_func(intersect, $array2, $object1), '', 0, 10, true); - $this->assertEquals([2, 3], call_user_func(intersect, $object2, $array1), '', 0, 10, true); - $this->assertEquals([2, 3], call_user_func(intersect, $object2, $object1), '', 0, 10, true); + $this->assertEqualsCanonicalizing([2, 3], call_user_func(intersect, $array2, $array1)); + $this->assertEqualsCanonicalizing([2, 3], call_user_func(intersect, $array2, $object1)); + $this->assertEqualsCanonicalizing([2, 3], call_user_func(intersect, $object2, $array1)); + $this->assertEqualsCanonicalizing([2, 3], call_user_func(intersect, $object2, $object1)); $this->assertEquals('\nspl\a\intersect', intersect); } @@ -770,27 +756,21 @@ public function testMoveElement() $this->assertEquals('\nspl\a\reorder', moveElement); } - /** - * @expectedException \InvalidArgumentException - */ public function testMoveElementToInNotList() { + $this->expectException(\InvalidArgumentException::class); moveElement(array(1 => 'a', 2 => 'b', 3 => 'c'), 1, 2); } - /** - * @expectedException \InvalidArgumentException - */ public function testMoveElementToInvalidPosition() { + $this->expectException(\InvalidArgumentException::class); moveElement([0, 1, 2], 0, 3); } - /** - * @expectedException \InvalidArgumentException - */ public function testMoveElementFromInvalidPosition() { + $this->expectException(\InvalidArgumentException::class); moveElement([0, 1, 2], 3, 0); } //endregion diff --git a/tests/NsplTest/ArgsTest.php b/tests/NsplTest/ArgsTest.php index e6e418f..cb6bc0d 100644 --- a/tests/NsplTest/ArgsTest.php +++ b/tests/NsplTest/ArgsTest.php @@ -53,7 +53,7 @@ use function \nspl\args\expectsWithKeys; use function \nspl\args\expectsToBe; -class ArgsTest extends \PHPUnit_Framework_TestCase +class ArgsTest extends \PHPUnit\Framework\TestCase { // Or-constraints #region bool @@ -64,12 +64,10 @@ function expectsBoolPositiveTest($arg) { expects(bool, $arg); } $this->assertNull(expectsBoolPositiveTest(false)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsBoolNegativeTest() must be a boolean, integer 1 given - */ public function testExpectsBool_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsBoolNegativeTest() must be a boolean, integer 1 given'); function expectsBoolNegativeTest($arg) { expects(bool, $arg); } $this->assertNull(expectsBoolNegativeTest(1)); } @@ -83,12 +81,10 @@ function expectsIntPositiveTest($arg1, $arg2) { expects(int, $arg2); } $this->assertNull(expectsIntPositiveTest('hello world', 0)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 2 passed to NsplTest\expectsIntNegativeTest() must be an integer, string '1' given - */ public function testExpectsInt_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 2 passed to NsplTest\\expectsIntNegativeTest() must be an integer, string '1' given"); function expectsIntNegativeTest($arg1, $arg2) { expects(int, $arg2); } $this->assertNull(expectsIntNegativeTest('hello world', '1')); } @@ -101,12 +97,10 @@ function expectsFloatPositiveTest($arg1, $arg2) { expects(float, $arg1); } $this->assertNull(expectsFloatPositiveTest(1.0, 'hello')); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsFloatNegativeTest() must be a float, string 'hello' given - */ public function testExpectsFloat_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsFloatNegativeTest() must be a float, string 'hello' given"); function expectsFloatNegativeTest($arg1, $arg2) { expects(float, $arg1); } $this->assertNull(expectsFloatNegativeTest('hello', 'world')); } @@ -121,12 +115,10 @@ function expectsNumericPositiveTest($arg1, $arg2) { expects(numeric, $arg2); } $this->assertNull(expectsNumericPositiveTest('number -> ', '1')); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 2 passed to NsplTest\expectsNumericNegativeTest() must be numeric, string 'world' given - */ public function testExpectsNumeric_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 2 passed to NsplTest\\expectsNumericNegativeTest() must be numeric, string 'world' given"); function expectsNumericNegativeTest($arg1, $arg2) { expects(numeric, $arg2); } $this->assertNull(expectsNumericNegativeTest('hello', 'world')); } @@ -139,12 +131,10 @@ function expectsStringPositiveTest($arg1, $arg2) { expects(string, $arg2, 2); } $this->assertNull(expectsStringPositiveTest(42, 'answer')); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 2 passed to NsplTest\expectsStringNegativeTest() must be a string, integer 42 given - */ public function testExpectsString_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 2 passed to NsplTest\\expectsStringNegativeTest() must be a string, integer 42 given'); function expectsStringNegativeTest($arg1, $arg2) { expects(string, $arg2, 2); } $this->assertNull(expectsStringNegativeTest(42, 42)); } @@ -158,12 +148,10 @@ function expectsArrayKeyPositiveTest($arg1, $arg2) { expects(arrayKey, $arg1); } $this->assertNull(expectsArrayKeyPositiveTest('answer', 42)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsArrayKeyNegativeTest() must be an integer or a string, double 2 given - */ public function testExpectsArrayKey_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsArrayKeyNegativeTest() must be an integer or a string, double 2 given'); function expectsArrayKeyNegativeTest($arg1, $arg2) { expects(arrayKey, $arg1); } $this->assertNull(expectsArrayKeyNegativeTest(2.0, 2.0)); } @@ -177,12 +165,10 @@ function expectsTraversablePositiveTest($arg1) { expects(traversable, $arg1); } $this->assertNull(expectsTraversablePositiveTest(new \ArrayIterator(array('hello', 'world')))); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsTraversableNegativeTest() must be an array or traversable, string 'hello world' given - */ public function testExpectsTraversable_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsTraversableNegativeTest() must be an array or traversable, string 'hello world' given"); function expectsTraversableNegativeTest($arg1) { expects(traversable, $arg1); } $this->assertNull(expectsTraversableNegativeTest('hello world')); } @@ -196,12 +182,10 @@ function expectsArrayAccessPositiveTest($arg1) { expects(arrayAccess, $arg1); } $this->assertNull(expectsTraversablePositiveTest(new \ArrayObject(array('hello', 'world')))); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsArrayAccessNegativeTest() must be an array or implement array access, string 'hello world' given - */ public function testExpectsArrayAccess_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsArrayAccessNegativeTest() must be an array or implement array access, string 'hello world' given"); function expectsArrayAccessNegativeTest($arg1) { expects(arrayAccess, $arg1); } $this->assertNull(expectsArrayAccessNegativeTest('hello world')); } @@ -215,12 +199,10 @@ function expectsUserDefinedTypePositiveTest($arg1) { expects([int, TestClass1::c $this->assertNull(expectsUserDefinedTypePositiveTest(new TestClass1())); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsUserDefinedTypeNegativeTest() must be an integer or be NsplTest\TestClass1, string 'hello world' given - */ public function testExpectsWithCustomType_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsUserDefinedTypeNegativeTest() must be an integer or be NsplTest\\TestClass1, string 'hello world' given"); function expectsUserDefinedTypeNegativeTest($arg1) { expects([int, TestClass1::class], $arg1); } $this->assertNull(expectsUserDefinedTypeNegativeTest('hello world')); } @@ -232,12 +214,10 @@ function expectsTwoClassesPositiveTest($arg1) { expects([TestClass1::class, Test $this->assertNull(expectsTwoClassesPositiveTest(new TestClass2())); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsTwoClassesNegativeTest() must be NsplTest\TestClass1 or be NsplTest\TestClass2, integer 1 given - */ public function testExpectsTwoClasses_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsTwoClassesNegativeTest() must be NsplTest\\TestClass1 or be NsplTest\\TestClass2, integer 1 given'); function expectsTwoClassesNegativeTest($arg1) { expects([TestClass1::class, TestClass2::class], $arg1); } $this->assertNull(expectsTwoClassesNegativeTest(1)); } @@ -251,12 +231,10 @@ function expectsNonEmptyPositiveTest($arg) { expects(nonEmpty, $arg); } $this->assertNull(expectsNonEmptyPositiveTest(true)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsNonEmptyNegativeTest() must not be empty, integer 0 given - */ public function testExpectsNonEmpty_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsNonEmptyNegativeTest() must not be empty, integer 0 given'); function expectsNonEmptyNegativeTest($arg) { expects(nonEmpty, $arg); } $this->assertNull(expectsNonEmptyNegativeTest(0)); } @@ -269,12 +247,10 @@ function expectsPositivePositiveTest($arg) { expects([positive, int], $arg); } $this->assertNull(expectsPositivePositiveTest(1)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsPositiveNegativeTest() must be an integer and be positive, integer 0 given - */ public function testExpectsPositive_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsPositiveNegativeTest() must be an integer and be positive, integer 0 given'); function expectsPositiveNegativeTest($arg) { expects([positive, int], $arg); } $this->assertNull(expectsPositiveNegativeTest(0)); } @@ -288,12 +264,10 @@ function expectsNonNegativePositiveTest($arg) { expects([nonNegative, int], $arg $this->assertNull(expectsNonNegativePositiveTest(0)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsNonNegativeNegativeTest() must be an integer and be non-negative, integer -1 given - */ public function testExpectsNonNegative_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsNonNegativeNegativeTest() must be an integer and be non-negative, integer -1 given'); function expectsNonNegativeNegativeTest($arg) { expects([nonNegative, int], $arg); } $this->assertNull(expectsNonNegativeNegativeTest(-1)); } @@ -307,12 +281,10 @@ function expectsNonZeroPositiveTest($arg) { expects([nonZero, int], $arg); } $this->assertNull(expectsNonZeroPositiveTest(-1)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsNonZeroNegativeTest() must be an integer and be non-zero, integer 0 given - */ public function testExpectsNonZero_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsNonZeroNegativeTest() must be an integer and be non-zero, integer 0 given'); function expectsNonZeroNegativeTest($arg) { expects([nonZero, int], $arg); } $this->assertNull(expectsNonZeroNegativeTest(0)); } @@ -326,12 +298,10 @@ function expectsValuesPositiveTest($arg) { expects(values('hello', 'world'), $ar $this->assertNull(expectsValuesPositiveTest('world')); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsValuesNegativeTest() must be one of the following values 'hello', 'world', integer 1 given - */ public function testExpectsValues_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsValuesNegativeTest() must be one of the following values 'hello', 'world', integer 1 given"); function expectsValuesNegativeTest($arg) { expects(values('hello', 'world'), $arg); } $this->assertNull(expectsValuesNegativeTest(1)); } @@ -344,12 +314,10 @@ function expectsLongerThanPositiveTest($arg1) { expects(longerThan(6), $arg1); } $this->assertNull(expectsLongerThanPositiveTest('hello world')); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsLongerThanNegativeTest() must be longer than 6, string 'hello' given - */ public function testExpectsLongerThan_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsLongerThanNegativeTest() must be longer than 6, string 'hello' given"); function expectsLongerThanNegativeTest($arg1) { expects(longerThan(6), $arg1); } $this->assertNull(expectsLongerThanNegativeTest('hello')); } @@ -362,12 +330,10 @@ function expectsShorterThanPositiveTest($arg1) { expects(shorterThan(6), $arg1); $this->assertNull(expectsShorterThanPositiveTest('hello')); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsShorterThanNegativeTest() must be shorter than 6, string 'hello world' given - */ public function testExpectsShorterThan_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsShorterThanNegativeTest() must be shorter than 6, string 'hello world' given"); function expectsShorterThanNegativeTest($arg1) { expects(shorterThan(6), $arg1); } $this->assertNull(expectsShorterThanNegativeTest('hello world')); } @@ -380,12 +346,10 @@ function expectsBiggerThanPositiveTest($arg1) { expects(biggerThan(2), $arg1); } $this->assertNull(expectsBiggerThanPositiveTest(3)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsBiggerThanNegativeTest() must be bigger than 2, integer 1 given - */ public function testExpectsBiggerThan_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsBiggerThanNegativeTest() must be bigger than 2, integer 1 given'); function expectsBiggerThanNegativeTest($arg1) { expects(biggerThan(2), $arg1); } $this->assertNull(expectsBiggerThanNegativeTest(1)); } @@ -398,12 +362,10 @@ function expectsSmallerThanPositiveTest($arg1) { expects(smallerThan(2), $arg1); $this->assertNull(expectsSmallerThanPositiveTest(1)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsSmallerThanNegativeTest() must be smaller than 2, integer 3 given - */ public function testExpectsSmallerThan_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsSmallerThanNegativeTest() must be smaller than 2, integer 3 given'); function expectsSmallerThanNegativeTest($arg1) { expects(smallerThan(2), $arg1); } $this->assertNull(expectsSmallerThanNegativeTest(3)); } @@ -416,12 +378,10 @@ function expectsWithMethodPositiveTest($arg1) { expects(withMethod('testMethod1' $this->assertNull(expectsWithMethodPositiveTest(new TestClass1())); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsWithMethodNegativeTest() must be an object with public method(s) 'test_Method_1', NsplTest\TestClass1 given - */ public function testExpectsWithMethod_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsWithMethodNegativeTest() must be an object with public method(s) 'test_Method_1', NsplTest\\TestClass1 given"); function expectsWithMethodNegativeTest($arg1) { expects(withMethod('test_Method_1'), $arg1); } $this->assertNull(expectsWithMethodNegativeTest(new TestClass1())); } @@ -434,12 +394,10 @@ function expectsWithMethodsPositiveTest($arg1) { expects(withMethods('testMethod $this->assertNull(expectsWithMethodsPositiveTest(new TestClass1())); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsWithMethodsNegativeTest() must be an object with public method(s) 'testMethod1', 'test_Method_2', NsplTest\TestClass1 given - */ public function testExpectsWithMethods_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsWithMethodsNegativeTest() must be an object with public method(s) 'testMethod1', 'test_Method_2', NsplTest\\TestClass1 given"); function expectsWithMethodsNegativeTest($arg1) { expects(withMethods('testMethod1', 'test_Method_2'), $arg1); } $this->assertNull(expectsWithMethodsNegativeTest(new TestClass1())); } @@ -452,12 +410,10 @@ function expectsWithKeyPositiveTest($arg1) { expects(withKey('hello'), $arg1); } $this->assertNull(expectsWithKeyPositiveTest(array('hello' => 'world'))); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsWithKeyNegativeTest() must be an array with key(s) 'answer' - */ public function testExpectsWithKey_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsWithKeyNegativeTest() must be an array with key(s) 'answer'"); function expectsWithKeyNegativeTest($arg1) { expects(withKey('answer'), $arg1); } $this->assertNull(expectsWithKeyNegativeTest(array('hello' => 'world'))); } @@ -473,12 +429,10 @@ function expectsWithKeysPositiveTest($arg1) { expects(withKeys('hello', 'answer' ))); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsWithKeysNegativeTest() must be an array with key(s) 'hello', 'answer' - */ public function testExpectsWithKeys_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsWithKeysNegativeTest() must be an array with key(s) 'hello', 'answer'"); function expectsWithKeysNegativeTest($arg1) { expects(withKeys('hello', 'answer'), $arg1); } $this->assertNull(expectsWithKeysNegativeTest(array('hello' => 'world'))); } @@ -492,12 +446,10 @@ function expectsNotPositivePositiveTest($arg) { expects(not(positive), $arg); } $this->assertNull(expectsNotPositivePositiveTest(-1)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsNotPositiveNegativeTest() must not be positive, integer 1 given - */ public function testExpectsNotPositive_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsNotPositiveNegativeTest() must not be positive, integer 1 given'); function expectsNotPositiveNegativeTest($arg) { expects(not(positive), $arg); } $this->assertNull(expectsNotPositiveNegativeTest(1)); } @@ -511,12 +463,10 @@ function expectsAnyPositiveTest($arg) { expects(any(shorterThan(6), longerThan(1 $this->assertNull(expectsAnyPositiveTest('hello world')); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsAnyNegativeTest() must be shorter than 6 or be longer than 10, string 'answer' given - */ public function testExpectsAny_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsAnyNegativeTest() must be shorter than 6 or be longer than 10, string 'answer' given"); function expectsAnyNegativeTest($arg) { expects(any(shorterThan(6), longerThan(10)), $arg); } $this->assertNull(expectsAnyNegativeTest('answer')); } @@ -530,12 +480,10 @@ function expectsAllPositiveTest($arg) { expects(all(I1::class, I2::class), $arg) $this->assertNull(expectsAllPositiveTest(new TestClass2())); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsAllNegativeTest() must be NsplTest\I1 and be NsplTest\I2, string 'hello world' given - */ public function testExpectsAll_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsAllNegativeTest() must be NsplTest\\I1 and be NsplTest\\I2, string 'hello world' given"); function expectsAllNegativeTest($arg) { expects(all(I1::class, I2::class), $arg); } $this->assertNull(expectsAllNegativeTest('hello world')); } @@ -551,12 +499,10 @@ function expectsArrayAccessOrStringPositiveTest($arg1) { expects([arrayAccess, s $this->assertNull(expectsArrayAccessOrStringPositiveTest('hello world')); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsArrayAccessOrStringNegativeTest() must be an array or implement array access or be a string, integer 1337 given - */ public function testExpectsArrayAccessOrString_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsArrayAccessOrStringNegativeTest() must be an array or implement array access or be a string, integer 1337 given'); function expectsArrayAccessOrStringNegativeTest($arg1) { expects([arrayAccess, string], $arg1); } $this->assertNull(expectsArrayAccessOrStringNegativeTest(1337)); } @@ -569,12 +515,10 @@ function expectsArrayKeyOrCallablePositiveTest($arg1) { expects([arrayKey, calla $this->assertNull(expectsArrayKeyOrCallablePositiveTest(function() { return 'answer 42'; })); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsArrayKeyOrCallableNegativeTest() must be an integer or a string or be callable, double 2 given - */ public function testExpectsArrayKeyOrCallable_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsArrayKeyOrCallableNegativeTest() must be an integer or a string or be callable, double 2 given'); function expectsArrayKeyOrCallableNegativeTest($arg1) { expects([arrayKey, callable_], $arg1); } $this->assertNull(expectsArrayKeyOrCallableNegativeTest(2.0)); } @@ -586,12 +530,10 @@ function expectsBoolOrCallablePositiveTest($arg1) { expects([bool, callable_], $ $this->assertNull(expectsBoolOrCallablePositiveTest(function() { return 'answer 42'; })); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsBoolOrCallableNegativeTest() must be a boolean or be callable, double 2 given - */ public function testExpectsBoolOrCallable_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsBoolOrCallableNegativeTest() must be a boolean or be callable, double 2 given'); function expectsBoolOrCallableNegativeTest($arg1) { expects([bool, callable_], $arg1); } $this->assertNull(expectsBoolOrCallableNegativeTest(2.0)); } @@ -604,22 +546,18 @@ function expectsAndRequirementAndOrRequirementPositiveTest($arg1) { expects([non $this->assertNull(expectsAndRequirementAndOrRequirementPositiveTest('hello world')); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsAndRequirementAndOrRequirementNegativeTest1() must be a string and not be empty, string '' given - */ public function testExpectsAndRequirementAndOrRequirement_Negative1() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsAndRequirementAndOrRequirementNegativeTest1() must be a string and not be empty, string '' given"); function expectsAndRequirementAndOrRequirementNegativeTest1($arg1) { expects([nonEmpty, string], $arg1); } $this->assertNull(expectsAndRequirementAndOrRequirementNegativeTest1('')); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsAndRequirementAndOrRequirementNegativeTest2() must be a string and not be empty, integer 1 given - */ public function testExpectsAndRequirementAndOrRequirement_Negative2() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsAndRequirementAndOrRequirementNegativeTest2() must be a string and not be empty, integer 1 given'); function expectsAndRequirementAndOrRequirementNegativeTest2($arg1) { expects([nonEmpty, string], $arg1); } $this->assertNull(expectsAndRequirementAndOrRequirementNegativeTest2(1)); } @@ -634,12 +572,10 @@ function expectsOptionalIntPositiveTest($arg1 = null) { expectsOptional([int], $ $this->assertNull(expectsOptionalIntPositiveTest(2)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsOptionalIntNegativeTest() must be an integer, string '1' given - */ public function testExpectsOptionalInt_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsOptionalIntNegativeTest() must be an integer, string '1' given"); function expectsOptionalIntNegativeTest($arg1) { expectsOptional([int], $arg1); } $this->assertNull(expectsOptionalIntNegativeTest('1')); } @@ -652,12 +588,10 @@ function expectsIntsPositiveTest($x, $y) { expectsAll(int, [$x, $y]); } $this->assertNull(expectsIntsPositiveTest(1, 2)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsIntsNegativeTest() must be an integer, string '1' given - */ public function testExpectsInts_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsIntsNegativeTest() must be an integer, string '1' given"); function expectsIntsNegativeTest($x, $y) { expectsAll(int, [$x, $y]); } $this->assertNull(expectsIntsNegativeTest('1', 2)); } @@ -670,28 +604,24 @@ function expectsCustomConstraintPositiveTest($year) { expects(validYear, $year); $this->assertNull(expectsCustomConstraintPositiveTest(2000)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsCustomConstraintNegativeTest() must be valid year, integer 1000 given - */ public function testCustomConstraint_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsCustomConstraintNegativeTest() must be valid year, integer 1000 given'); function expectsCustomConstraintNegativeTest($year) { expects(validYear, $year); } $this->assertNull(expectsCustomConstraintNegativeTest(1000)); } #endregion #region Custom exception - /** - * @expectedException \BadFunctionCallException - * @expectedExceptionMessage Function NsplTest\expectsWithCustomExceptionTest() does not like the given input - */ public function testExpectsWithCustomException() { + $this->expectException(\BadFunctionCallException::class); + $this->expectExceptionMessage('Function NsplTest\\expectsWithCustomExceptionTest() does not like the given input'); function expectsWithCustomExceptionTest($arg1) { expects(bool, $arg1, 1, new \BadFunctionCallException( - 'Function NsplTest\expectsWithCustomExceptionTest() does not like the given input' + 'Function NsplTest\\expectsWithCustomExceptionTest() does not like the given input' )); } @@ -720,12 +650,10 @@ function deprecatedExpectsNotEmptyPositiveTest($arg) { expectsNotEmpty($arg); } $this->assertNull(deprecatedExpectsNotEmptyPositiveTest(true)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\deprecatedExpectsNotEmptyNegativeTest() must not be empty, integer 0 given - */ public function testDeprecatedExpectsNotEmpty_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\deprecatedExpectsNotEmptyNegativeTest() must not be empty, integer 0 given'); function deprecatedExpectsNotEmptyNegativeTest($arg) { expectsNotEmpty($arg); } $this->assertNull(deprecatedExpectsNotEmptyNegativeTest(0)); } @@ -737,12 +665,10 @@ function deprecatedExpectsBoolPositiveTest($arg) { expectsBool($arg); } $this->assertNull(deprecatedExpectsBoolPositiveTest(false)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\deprecatedExpectsBoolNegativeTest() must be a boolean, integer 1 given - */ public function testDeprecatedExpectsBool_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\deprecatedExpectsBoolNegativeTest() must be a boolean, integer 1 given'); function deprecatedExpectsBoolNegativeTest($arg) { expectsBool($arg); } $this->assertNull(deprecatedExpectsBoolNegativeTest(1)); } @@ -754,12 +680,10 @@ function deprecatedExpectsIntPositiveTest($arg1, $arg2) { expectsInt($arg2); } $this->assertNull(deprecatedExpectsIntPositiveTest('hello world', 0)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 2 passed to NsplTest\deprecatedExpectsIntNegativeTest() must be an integer, string '1' given - */ public function testDeprecatedExpectsInt_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 2 passed to NsplTest\\deprecatedExpectsIntNegativeTest() must be an integer, string '1' given"); function deprecatedExpectsIntNegativeTest($arg1, $arg2) { expectsInt($arg2); } $this->assertNull(deprecatedExpectsIntNegativeTest('hello world', '1')); } @@ -770,12 +694,10 @@ function deprecatedExpectsFloatPositiveTest($arg1, $arg2) { expectsFloat($arg1); $this->assertNull(deprecatedExpectsFloatPositiveTest(1.0, 'hello')); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\deprecatedExpectsFloatNegativeTest() must be a float, string 'hello' given - */ public function testDeprecatedExpectsFloat_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\deprecatedExpectsFloatNegativeTest() must be a float, string 'hello' given"); function deprecatedExpectsFloatNegativeTest($arg1, $arg2) { expectsFloat($arg1); } $this->assertNull(deprecatedExpectsFloatNegativeTest('hello', 'world')); } @@ -788,12 +710,10 @@ function deprecatedExpectsNumericPositiveTest($arg1, $arg2) { expectsNumeric($ar $this->assertNull(deprecatedExpectsNumericPositiveTest('number -> ', '1')); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 2 passed to NsplTest\deprecatedExpectsNumericNegativeTest() must be numeric, string 'world' given - */ public function testDeprecatedExpectsNumeric_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 2 passed to NsplTest\\deprecatedExpectsNumericNegativeTest() must be numeric, string 'world' given"); function deprecatedExpectsNumericNegativeTest($arg1, $arg2) { expectsNumeric($arg2); } $this->assertNull(deprecatedExpectsNumericNegativeTest('hello', 'world')); } @@ -804,12 +724,10 @@ function deprecatedExpectsStringPositiveTest($arg1, $arg2) { expectsString($arg2 $this->assertNull(deprecatedExpectsStringPositiveTest(42, 'answer')); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 2 passed to NsplTest\deprecatedExpectsStringNegativeTest() must be a string, integer 42 given - */ public function testDeprecatedExpectsString_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 2 passed to NsplTest\\deprecatedExpectsStringNegativeTest() must be a string, integer 42 given'); function deprecatedExpectsStringNegativeTest($arg1, $arg2) { expectsString($arg2, 2); } $this->assertNull(deprecatedExpectsStringNegativeTest(42, 42)); } @@ -821,12 +739,10 @@ function deprecatedExpectsArrayKeyPositiveTest($arg1, $arg2) { expectsArrayKey($ $this->assertNull(deprecatedExpectsArrayKeyPositiveTest('answer', 42)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\deprecatedExpectsArrayKeyNegativeTest() must be an integer or a string, double 2 given - */ public function testDeprecatedExpectsArrayKey_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\deprecatedExpectsArrayKeyNegativeTest() must be an integer or a string, double 2 given'); function deprecatedExpectsArrayKeyNegativeTest($arg1, $arg2) { expectsArrayKey($arg1); } $this->assertNull(deprecatedExpectsArrayKeyNegativeTest(2.0, 2.0)); } @@ -838,12 +754,10 @@ function deprecatedExpectsTraversablePositiveTest($arg1) { expectsTraversable($a $this->assertNull(deprecatedExpectsTraversablePositiveTest(new \ArrayIterator(array('hello', 'world')))); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\deprecatedExpectsTraversableNegativeTest() must be an array or traversable, string 'hello world' given - */ public function testDeprecatedExpectsTraversable_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\deprecatedExpectsTraversableNegativeTest() must be an array or traversable, string 'hello world' given"); function deprecatedExpectsTraversableNegativeTest($arg1) { expectsTraversable($arg1); } $this->assertNull(deprecatedExpectsTraversableNegativeTest('hello world')); } @@ -855,12 +769,10 @@ function deprecatedExpectsArrayAccessPositiveTest($arg1) { expectsArrayAccess($a $this->assertNull(deprecatedExpectsTraversablePositiveTest(new \ArrayObject(array('hello', 'world')))); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\deprecatedExpectsArrayAccessNegativeTest() must be an array or implement array access, string 'hello world' given - */ public function testDeprecatedExpectsArrayAccess_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\deprecatedExpectsArrayAccessNegativeTest() must be an array or implement array access, string 'hello world' given"); function deprecatedExpectsArrayAccessNegativeTest($arg1) { expectsArrayAccess($arg1); } $this->assertNull(deprecatedExpectsArrayAccessNegativeTest('hello world')); } @@ -873,12 +785,10 @@ function deprecatedExpectsArrayAccessOrStringPositiveTest($arg1) { expectsArrayA $this->assertNull(deprecatedExpectsArrayAccessOrStringPositiveTest('hello world')); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\deprecatedExpectsArrayAccessOrStringNegativeTest() must be a string, an array or implement array access, integer 1337 given - */ public function testDeprecatedExpectsArrayAccessOrString_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\deprecatedExpectsArrayAccessOrStringNegativeTest() must be a string, an array or implement array access, integer 1337 given'); function deprecatedExpectsArrayAccessOrStringNegativeTest($arg1) { expectsArrayAccessOrString($arg1); } $this->assertNull(deprecatedExpectsArrayAccessOrStringNegativeTest(1337)); } @@ -891,12 +801,10 @@ function deprecatedExpectsArrayKeyOrCallablePositiveTest($arg1) { expectsArrayKe $this->assertNull(deprecatedExpectsArrayKeyOrCallablePositiveTest(function() { return 'answer 42'; })); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\deprecatedExpectsArrayKeyOrCallableNegativeTest() must be an integer or a string or a callable, double 2 given - */ public function testDeprecatedExpectsArrayKeyOrCallable_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\deprecatedExpectsArrayKeyOrCallableNegativeTest() must be an integer or a string or a callable, double 2 given'); function deprecatedExpectsArrayKeyOrCallableNegativeTest($arg1) { expectsArrayKeyOrCallable($arg1); } $this->assertNull(deprecatedExpectsArrayKeyOrCallableNegativeTest(2.0)); } @@ -908,12 +816,10 @@ function deprecatedExpectsBoolOrCallablePositiveTest($arg1) { expectsBoolOrCalla $this->assertNull(deprecatedExpectsBoolOrCallablePositiveTest(function() { return 'answer 42'; })); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\deprecatedExpectsBoolOrCallableNegativeTest() must be boolean or callable, double 2 given - */ public function testDeprecatedExpectsBoolOrCallable_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\deprecatedExpectsBoolOrCallableNegativeTest() must be boolean or callable, double 2 given'); function deprecatedExpectsBoolOrCallableNegativeTest($arg1) { expectsBoolOrCallable($arg1); } $this->assertNull(deprecatedExpectsBoolOrCallableNegativeTest(2.0)); } @@ -924,12 +830,10 @@ function deprecatedExpectsWithMethodPositiveTest($arg1) { expectsWithMethod($arg $this->assertNull(deprecatedExpectsWithMethodPositiveTest(new TestClass1())); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\deprecatedExpectsWithMethodNegativeTest() must be an object with public method "test_Method_1", NsplTest\TestClass1 given - */ public function testDeprecatedExpectWithMethod_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\deprecatedExpectsWithMethodNegativeTest() must be an object with public method "test_Method_1", NsplTest\\TestClass1 given'); function deprecatedExpectsWithMethodNegativeTest($arg1) { expectsWithMethod($arg1, 'test_Method_1'); } $this->assertNull(deprecatedExpectsWithMethodNegativeTest(new TestClass1())); } @@ -940,12 +844,10 @@ function deprecatedExpectsWithMethodsPositiveTest($arg1) { expectsWithMethods($a $this->assertNull(deprecatedExpectsWithMethodsPositiveTest(new TestClass1())); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\deprecatedExpectsWithMethodsNegativeTest() must be an object with public methods "testMethod1", "test_Method_2", NsplTest\TestClass1 given - */ public function testDeprecatedExpectWithMethods_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\deprecatedExpectsWithMethodsNegativeTest() must be an object with public methods "testMethod1", "test_Method_2", NsplTest\\TestClass1 given'); function deprecatedExpectsWithMethodsNegativeTest($arg1) { expectsWithMethods($arg1, ['testMethod1', 'test_Method_2']); } $this->assertNull(deprecatedExpectsWithMethodsNegativeTest(new TestClass1())); } @@ -959,12 +861,10 @@ function deprecatedExpectsWithKeysPositiveTest($arg1) { expectsWithKeys($arg1, [ ))); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\deprecatedExpectsWithKeysNegativeTest() must be an array with keys "hello", "answer" - */ public function testDeprecatedExpectWithKeys_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\deprecatedExpectsWithKeysNegativeTest() must be an array with keys "hello", "answer"'); function deprecatedExpectsWithKeysNegativeTest($arg1) { expectsWithKeys($arg1, ['hello', 'answer']); } $this->assertNull(deprecatedExpectsWithKeysNegativeTest(array('hello' => 'world'))); } @@ -981,12 +881,10 @@ function deprecatedExpectsPositiveTest($arg1) $this->assertNull(deprecatedExpectsPositiveTest(42)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\deprecatedExpectsNegativeTest() has to be a positive integer, integer -1 given - */ public function testDeprecatedExpects_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\deprecatedExpectsNegativeTest() has to be a positive integer, integer -1 given'); function deprecatedExpectsNegativeTest($arg1) { expects($arg1, 'a positive integer', function($arg) { @@ -997,16 +895,14 @@ function deprecatedExpectsNegativeTest($arg1) $this->assertNull(deprecatedExpectsNegativeTest(-1)); } - /** - * @expectedException \BadFunctionCallException - * @expectedExceptionMessage Function NsplTest\deprecatedExpectsWithCustomExceptionTest() does not like the given input - */ public function testDeprecatedExpectsWithCustomException() { + $this->expectException(\BadFunctionCallException::class); + $this->expectExceptionMessage('Function NsplTest\\deprecatedExpectsWithCustomExceptionTest() does not like the given input'); function deprecatedExpectsWithCustomExceptionTest($arg1) { expectsBool($arg1, 1, new \BadFunctionCallException( - 'Function NsplTest\deprecatedExpectsWithCustomExceptionTest() does not like the given input' + 'Function NsplTest\\deprecatedExpectsWithCustomExceptionTest() does not like the given input' )); } @@ -1025,12 +921,10 @@ function expectsCustomPositiveTest($arg1) $this->assertNull(expectsCustomPositiveTest(42)); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument 1 passed to NsplTest\expectsCustomNegativeTest() has to be a positive integer, integer -1 given - */ public function testExpectsCustom_Negative() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Argument 1 passed to NsplTest\\expectsCustomNegativeTest() has to be a positive integer, integer -1 given'); function expectsCustomNegativeTest($arg1) { expectsToBe($arg1, 'a positive integer', function($arg) { diff --git a/tests/NsplTest/DSTest.php b/tests/NsplTest/DsTest.php similarity index 94% rename from tests/NsplTest/DSTest.php rename to tests/NsplTest/DsTest.php index beb90ed..3adc903 100644 --- a/tests/NsplTest/DSTest.php +++ b/tests/NsplTest/DsTest.php @@ -14,7 +14,7 @@ use function \nspl\ds\isList; //endregion -class DsTest extends \PHPUnit_Framework_TestCase +class DsTest extends \PHPUnit\Framework\TestCase { public function testArrayObject() { @@ -67,7 +67,7 @@ public function testDefaultArray() $this->assertEquals(10, $array['bananas']); $array = new DefaultArray(function() { return time(); }); - $this->assertEquals(time(), $array['apples'], '', 0.1); + $this->assertEqualsWithDelta(time(), $array['apples'], 0.1); $array = defaultarray(0); $this->assertEquals(0, $array['apples']); @@ -79,7 +79,7 @@ public function testDefaultArray() $this->assertEquals(10, $array['bananas']); $array = defaultarray(function() { return time(); }); - $this->assertEquals(time(), $array['apples'], '', 0.1); + $this->assertEqualsWithDelta(time(), $array['apples'], 0.1); $array = new DefaultArray(10, array('apples' => 20, 'bananas' => 30)); $this->assertEquals(10, $array['oranges']); @@ -128,10 +128,10 @@ public function testSet() $this->assertEquals([1, 2], $intersection->toArray()); $union = $set->union([1, 2, 3]); - $this->assertEquals([1, 2, 3, 'world'], $union->toArray(), '', 0, 10, true); + $this->assertEqualsCanonicalizing([1, 2, 3, 'world'], $union->toArray()); $union = $set->union(Set::fromArray([1, 2, 3])); - $this->assertEquals([1, 2, 3, 'world'], $union->toArray(), '', 0, 10, true); + $this->assertEqualsCanonicalizing([1, 2, 3, 'world'], $union->toArray()); $this->assertTrue($set->isSubset($union)); $this->assertFalse($set->isSubset([1, 2, 3, 4])); diff --git a/tests/NsplTest/FTest.php b/tests/NsplTest/FTest.php index 8a97436..0d929bc 100644 --- a/tests/NsplTest/FTest.php +++ b/tests/NsplTest/FTest.php @@ -43,12 +43,12 @@ use const \nspl\f\span; //endregion -class FTest extends \PHPUnit_Framework_TestCase +class FTest extends \PHPUnit\Framework\TestCase { public function testApply() { $this->assertEquals([1, 3, 5, 7, 9], apply('range', [1, 10, 2])); - $this->assertEquals(time(), apply('time'), '', 0.1); + $this->assertEqualsWithDelta(time(), apply('time'), 0.1); $this->assertEquals([1, 3, 5, 7, 9], call_user_func(apply, 'range', [1, 10, 2])); $this->assertEquals('\nspl\f\apply', apply); @@ -76,7 +76,7 @@ public function testPartial() $this->assertEquals(3, $oneArgFuncPartial()); $noArgFuncPartial = partial('locale_get_default', null); - $this->assertEquals(locale_get_default(), $noArgFuncPartial(), '', 0.1); + $this->assertEqualsWithDelta(locale_get_default(), $noArgFuncPartial(), 0.1); $sqrList = call_user_func(partial, 'array_map', function($v) { return $v * $v; }); $this->assertEquals([1, 4, 9], $sqrList([1, 2, 3])); @@ -93,7 +93,7 @@ public function testRpartial() $this->assertEquals(3, $oneArgFuncPartial()); $noArgFuncPartial = rpartial('locale_get_default', null); - $this->assertEquals(locale_get_default(), $noArgFuncPartial(), '', 0.1); + $this->assertEqualsWithDelta(locale_get_default(), $noArgFuncPartial(), 0.1); $cube = call_user_func(rpartial, 'pow', 3); $this->assertEquals(27, $cube(3)); diff --git a/tests/NsplTest/NsplTest.php b/tests/NsplTest/NsplTest.php index 29de944..407f62e 100644 --- a/tests/NsplTest/NsplTest.php +++ b/tests/NsplTest/NsplTest.php @@ -4,7 +4,7 @@ use function \nspl\getType; -class NsplTest extends \PHPUnit_Framework_TestCase +class NsplTest extends \PHPUnit\Framework\TestCase { public function testGetType() { diff --git a/tests/NsplTest/OpTest.php b/tests/NsplTest/OpTest.php index ca5710d..0fdc358 100644 --- a/tests/NsplTest/OpTest.php +++ b/tests/NsplTest/OpTest.php @@ -47,7 +47,7 @@ use function \nspl\a\map; use function nspl\f\partial; -class OpTest extends \PHPUnit_Framework_TestCase +class OpTest extends \PHPUnit\Framework\TestCase { public function testSum() { diff --git a/tests/NsplTest/RndTest.php b/tests/NsplTest/RndTest.php index 1e3fe82..f0752b3 100644 --- a/tests/NsplTest/RndTest.php +++ b/tests/NsplTest/RndTest.php @@ -10,7 +10,7 @@ use function \nspl\rnd\choice; use function \nspl\rnd\weightedChoice; -class RndTest extends \PHPUnit_Framework_TestCase +class RndTest extends \PHPUnit\Framework\TestCase { public function testRandomString() { @@ -49,15 +49,13 @@ public function testSample() } foreach ($appearances as $item => $rate) { - $this->assertEquals(30000, $rate, '', 500); + $this->assertEqualsWithDelta(30000, $rate, 500); } } - /** - * @expectedException \InvalidArgumentException - */ public function testSampleOfPopulationLessThanLength() { + $this->expectException(\InvalidArgumentException::class); sample(['a', 'b', 'c'], 4); } @@ -74,15 +72,13 @@ public function testChoice() } foreach ($appearances as $item => $rate) { - $this->assertEquals(10000, $rate, '', 300); + $this->assertEqualsWithDelta(10000, $rate, 300); } } - /** - * @expectedException \InvalidArgumentException - */ public function testChoiceFromEmptySequence() { + $this->expectException(\InvalidArgumentException::class); choice([]); } @@ -104,7 +100,7 @@ public function testWeightedChoice() } foreach ($appearances as $item => $rate) { - $this->assertEquals(50000 * $weights[$item] / 100, $rate, '', 300); + $this->assertEqualsWithDelta(50000 * $weights[$item] / 100, $rate, 300); } } @@ -126,15 +122,13 @@ public function testWeightedChoiceWithNonIntegerWeights() } foreach ($appearances as $item => $rate) { - $this->assertEquals(50000 * $weights[$item], $rate, '', 300); + $this->assertEqualsWithDelta(50000 * $weights[$item], $rate, 300); } } - /** - * @expectedException \InvalidArgumentException - */ public function testWeightedChoiceFromEmptySequence() { + $this->expectException(\InvalidArgumentException::class); weightedChoice([]); } From 750c47cbbd6558530472df650734e4e875a874c5 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Fri, 10 Dec 2021 01:28:19 +0100 Subject: [PATCH 02/10] composer: Add a test script So that we can just run `composer test` to execute tests. --- composer.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/composer.json b/composer.json index e826c29..e3d6be2 100644 --- a/composer.json +++ b/composer.json @@ -22,5 +22,8 @@ "branch-alias": { "dev-master": "2.0-dev" } + }, + "scripts": { + "test": "phpunit --verbose tests/" } } From d7ce2ddabbf13c0371fd06445e3a7524b91d9c7f Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Fri, 10 Dec 2021 01:35:13 +0100 Subject: [PATCH 03/10] Add CI for running tests --- .github/workflows/tests.yaml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/tests.yaml diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 0000000..767b2c2 --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,32 @@ +name: Tests + +on: + pull_request: + push: + +jobs: + run: + runs-on: ubuntu-latest + strategy: + matrix: + php-version: + - '7.1' + - '7.2' + - '7.3' + - '7.4' + - '8.0' + - '8.1' + steps: + - uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + tools: php-cs-fixer, phpunit:${{ matrix.phpunit-versions }} + + - name: Install dependencies + run: composer install + + - name: Run tests + run: composer test From d5f71de72816504b96c2c007cb18567b9731d64d Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Fri, 10 Dec 2021 02:09:02 +0100 Subject: [PATCH 04/10] Fix calling {p,r,}partial with no args MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The no args test actually passed null to the partial, which started to fail as PHP got stricter in 8.0. Let’s actually make partial function accept no arguments to get rid of the edge case and then we will be able to test it properly. Let’s also return to the original time function for consistency: https://github.com/ihor/NSPL/commit/cd9e331494c1f90e016f8c1ded6eff8f28e68f50 --- nspl/f.php | 31 ++++++++++++------------------- tests/NsplTest/FTest.php | 12 ++++++------ 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/nspl/f.php b/nspl/f.php index 009f8b2..2bd41f1 100644 --- a/nspl/f.php +++ b/nspl/f.php @@ -41,16 +41,13 @@ function apply(callable $function, array $args = array()) * predefined left arguments passed to partial * * @param callable $function - * @param mixed $arg1 - * @param mixed $arg2 - * @param mixed ... + * @param mixed[] ...$args * @return callable */ -function partial(callable $function, $arg1) +function partial(callable $function, ...$args) { - $args = array_slice(func_get_args(), 1); - return function() use ($function, $args) { - return call_user_func_array($function, array_merge($args, func_get_args())); + return function(...$extraArgs) use ($function, $args) { + return call_user_func_array($function, array_merge($args, $extraArgs)); }; } const partial = '\nspl\f\partial'; @@ -60,16 +57,13 @@ function partial(callable $function, $arg1) * predefined right arguments passed to rpartial * * @param callable $function - * @param mixed $arg1 - * @param mixed $arg2 - * @param mixed ... + * @param mixed[] ...$args * @return callable */ -function rpartial(callable $function, $arg1) +function rpartial(callable $function, ...$args) { - $args = array_slice(func_get_args(), 1); - return function() use ($function, $args) { - return call_user_func_array($function, array_merge(func_get_args(), $args)); + return function(...$extraArgs) use ($function, $args) { + return call_user_func_array($function, array_merge($extraArgs, $args)); }; } const rpartial = '\nspl\f\rpartial'; @@ -84,15 +78,14 @@ function rpartial(callable $function, $arg1) */ function ppartial(callable $function, array $args) { - return function() use ($function, $args) { - $_args = func_get_args(); + return function(...$extraArgs) use ($function, $args) { $position = 0; - do { + while ($extraArgs) { if (!isset($args[$position]) && !array_key_exists($position, $args)) { - $args[$position] = array_shift($_args); + $args[$position] = array_shift($extraArgs); } ++$position; - } while($_args); + }; ksort($args); return call_user_func_array($function, $args); }; diff --git a/tests/NsplTest/FTest.php b/tests/NsplTest/FTest.php index 0d929bc..cbe3f60 100644 --- a/tests/NsplTest/FTest.php +++ b/tests/NsplTest/FTest.php @@ -75,8 +75,8 @@ public function testPartial() $oneArgFuncPartial = partial('count', [1, 2, 3]); $this->assertEquals(3, $oneArgFuncPartial()); - $noArgFuncPartial = partial('locale_get_default', null); - $this->assertEqualsWithDelta(locale_get_default(), $noArgFuncPartial(), 0.1); + $noArgFuncPartial = partial('time'); + $this->assertEqualsWithDelta(time(), $noArgFuncPartial(), 0.1); $sqrList = call_user_func(partial, 'array_map', function($v) { return $v * $v; }); $this->assertEquals([1, 4, 9], $sqrList([1, 2, 3])); @@ -92,8 +92,8 @@ public function testRpartial() $oneArgFuncPartial = rpartial('count', [1, 2, 3]); $this->assertEquals(3, $oneArgFuncPartial()); - $noArgFuncPartial = rpartial('locale_get_default', null); - $this->assertEqualsWithDelta(locale_get_default(), $noArgFuncPartial(), 0.1); + $noArgFuncPartial = rpartial('time'); + $this->assertEqualsWithDelta(time(), $noArgFuncPartial(), 0.1); $cube = call_user_func(rpartial, 'pow', 3); $this->assertEquals(27, $cube(3)); @@ -109,8 +109,8 @@ public function testPpartial() $oneArgFuncPartial = ppartial('count', array(0 => [1, 2, 3])); $this->assertEquals(3, $oneArgFuncPartial()); - $noArgFuncPartial = ppartial('locale_get_default', array(0 => null)); - $this->assertEquals(locale_get_default(), $noArgFuncPartial()); + $noArgFuncPartial = ppartial('time', array()); + $this->assertEquals(time(), $noArgFuncPartial()); $f = function($a, $b, $c) { return $a . $b . $c; }; $f1 = ppartial($f, array(0 => 'a')); From 9ffbcbe9cf00153fd0bc4639a8405e10dd2a9474 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Fri, 10 Dec 2021 02:30:18 +0100 Subject: [PATCH 05/10] a\lazy\zip: Fix compatibility with PHP 8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `each` function has been deprecated in PHP 7.2 and removed in 8.0. Let’s convert arrays to iterators to make it work again and also simplify code. --- nspl/a/lazy.php | 44 ++++++++++++++------------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/nspl/a/lazy.php b/nspl/a/lazy.php index f7e448f..b00c2df 100644 --- a/nspl/a/lazy.php +++ b/nspl/a/lazy.php @@ -52,30 +52,22 @@ function zip($sequence1, $sequence2) $sequences = func_get_args(); $count = func_num_args(); - $isArray = array(); for ($j = 0; $j < $count; ++$j) { args\expects(args\traversable, $sequences[$j], $j + 1); - $isArray[$j] = is_array($sequences[$j]); + if (is_array($sequences[$j])) { + $sequences[$j] = new \ArrayIterator($sequences[$j]); + } } do { $zipped = []; for ($j = 0; $j < $count; ++$j) { - if ($isArray[$j]) { - $data = each($sequences[$j]); - if (!$data) { - break 2; - } - $data = $data['value']; + $data = $sequences[$j]->current(); + if (null === $data) { + break 2; } - else { - $data = $sequences[$j]->current(); - if (null === $data) { - break 2; - } - $sequences[$j]->next(); - } + $sequences[$j]->next(); $zipped[] = $data; } @@ -98,30 +90,22 @@ function zipWith(callable $function, $sequence1, $sequence2) array_shift($sequences); $count = count($sequences); - $isArray = array(); for ($j = 0; $j < $count; ++$j) { args\expects(args\traversable, $sequences[$j], $j + 1); - $isArray[$j] = is_array($sequences[$j]); + if (is_array($sequences[$j])) { + $sequences[$j] = new \ArrayIterator($sequences[$j]); + } } do { $zipped = []; for ($j = 0; $j < $count; ++$j) { - if ($isArray[$j]) { - $data = each($sequences[$j]); - if (!$data) { - break 2; - } - $data = $data['value']; + $data = $sequences[$j]->current(); + if (null === $data) { + break 2; } - else { - $data = $sequences[$j]->current(); - if (null === $data) { - break 2; - } - $sequences[$j]->next(); - } + $sequences[$j]->next(); $zipped[] = $data; } From 8e31d6c677d9548bc88bc3e22758ed052ceab9e9 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Fri, 10 Dec 2021 02:38:26 +0100 Subject: [PATCH 06/10] ATest: Fix deprecation warning sortedTest complained: Deprecated: uasort(): Returning bool from comparison function is deprecated, return an integer less than, equal to, or greater than zero --- tests/NsplTest/ATest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/NsplTest/ATest.php b/tests/NsplTest/ATest.php index 11bf621..38a2596 100644 --- a/tests/NsplTest/ATest.php +++ b/tests/NsplTest/ATest.php @@ -350,7 +350,7 @@ public function testSorted() sorted(['orange', 'cat', 'apple'], false, null, function($v1, $v2) use ($isFruit) { if (!$isFruit($v1)) return 1; if (!$isFruit($v2)) return -1; - return $v1 > $v2; + return $v1 <=> $v2; }) ); From ae07dd5d656307bfc05213bb07eee079bc4c4537 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Fri, 10 Dec 2021 04:19:55 +0100 Subject: [PATCH 07/10] Change index.md to symlink To prevent it from becoming stale --- index.md | 1144 +----------------------------------------------------- 1 file changed, 1 insertion(+), 1143 deletions(-) mode change 100644 => 120000 index.md diff --git a/index.md b/index.md deleted file mode 100644 index e5a9b3d..0000000 --- a/index.md +++ /dev/null @@ -1,1143 +0,0 @@ -Non-standard PHP library (NSPL) -=============================== -Non-standard PHP Library (NSPL) is a collection of modules that are meant to solve common day to day routine problems: - - - [nspl\f](#nsplf) - provides functions that act on other functions. Helps to write code in functional programming paradigm - - [nspl\op](#nsplop) - provides functions that perform standard PHP operations and can be passed as callbacks to higher-order functions. Mimics Python's [operator](https://docs.python.org/2/library/operator.html) module - - [nspl\a](#nspla) - provides missing array functions which also can be applied to traversable sequences - - [nspl\a\lazy](#nsplalazy) - provides lazy versions of functions from ```\nspl\a``` - - [nspl\args](#nsplargs) - validates function arguments (will be moved into a separate package in version 2.0) - - [nspl\ds](#nsplds) - provides non-standard data structures and methods to work with them - - [nspl\rnd](#nsplrnd) - helps to pick random items from sequences of data - -NSPL aims to make code compact and less verbose but still clear and readable. Look at the following example: -```php -// get user ids -$userIds = map(propertyGetter('id'), $users); - -// or sort them by age -$sortedByAge = sorted($users, methodCaller('getAge')); - -// or check if they all are online -$online = all($users, methodCaller('isOnline')); - -// or define new function as composition of the existing ones -$flatMap = compose(rpartial(flatten, 1), map); -``` - -In pure PHP it would look like this: -```php -// get user ids -$userIds = array_map(function($user) { return $user->id; }, $users); - -// sort them by age, note that the following code modifies the original users array -usort($users, function($user1, $user2) { - return $user1->getAge() - $user2->getAge(); -}); - -// check if they all are online -$online = true; -foreach ($users as $user) { - if (!$user->isOnline()) { - $online = false; - break; - } -} - -// define new function as composition of the existing ones -$flatMap = function($function, $list) { - // note the inconsistency in array_map and array_reduce parameters - return array_reduce(array_map($function, $list), 'array_merge', []); -}; -``` -You can see more examples in the [library reference](#reference) below or [here](https://github.com/ihor/Nspl/blob/master/examples). - -Installation ------------- -#### Using [composer](https://getcomposer.org) -Define the following requirement in your composer.json file: -``` -"require": { - "ihor/nspl": "~1.3" -} -``` -or execute the following in the command line: -``` -composer require ihor/nspl -``` - -For the latest version which contains way more functionality require version ```2.0.*-dev``` - -#### Manually -Checkout [the code](https://github.com/ihor/Nspl) and include ```autoload.php```: -```php -include 'path/to/nspl/autoload.php'; -``` - -Reference -========= -This is documentation for the dev version ```2.0.*-dev``` which contains the latest changes. For the version ```1.3``` (latest stable version) documentation click [here](https://github.com/ihor/Nspl/tree/1.3). - -Here I assume that described functions are imported with [use function](http://php.net/manual/en/language.namespaces.importing.php): -```php -use function nspl\a\zip; -$pairs = zip([1, 2, 3], ['a', 'b', 'c']); -``` -If your PHP version is less than 5.6 you should import parent namespace and use functions with the namespace prefix: -```php -use nspl\a; -$pairs = a\zip([1, 2, 3], ['a', 'b', 'c']); -``` - -## Table of contents - -* [nspl\f](#nsplf) - * [id](#idvalue) - * [apply](#applyfunction-array-args--) - * [partial](#partialfunction-arg1) - * [rpartial](#rpartialfunction-arg1) - * [ppartial](#ppartialfunction-array-args) - * [flipped](#flippedfunction) - * [compose](#composef-g) - * [pipe](#pipeinput-function1-function2) - * [curried](#curriedfunction-withoptionalargs--false) - * [uncurried](#uncurriedfunction) - * [memoized](#memoizedfunction) - * [throttled](#throttledfunction-wait) - * [Callbacks](#callbacks) -* [nspl\op](#nsplop) - * [Callbacks](#callbacks-1) - * [itemGetter](#itemgetterkey) - * [propertyGetter](#propertygetterproperty) - * [methodCaller](#methodcallermethod-array-args--array) - * [instanceCreator](#instancecreatorclass) -* [nspl\a](#nspla) - * [all](#allsequence-predicate) - * [any](#anysequence-predicate) - * [map](#mapfunction-sequence) - * [flatMap](#flatmapfunction-sequence) - * [zip](#zipsequence1-sequence2) - * [zipWith](#zipwithfunction-sequence1-sequence2) - * [reduce](#reducefunction-sequence-initial--0) - * [filter](#filterpredicate-sequence) - * [filterNot](#filternotpredicate-sequence) - * [take](#takesequence-n-step--1) - * [takeKeys](#takekeyssequence-array-keys) - * [takeWhile](#takewhilepredicate-sequence) - * [first](#firstsequence) - * [second](#secondsequence) - * [drop](#dropsequence-n) - * [dropKeys](#dropkeyssequence-array-keys) - * [dropWhile](#dropwhilepredicate-sequence) - * [last](#lastsequence) - * [partition](#partitionpredicate-sequence) - * [span](#spanpredicate-sequence) - * [indexed](#indexedsequence-by-keeplast--true-transform--null) - * [sorted](#sortedsequence-reversed--false-key--null-cmp--null) - * [keySorted](#keysortedsequence-reversed--false) - * [flatten](#flattensequence-depth--null) - * [pairs](#pairssequence-valuekey--false) - * [merge](#mergesequence1-sequence2) - * [reorder](#reorderarray-list-from-to) - * [value](#valuearray-key-default--null) - * [values](#valuessequence) - * [keys](#keyssequence) - * [in](#initem-sequence) - * [diff](#diffsequence1-sequence2) - * [intersect](#intersectsequence1-sequence2) - * [cartesianProduct](#cartesianproductsequences) - * [isList](#islistvar) - * [Chaining](#chaining) - * [Callbacks](#callbacks-2) -* [nspl\a\lazy](#nsplalazy) -* [nspl\args](#nsplargs) - * [expects](#expectsconstraints-arg-atposition--null-otherwisethrow--invalidargumentexception) - * [expectsAll](#expectsallconstraints-array-args-array-atpositions---otherwisethrow--invalidargumentexception) - * [expectsOptional](#expectsoptionalconstraints-arg-atposition--null-otherwisethrow--invalidargumentexception) - * [Predefined constraints](#predefined-constraints) - * [Custom constraints](#custom-constraints) -* [nspl\ds](#nsplds) - * [DefaultArray](#defaultarray) - * [Set](#set) -* [nspl\rnd](#nsplrnd) - * [randomString](#length) - * [choice](#choicesequence) - * [weightedChoice](#weightedchoiceweightpairs) - * [sample](#samplepopulation-length-preservekeys--false) -* [nspl](#nspl) - * [getType](#gettypevar) - -## nspl\f - -Provides functions that act on other functions. Helps to write code in the functional programming paradigm. - -##### id($value) - -Identity function. Returns passed value. - -```php -assert(1 === id(1)); -``` - -##### apply($function, array $args = []) - -Applies given function to arguments and returns the result -```php -assert([1, 3, 5, 7, 9] === apply('range', [1, 10, 2])); -``` - -##### partial($function, $arg1) - -Returns new partial function which will behave like ```$function``` with predefined *left* arguments passed to partial -```php -$sum = function($a, $b) { return $a + $b; }; -$inc = partial($sum, 1); -``` - -##### rpartial($function, $arg1) - -Returns new partial function which will behave like ```$function``` with predefined *right* arguments passed to rpartial -```php -$cube = rpartial('pow', 3); -``` - -##### ppartial($function, array $args) - -Returns new partial function which will behave like ```$function``` with predefined *positional* arguments passed to ppartial -```php -$oddNumbers = ppartial('range', array(0 => 1, 2 => 2)); -``` - -##### flipped($function) - -Returns function which accepts arguments in the reversed order - -##### compose($f, $g) - -Returns new function which applies each given function to the result of another from right to left -```compose(f, g, h)``` is the same as ```f(g(h(x)))``` -```php -use const \nspl\a\flatten; -use const \nspl\a\map; -use function \nspl\f\compose; -use function \nspl\f\partial; -use function \nspl\f\rpartial; - -$flatMap = compose(rpartial(flatten, 1), map); -assert(['hello', 'world', 'foo', 'bar'] === $flatMap(partial('explode', ' '), ['hello world', 'foo bar'])); -``` - -##### pipe($input, $function1, $function2) - -Passes ```$input``` to composition of functions (functions have to be in the reversed order) -```php -use const \nspl\op\sum; -use const \nspl\a\filter; -use const \nspl\a\map; -use const \nspl\a\reduce; -use function \nspl\f\partial; - -$isEven = function($x) { return $x % 2 === 0; }; -$square = function($x) { return $x * $x; }; - -// sum of squares of all even numbers less than 20 -$sum = pipe( - range(1, 20), - partial(filter, $isEven), - partial(map, $square), - partial(reduce, sum) -); -``` - -> **Tip** -> -> You can use [chaining](#chaining) to get rid of partials in sequence transformations: -> -> ```php -> use function \nspl\a\with; -> -> $sum = with(range(1, 20)) -> ->filter($isEven) -> ->map($square) -> ->reduce(sum); -> ``` - -##### curried($function, $withOptionalArgs = false) - -Returns a [curried](https://en.wikipedia.org/wiki/Currying) version of the function. If you are going to curry a function which reads args with ```func_get_args()``` then pass the number of args as the 2nd argument. - -If the second argument is true, then curry function with optional args otherwise curry it only with required args. Alternatively, you can pass the exact number of args you want to curry. - -##### uncurried($function) - -Returns normal (uncurried) version of a [curried function](https://en.wikipedia.org/wiki/Currying) - -##### memoized($function) - -Returns memoized ```$function``` which returns the cached result when the same inputs occur again -```php -$f = function($arg) { - echo sprintf("Performing heavy calculations with '%s'\n", $arg); - return $arg; -}; - -$memoized = memoized($f); -echo $memoized('Hello world!') . "\n"; -echo $memoized('Hello world!') . "\n"; -``` -which outputs -``` -Performing heavy calculations with 'Hello world!' -Hello world! -Hello world! -``` - -##### throttled($function, $wait) - -Returns throttled version of the passed function, that, when invoked repeatedly, will only actually call the original function at most once per every wait milliseconds. -```php -$f = function() { - echo "Invoked\n"; -}; - -$throttled = throttled($f, 10); - -$startedAt = microtime(true); -do { - $throttled(); -} while((microtime(true) - $startedAt) * 1000 < 30); // 30ms -``` -which outputs -``` -Invoked -Invoked -Invoked -``` - -##### Callbacks - -```nspl\f``` provides all its functions as callbacks in its constants which have the same names as the functions. -```php -use const \nspl\a\map; -use const \nspl\a\filter; - -$incListItems = partial(map, function($v) { return $v + 1; }); -$filterNumbers = partial(filter, 'is_numeric'); -``` - -Check more ```\nspl\f``` examples [here](https://github.com/ihor/Nspl/blob/master/examples/f.php). - - -## nspl\op - -Class ```nspl\op``` provides functions that perform standard PHP operations and can be passed as callbacks to higher-order functions. Mimics Python's [operator](https://docs.python.org/2/library/operator.html) module. For example: - -```php -use const nspl\op\sum; -use function nspl\a\reduce; - -assert(6 === reduce(sum, [1, 2, 3])); -``` - -##### Callbacks - -The module provides the following operations both as functions and callbacks. See an example below. - -Function | Operation ----------|----------------------------------------------- -sum | + -sub | - -mul | * -div | / -mod | % -inc | ++ -dec | -- -neg | - -band | & -bxor | ^ -bor | | -bnot | ~ -lshift | << -rshift | >> -lt | < -le | <= -eq | == -idnt | === -ne | != -nidnt | !== -ge | > -gt | >= -and_ | && -or_ | || -xor_ | xor -not | ! -concat | . -instanceof_ | instanceof -int | (int) -bool | (bool) -float | (float) -str | (string) -array_ | (array) -object | (object) - -##### itemGetter($key) -Returns a function that returns key value for a given array - -```php -use function nspl\op\itemGetter; -use function nspl\a\map; - -assert([2, 5, 8] === map(itemGetter(1), [[1, 2, 3], [4, 5, 6], [7, 8, 9]])); -``` - -##### propertyGetter($property) -Returns a function that returns property value for a given object - -```php -$userIds = map(propertyGetter('id'), $users); -``` - -##### methodCaller($method, array $args = array()) -Returns a function that returns method result for a given object on predefined arguments - -```php -$userIds = map(methodCaller('getId'), $users); -``` - -##### instanceCreator($class) -Returns a function that returns a new instance of a predefined class, passing its parameters to the constructor - -```php -$users = map(instanceCreator(User::class), $usersData); -``` - -Check more ```\nspl\op``` examples [here](https://github.com/ihor/Nspl/blob/master/examples/op.php). - -## nspl\a - -Provides missing array functions which also can be applied to traversable sequences - -##### all($sequence, $predicate) - -Returns true if all of the ````$sequence``` items satisfy the predicate (or if the ```$sequence``` is empty). If the predicate was not passed returns true if all of the ```$sequence``` items are true. - -```php -assert(true === all([true, true, true])); -``` - -##### any($sequence, $predicate) - -Returns true if any of the ```$sequence``` items satisfy the predicate. If the predicate was not passed returns true if any of the ```$sequence``` items are true. - -```php -assert(true === any([true, false, false])); -``` - -##### map($function, $sequence) - -Applies function of one argument to each sequence item -```php -assert(['A', 'B', 'C'] === map('strtoupper', ['a', 'b', 'c'])); -``` - -##### flatMap($function, $sequence) - -Applies function of one argument to each sequence item and flattens the result -```php -$duplicate = function($v) { return [$v, $v]; } -assert(['hello', 'hello', 'world', 'world'] === flatMap($duplicate, ['hello', 'world'])); -``` - -##### zip($sequence1, $sequence2) - -Zips two or more sequences -```php -assert([[1, 'a'], [2, 'b'], [3, 'c']] === zip([1, 2, 3], ['a', 'b', 'c'])); -``` - -##### zipWith($function, $sequence1, $sequence2) - -Generalises zip by zipping with the function given as the first argument, instead of an array-creating function -```php -use const \nspl\op\sum; - -assert([101, 1002, 10003] === zipWith(sum, [1, 2, 3], [100, 1000, 10000])); -``` - -##### reduce($function, $sequence, $initial = 0) - -Applies function of two arguments cumulatively to the sequence items, from left to right to reduce the sequence to a single value -```php -assert(6 === reduce(function($a, $b) { return $a + $b; }, [1, 2, 3])); - -// Which is the same as -use const \nspl\op\sum; -assert(6 === reduce(sum, [1, 2, 3])); - -``` - -##### filter($predicate, $sequence) - -Returns sequence items that satisfy the predicate -```php -assert([1, 2, 3] === filter('is_numeric', ['a', 1, 'b', 2, 'c', 3])); -``` - -##### filterNot($predicate, $sequence) - -Returns sequence items that don't satisfy the predicate -```php -assert(['a', 'b', 'c'] === filterNot('is_numeric', ['a', 1, 'b', 2, 'c', 3])); -``` - -##### take($sequence, $N, $step = 1) - -Returns the first N sequence items with the given step -```php -assert([1, 3, 5] === take([1, 2, 3, 4, 5, 6, 7, 8, 9], 3, 2)); -``` - -##### takeKeys($sequence, array $keys) - -Returns sequence containing only the given keys -```php -assert(array('hello' => 1, 'world' => 2) === takeKeys(array('hello' => 1, 'world' => 2, 'foo' => 3), ['hello', 'world'])); -``` - -##### takeWhile($predicate, $sequence) - -Returns the longest sequence prefix of all items which satisfy the predicate -```php -assert([1, 2, 3] === takeWhile('is_numeric', [1, 2, 3, 'a', 'b', 'c', 4, 5, 6])); -``` - -##### first($sequence) - -Returns the first sequence item -```php -assert(1 === first([1, 2, 3, 4, 5, 6, 7, 8, 9])); -``` - -##### second($sequence) - -Returns the second sequence item -```php -assert(2 === second([1, 2, 3, 4, 5, 6, 7, 8, 9])); -``` - -##### drop($sequence, $N) - -Drops the first N sequence items -```php -assert([7, 8, 9] === drop([1, 2, 3, 4, 5, 6, 7, 8, 9], 6)); -``` - -##### dropKeys($sequence, array $keys) - -Returns array containing all keys except the given ones -```php -assert(array('hello' => 1, 'world' => 2) === dropKeys(array('hello' => 1, 'world' => 2, 'foo' => 3), ['foo'])); -``` - -##### dropWhile($predicate, $sequence) - -Drops the longest sequence prefix of all items which satisfy the predicate -```php -assert(['a', 'b', 'c', 4, 5, 6] === dropWhile('is_numeric', [1, 2, 3, 'a', 'b', 'c', 4, 5, 6])); -``` - -##### last($sequence) - -Returns the last sequence item -```php -assert(9 === last([1, 2, 3, 4, 5, 6, 7, 8, 9])); -``` - -##### partition($predicate, $sequence) - -Returns two lists, one containing values for which the predicate returned true, and the other containing the items that returned false -```php -assert([[1, 2, 3], ['a', 'b', 'c']] === partition('is_numeric', ['a', 1, 'b', 2, 'c', 3])); -``` - -##### span($predicate, $sequence) - -Returns two lists, one containing values for which your predicate returned true until the predicate returned false, and the other containing all the items that left -```php -assert([[1], ['a', 2, 'b', 3, 'c']] === span('is_numeric', [1, 'a', 2, 'b', 3, 'c'])); -``` - -##### indexed($sequence, $by, $keepLast = true, $transform = null) - -Returns array which contains indexed sequence items - -```$by``` is an array key or a function -If ```$keepLast``` is true only the last item with the key will be returned otherwise a list of items which share the same key value will be returned -```$transform``` is a function that transforms list item after indexing - -```php -$indexedById = indexed([ - array('id' => 1, 'name' => 'John'), - array('id' => 2, 'name' => 'Kate'), - array('id' => 3, 'name' => 'Robert'), -], 'id'); -``` - -##### sorted($sequence, $reversed = false, $key = null, $cmp = null) - -Returns array which contains sorted items from the passed sequence - -If ```$reversed``` is true then return reversed sorted sequence. If ```$reversed``` is not boolean and ```$key``` was not passed then acts as a ```$key``` parameter -```$key``` is a function of one argument that is used to extract a comparison key from each item -```$cmp``` is a function of two arguments which returns a negative number, zero or positive number depending on whether the first argument is smaller than, equal to, or larger than the second argument -```php -assert([1, 2, 3] === sorted([2, 3, 1])); -assert(['c', 'b', 'a'] === sorted(['c', 'a', 'b'], true)); - -$usersSortedByName = sorted($users, function($u) { return $u->getName(); }); - -// Which is the same as -use function \nspl\op\methodCaller; -$usersSortedByName = sorted($users, methodCaller('getName')); -``` - -Check more ```\nspl\a\sorted``` examples [here](https://github.com/ihor/Nspl/blob/master/examples/a_sorted.php). - -##### keySorted($sequence, $reversed = false) - -Returns array which contains sequence items sorted by keys -```php -assert(array('a' => 1, 'b' => 2, 'c' => 3) === keySorted(array('b' => 2, 'c' => 3, 'a' => 1)); -``` - -##### flatten($sequence, $depth = null) - -Flattens multidimensional sequence -```php -assert([1, 2, 3, 4, 5, 6, 7, 8, 9] === flatten([[1, [2, [3]]], [[[4, 5, 6]]], 7, 8, [9]])); -assert([1, 2, [3], [4, 5, 6], 7, 8, 9] === flatten([[1, [2, [3]]], [[[4, 5, 6]]], 7, 8, [9]], 2)); -``` - -##### pairs($sequence, $valueKey = false) - -Returns a list of (key, value) pairs. If ```$valueKey``` is true then returns (value, key) pairs. -```php -assert([['a', 'hello'], ['b', 'world'], ['c', 42]] === pairs(array('a' => 'hello', 'b' => 'world', 'c' => 42))); -``` - -##### merge($sequence1, $sequence2) - -Returns array containing ```$sequence1``` items and ```$sequence2``` items -```php -assert([1, 2, 3, 4, 5, 6] === merge([1, 2, 3], [4, 5, 6])); -``` - -##### reorder(array $list, $from, $to) - -Moves list item to another position -```php -assert([2, 0, 1] === reorder([0, 1, 2], 2, 0)); // move item from the 2nd position to the begining of the list -``` - -##### value($array, $key, $default = null) - -Returns array value by key if it exists otherwise returns the default value -```php -$data = array('a' => 1, 'b' => 2, 'c' => 3); -assert(2 === value($data, 'b', -1)); -assert(-1 === value($data, 'd', -1)); -``` - -##### values($sequence) - -Returns list of the sequence values -```php -assert([1, 2, 3] === values(array('a' => 1, 'b' => 2, 'c' => 3))); -``` - -##### keys($sequence) - -Returns list of the sequence keys -```php -assert(['a', 'b', 'c'] === keys(array('a' => 1, 'b' => 2, 'c' => 3))); -``` - -##### in($item, $sequence) - -Checks if the item is present in array or traversable object -```php -assert(true === in(1, [1, 2, 3]); -``` - -##### diff($sequence1, $sequence2) - -Computes the difference of arrays or traversable objects -```php -assert([1] === diff([1, 2, 3], new ArrayObject([2, 3, 4])); -``` - -##### intersect($sequence1, $sequence2) - -Computes the intersection of arrays or traversable objects -```php -assert([2, 3] === intersect([1, 2, 3], new ArrayObject([2, 3, 4])); -``` - -##### cartesianProduct($sequences) - -Computes the cartesian product of two or more arrays or traversable objects -```php -assert([ - [1, 'a'], - [1, 'b'], - [2, 'a'], - [2, 'b'], -], cartesianProduct([1, 2], ['a', 'b'])); -``` - -```php -assert([ - array('hello' => 1, 'world' => 'a'), - array('hello' => 1, 'world' => 'b'), - array('hello' => 2, 'world' => 'a'), - array('hello' => 2, 'world' => 'b'), -], cartesianProduct(array('hello' => [1, 2], 'world' => ['a', 'b']))); -``` - -##### isList($var) - -Returns true if the variable is a list - -##### Chaining - -It is possible to chain array function calls using the `with` function: - -```php -use function nspl\op\sum; - -$square = function($n) { return $n * $n; }; - -$isEven = function($n) { return $n % 2 === 0; }; - -$sum = with(range(1, 5)) - ->filter($isEven) - ->map($square) - ->reduce(sum); - -assert(20 === $sum); -``` - -##### Callbacks - -```nspl\a``` provides all its functions as callbacks in its constants which have the same names as the functions. -```php -use const \nspl\a\first; -assert([1, 2, 3] === map(first, [[1, 'a'], [2, 'b'], [3, 'c']])); -``` - -Check more ```\nspl\a``` examples [here](https://github.com/ihor/Nspl/blob/master/examples/a.php). - -## nspl\a\lazy -Provides lazy versions of functions from [nspl\a](#nspla) - -This module might be useful when you don't need to process all the values from an array or any other traversable sequence. To understand how these lazy functions work let's have a look at the following example. - -Let's define a function which wraps a generator function and logs all the values it yields. It will help up us to see the order of function calls: -```php -// Calls generator function and logs the yielded values -function logged(callable $generatorFunction) -{ - static $count = 1; - return function(...$args) use ($generatorFunction, &$count) { - foreach ($generatorFunction(...$args) as $value) { - echo $count++ . '. ' . (string) $generatorFunction . ' -> ' . $value . "\n"; - yield $value; - }; - }; -} -``` - -To have some data to operate on, let's define a function which returns all natural numbers. Since it returns all the natural numbers it never terminates: -```php -function naturalNumbers() -{ - $current = 1; - while (true) yield $current++; -} -const naturalNumbers = 'naturalNumbers'; -``` - -Also, let's define the operations we want to perform on those numbers: -```php -// Returns square of a number -function square($n) -{ - return $n * $n; -} -const square = 'square'; - -// Checks if a number is even -function isEven($n) -{ - return $n % 2 === 0; -} -const isEven = 'isEven'; -``` - -Now let's assume we want to take the first three even natural numbers and calculate their squares: -```php -use const nspl\a\lazy\{take, map, filter}; - -$map = logged(map); -$take = logged(take); -$filter = logged(filter); -$numbers = logged(naturalNumbers)(); - -$evenNumbers = $filter(isEven, $numbers); // filter only even numbers -$firstThreeEvenNumbers = $take($evenNumbers, 3); // take only first 3 even numbers -$result = $map(square, $firstThreeEvenNumbers); // and calculate their squares - -foreach ($result as $value) { - echo "\nNext value is $value \n\n"; -} -``` - -When we run this example we'll see the following output: -``` -1. naturalNumbers -> 1 -2. naturalNumbers -> 2 -3. \nspl\a\lazy\filter -> 2 -4. \nspl\a\lazy\take -> 2 -5. \nspl\a\lazy\map -> 4 - -Next value is 4 - -6. naturalNumbers -> 3 -7. naturalNumbers -> 4 -8. \nspl\a\lazy\filter -> 4 -9. \nspl\a\lazy\take -> 4 -10. \nspl\a\lazy\map -> 16 - -Next value is 16 - -11. naturalNumbers -> 5 -12. naturalNumbers -> 6 -13. \nspl\a\lazy\filter -> 6 -14. \nspl\a\lazy\take -> 6 -15. \nspl\a\lazy\map -> 36 - -Next value is 36 -``` - -If we used regular non-lazy versions of these functions, we would generate all the natural numbers, then filtered only even numbers, then took only the first three of them and then calculated their squares. Instead of that, you see that functions were called one by one passing the result to the next function until we completed the full cycle: - 1. We took the first natural number – 1. It wasn't even, so we skipped it - 2. We took the next one – 2, it was even - 3. So it passed the ```filter``` function - 4. It was the first number we took, so it passed through the ```take``` function as well - 5. Then we calculated its square and printed the result - -The same repeated on steps 6-10 and 11-15. On step 14 the ```take``` function took the last third number. So after step 15, when ```map``` requested the next value ```take``` didn't yield anything, and the whole iteration was finished. - -Check this example [here](https://github.com/ihor/Nspl/blob/master/examples/a_lazy.php). - -It possible to rewrite the code above using [chaining](#chaining): -```php -$result = with(naturalNumbers()) - ->filter(isEven) - ->take(3) - ->map(square); -``` - -> **Tip** -> -> Note that while functions from ```\nspl\a\lazy``` allow you to avoid redundant computations, in case when you need to process all sequence values, functions from ```\nspl\a``` will do the job faster. - -## nspl\args - -Helps to validate function arguments - -##### expects($constraints, $arg, $atPosition = null, $otherwiseThrow = '\InvalidArgumentException') - -Checks that argument satisfies the required constraints otherwise throws the corresponding exception. - -```$constraints``` are callable(s) which return(s) true if the argument satisfies the requirements or it also might contain the required class name(s) -If ```$atPosition``` is null, then the position is calculated automatically comparing given argument to the actual arguments passed to the function -```$otherwiseThrow``` defines exception which will be thrown if the given argument is invalid, it can be the exception class or exception object - -```php -use const \nspl\args\int; -use const \nspl\args\string; -use const \nspl\args\arrayAccess; -use function \nspl\args\expects; - -function nth($sequence, $n) -{ - expects([arrayAccess, string], $sequence); - expects(int, $n); - - return $sequence[$n]; -} - -nth('hello world', 'blah'); -``` - -Outputs: -``` -InvalidArgumentException: Argument 2 passed to nth() must be integer, string 'blah' given in /path/to/example.php on line 17 - -Call Stack: - 0.0002 230304 1. {main}() /path/to/example.php:0 - 0.0023 556800 2. sqr() /path/to/example.php:17 -``` - -##### expectsAll($constraints, array $args, array $atPositions = [], $otherwiseThrow = '\InvalidArgumentException') - -Checks that all specified arguments satisfy the required constraints otherwise throws the corresponding exception. - -```php -use const \nspl\args\numeric; -use function \nspl\args\expects; - -function sum($x, $y) -{ - expectsAll(numeric, [$x, $y]); - - return $x + $y; -} -``` - -##### expectsOptional($constraints, $arg, $atPosition = null, $otherwiseThrow = '\InvalidArgumentException') - -Checks that argument is null or satisfies the required constraints otherwise throws the corresponding exception. - -```php -function splitBy($string, $separator = ' ', $limit = null) -{ - expectsAll(string, [$string, $separator]); - expectsOptional(int, $limit); - - return explode($separator, $string, $limit); -} -``` - -##### Predefined constraints - -The module provides predefined constraints. Which can be one of the two types: -- OR-constraints which are evaluated with ```or``` operator (e.g. ```expects([int, string], $arg)``` evaluates as ```$arg``` has to be an ```int``` or a ```string```) -- AND-constraints which are evaluated with ```and``` operator (e.g. ```expects([string, longerThan(3), shorterThan(10)], $arg)``` evaluates as ```$arg``` has to be a string longer than 3 characters and shorter than 10 characters). If you want to evaluate several AND-constraints as they were OR-constraints you can use ```any``` constraint. If you want to evaluate several OR-constraints as they were AND-constraints you can use ```all``` constraint - -Callback | Explanation | Type -------------------------------------|------------------------------------------------------------------------|---------- -bool | Checks that argument is a bool | OR -int | Checks that argument is an int | OR -float | Checks that argument is a float | OR -numeric | Checks that argument is numeric | OR -string | Checks that argument is a string | OR -array_ | Checks that argument is an array | OR -object | Checks that argument is an object | OR -callable_ | Checks that argument is callable | OR -arrayKey | Checks that argument can be an array key | OR -traversable | Checks that argument can be traversed with foreach | OR -arrayAccess | Checks that argument supports array index access | OR -nonEmpty | Checks that argument is not empty | AND -positive | Checks that argument is positive (> 0) | AND -nonNegative | Checks that argument is not negative (>= 0) | AND -nonZero | Checks that argument is not zero (!== 0) | AND -any(constraint1, ..., constraintN) | Checks constraints as if they were OR-constraints | AND -all(constraint1, ..., constraintN) | Checks constraints as if they were AND-constraints | AND -not(constraint1, ..., constraintN) | Checks that argument does't satisfy all listed constraints | AND -values(value1, ..., valueN) | Checks that argument is one of the specified values | AND -longerThan($threshold) | Checks that string argument is longer than given threshold | AND -shorterThan($threshold) | Checks that string argument is shorter than given threshold | AND -biggerThan($threshold) | Checks that number is bigger than given threshold | AND -smallerThan($threshold) | Checks that number is smaller than given threshold | AND -hasKey($key) | Checks that argument supports array index access and has given key | AND -hasKeys($key1, ..., $keyN) | Checks that argument supports array index access and has given keys | AND -hasMethod($method) | Checks that argument is an object and has given method | AND -hasMethods($method1, ..., $methodN) | Checks that argument is an object and has given methods | AND - - -```php -function setUsername($username) -{ - expects([string, longerThan(3), shorterThan(10)], $username); - // ... -} - -function setState($state) -{ - expects(values('running', 'idle', 'stopped'), $state); - // ... -} -``` - -Duck-typing example: -```php -class Service -{ - // ... - public function setCache($cache) - { - expects(withMethods('set', 'get'), $cache); - $this->cache = $cache; - } - // .... -} -``` - -##### Custom constraints - -It is possible to use custom constraints. Just define a new function which returns true when argument satisfies the constraint: -```php -function even($value) -{ - return is_int($value) && $value %2 === 0; -} - -function half($number) -{ - expects('even', $number); - return $number / 2; -} -``` -or we can make it more convenient to use introducing a constant: -```php -const even = 'even'; - -function half($number) -{ - expects(even, $number); - return $number / 2; -} - -half('pie'); -``` -Outputs: -``` -InvalidArgumentException: Argument 1 passed to half() must be even, string 'pie' given in /path/to/example.php on line 25 - -Call Stack: - 0.0009 253640 1. {main}() /path/to/example.php:0 - 0.0123 673984 2. half() /path/to/example.php:25 -``` - -If you need to create a constraint which takes arguments, you must create a callable object which implements ```\nspl\args\Constraint``` interface. It contains two methods: -- ```__invoke($value)``` - returns true if the value satisfies the constraint -- ```__toString()``` - returns text which will be used in the exception when the value doesn't satisfy the constraint. The text must contain a message which goes after "must" in the exception message. - - -## nspl\ds - -Provides non-standard data structures and methods to work with them - -##### DefaultArray - -Array with a default value for missing keys. If you pass a function as default value it will be called without arguments to provide a default value for the given key, this value will be inserted in the array for the key, and returned. -Using DefaultArray turns this code: -```php -$a = array(); -foreach([1, 2, 1, 1, 3, 3, 3] as $v) { - if (!isset($a[$v])) { - $a[$v] = 0; - } - ++$a[$v]; -} -``` -into this: -```php -$a = defaultarray(0); -foreach([1, 2, 1, 1, 3, 3, 3] as $v) { - ++$a[$v]; -} -``` - -##### defaultarray($default, $data = array()) - -Returns new DefaultArray - -##### Set - -An array-like collection that contains no duplicate elements. It supports basic set operations which take other sets, arrays and traversable objects as arguments - -```php -$set = set(1, 2); - -$set->add('hello'); -$set[] = 'world'; - -$set->delete('hello'); - -$array = [1, 2, 3]; -$intersection = $set->intersection($array); - -$anotherSet = Set::fromArray([1, 2, 3]); -$difference = $set->difference($anotherSet); - -$iterator = new \ArrayIterator([1, 2, 3]); -$union = $set->union($iterator); - -$isSubset = $set->isSubset([1, 2, 'hello', 'world']); - -$isSuperset = $set->isSuperset([1, 2]); -``` - -##### set - -Returns new Set - -Check more ```\nspl\ds``` examples [here](https://github.com/ihor/Nspl/blob/master/examples/ds.php). - -## nspl\rnd - -##### randomString($length) - -Returns a random alpha-numeric string of the given length - -##### choice($sequence) - -Returns a random item from a non-empty sequence - -##### weightedChoice($weightPairs) - -Returns a random item from a non-empty sequence of items with associated weights presented as pairs (item, weight) - -```php -use function \nspl\rnd\weightedChoice; -use function \nspl\a\pairs; - -$nextPet = weightedChoice([['cat', 20], ['hamster', 30], ['dog', 50]]); -$nextFavouriteColor = weightedChoice(pairs(array( - 'red' => 0.2, - 'green' => 0.3, - 'blue' => 0.5, -))); -``` - -##### sample($population, $length, $preserveKeys = false) - -Returns a k length list of unique items chosen from the population sequence - -Check more ```\nspl\rnd``` examples [here](https://github.com/ihor/Nspl/blob/master/examples/rnd.php). - -## nspl - -##### getType($var) - -Returns the variable type or its class name if it is an object - - -Roadmap -======= - -- Rewrite library using the latest features from PHP 7.2 -- Move `nspl\args` into a separate module - -Contributing -============ - -This project uses [semantic versioning](http://semver.org/) to tag releases. Please submit your pull requests to the latest release branch where the issue was introduced. - -Feedback -======== - -There are no mailing lists or discussion groups yet. Please use GitHub issues and pull request or follow me on Twitter [@IhorBurlachenko](https://twitter.com/IhorBurlachenko) diff --git a/index.md b/index.md new file mode 120000 index 0000000..42061c0 --- /dev/null +++ b/index.md @@ -0,0 +1 @@ +README.md \ No newline at end of file From 79c593eca7a5c1ba5c676b4b8a07763b97daeadc Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Fri, 10 Dec 2021 04:04:35 +0100 Subject: [PATCH 08/10] Deprecate args\traversable in favour of iterable It has been available since PHP 7.1: https://www.php.net/manual/en/language.types.iterable.php --- README.md | 17 +-- nspl/a.php | 194 ++++++++++---------------- nspl/a/ChainableSequence.php | 24 ++-- nspl/a/lazy.php | 84 +++++------ nspl/a/lazy/LazyChainableSequence.php | 8 +- nspl/args.php | 17 ++- nspl/ds/Set.php | 41 ++---- nspl/f.php | 28 ++-- nspl/rnd.php | 11 +- tests/NsplTest/ArgsTest.php | 41 ++++-- 10 files changed, 200 insertions(+), 265 deletions(-) diff --git a/README.md b/README.md index 5334bf6..89845ee 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Non-standard PHP Library (NSPL) is a collection of modules that are meant to sol - [nspl\f](#nsplf) - provides functions that act on other functions. Helps to write code in functional programming paradigm - [nspl\op](#nsplop) - provides functions that perform standard PHP operations and can be passed as callbacks to higher-order functions. Mimics Python's [operator](https://docs.python.org/2/library/operator.html) module - - [nspl\a](#nspla) - provides missing array functions which also can be applied to traversable sequences + - [nspl\a](#nspla) - provides missing array functions which also can be applied to iterable sequences - [nspl\a\lazy](#nsplalazy) - provides lazy versions of functions from ```\nspl\a``` - [nspl\args](#nsplargs) - validates function arguments (will be moved into a separate package in version 2.0) - [nspl\ds](#nsplds) - provides non-standard data structures and methods to work with them @@ -415,7 +415,7 @@ Check more ```\nspl\op``` examples [here](https://github.com/ihor/Nspl/blob/mast ## nspl\a -Provides missing array functions which also can be applied to traversable sequences +Provides missing array functions which also can be applied to iterable sequences ##### all($sequence, $predicate) @@ -664,28 +664,28 @@ assert(['a', 'b', 'c'] === keys(array('a' => 1, 'b' => 2, 'c' => 3))); ##### in($item, $sequence) -Checks if the item is present in array or traversable object +Checks if the item is present in iterable (array or traversable object) ```php assert(true === in(1, [1, 2, 3]); ``` ##### diff($sequence1, $sequence2) -Computes the difference of arrays or traversable objects +Computes the difference of iterables (arrays or traversable objects) ```php assert([1] === diff([1, 2, 3], new ArrayObject([2, 3, 4])); ``` ##### intersect($sequence1, $sequence2) -Computes the intersection of arrays or traversable objects +Computes the intersection of iterables (arrays or traversable objects) ```php assert([2, 3] === intersect([1, 2, 3], new ArrayObject([2, 3, 4])); ``` ##### cartesianProduct($sequences) -Computes the cartesian product of two or more arrays or traversable objects +Computes the cartesian product of two or more iterables (arrays or traversable objects) ```php assert([ [1, 'a'], @@ -740,7 +740,7 @@ Check more ```\nspl\a``` examples [here](https://github.com/ihor/Nspl/blob/maste ## nspl\a\lazy Provides lazy versions of functions from [nspl\a](#nspla) -This module might be useful when you don't need to process all the values from an array or any other traversable sequence. To understand how these lazy functions work let's have a look at the following example. +This module might be useful when you don't need to process all the values from an iterable (array or any other traversable sequence). To understand how these lazy functions work let's have a look at the following example. Let's define a function which wraps a generator function and logs all the values it yields. It will help up us to see the order of function calls: ```php @@ -937,7 +937,8 @@ array_ | Checks that argument is an array object | Checks that argument is an object | OR callable_ | Checks that argument is callable | OR arrayKey | Checks that argument can be an array key | OR -traversable | Checks that argument can be traversed with foreach | OR +iterable_ | Checks that argument can be iterated with foreach | OR +~~traversable~~ | Deprecated in favour of `iterable_` | OR arrayAccess | Checks that argument supports array index access | OR nonEmpty | Checks that argument is not empty | AND positive | Checks that argument is positive (> 0) | AND diff --git a/nspl/a.php b/nspl/a.php index 817d833..43f7b10 100644 --- a/nspl/a.php +++ b/nspl/a.php @@ -10,14 +10,12 @@ * Returns true if all of the $sequence items satisfy the predicate (or if the $sequence is empty). * If the predicate was not passed returns true if all of the $sequence items are true. * - * @param array|\Traversable $sequence + * @param iterable $sequence * @param callable $predicate * @return bool */ -function all($sequence, callable $predicate = null) +function all(iterable $sequence, callable $predicate = null) { - args\expects(args\traversable, $sequence); - foreach ($sequence as $value) { if ($predicate && !$predicate($value) || !$predicate && !$value) { return false; @@ -32,14 +30,12 @@ function all($sequence, callable $predicate = null) * Returns true if any of the $sequence items satisfy the predicate. * If the predicate was not passed returns true if any of the $sequence items are true. * - * @param array|\Traversable $sequence + * @param iterable $sequence * @param callable $predicate * @return bool */ -function any($sequence, callable $predicate = null) +function any(iterable $sequence, callable $predicate = null) { - args\expects(args\traversable, $sequence); - foreach ($sequence as $value) { if ($predicate && $predicate($value) || !$predicate && $value) { return true; @@ -54,13 +50,11 @@ function any($sequence, callable $predicate = null) * Applies function of one argument to each sequence item * * @param callable $function - * @param array|\Traversable $sequence + * @param iterable $sequence * @return array */ -function map(callable $function, $sequence) +function map(callable $function, iterable $sequence) { - args\expects(args\traversable, $sequence); - $result = []; foreach ($sequence as $key => $item) { $result[$key] = $function($item); @@ -74,13 +68,11 @@ function map(callable $function, $sequence) * Applies function of one argument to each sequence item and flattens the result * * @param callable $function - * @param array|\Traversable $sequence + * @param iterable $sequence * @return array */ -function flatMap(callable $function, $sequence) +function flatMap(callable $function, iterable $sequence) { - args\expects(args\traversable, $sequence); - $result = []; foreach ($sequence as $item) { foreach ($function($item) as $resultValue) { @@ -95,17 +87,17 @@ function flatMap(callable $function, $sequence) /** * Zips two or more sequences * - * @param array|\Traversable $sequence1 - * @param array|\Traversable $sequence2 + * @param iterable $sequence1 + * @param iterable $sequence2 * @return array */ -function zip($sequence1, $sequence2) +function zip(iterable $sequence1, iterable $sequence2) { $sequences = func_get_args(); $count = func_num_args(); for ($j = 0; $j < $count; ++$j) { - args\expects(args\traversable, $sequences[$j], $j + 1); + args\expects(args\iterable_, $sequences[$j], $j + 1); if ($sequences[$j] instanceof \Iterator) { $sequences[$j] = iterator_to_array($sequences[$j]); } @@ -137,18 +129,18 @@ function zip($sequence1, $sequence2) * Generalises zip by zipping with the function given as the first argument, instead of an array-creating function * * @param callable $function - * @param array|\Traversable $sequence1 - * @param array|\Traversable $sequence2 + * @param iterable $sequence1 + * @param iterable $sequence2 * @return array */ -function zipWith(callable $function, $sequence1, $sequence2) +function zipWith(callable $function, iterable $sequence1, iterable $sequence2) { $sequences = func_get_args(); array_shift($sequences); $count = count($sequences); for ($j = 0; $j < $count; ++$j) { - args\expects(args\traversable, $sequences[$j], $j + 1); + args\expects(args\iterable_, $sequences[$j], $j + 1); if ($sequences[$j] instanceof \Iterator) { $sequences[$j] = iterator_to_array($sequences[$j]); } @@ -185,14 +177,12 @@ function zipWith(callable $function, $sequence1, $sequence2) * to reduce the sequence to a single value. * * @param callable $function - * @param array|\Traversable $sequence + * @param iterable $sequence * @param mixed $initial * @return mixed */ -function reduce(callable $function, $sequence, $initial = 0) +function reduce(callable $function, iterable $sequence, $initial = 0) { - args\expects(args\traversable, $sequence); - foreach ($sequence as $item) { $initial = $function($initial, $item); } @@ -205,13 +195,11 @@ function reduce(callable $function, $sequence, $initial = 0) * Returns sequence items that satisfy the predicate * * @param callable $predicate - * @param array|\Traversable $sequence + * @param iterable $sequence * @return array */ -function filter(callable $predicate, $sequence) +function filter(callable $predicate, iterable $sequence) { - args\expects(args\traversable, $sequence); - $prevKey = -1; $isList = true; @@ -237,13 +225,11 @@ function filter(callable $predicate, $sequence) * Returns sequence items that don't satisfy the predicate * * @param callable $predicate - * @param array|\Traversable $sequence + * @param iterable $sequence * @return array */ -function filterNot(callable $predicate, $sequence) +function filterNot(callable $predicate, iterable $sequence) { - args\expects(args\traversable, $sequence); - $prevKey = -1; $isList = true; @@ -268,14 +254,13 @@ function filterNot(callable $predicate, $sequence) /** * Returns the first N sequence items with the given step * - * @param array|\Traversable $sequence + * @param iterable $sequence * @param int $N * @param int $step * @return array */ -function take($sequence, $N, $step = 1) +function take(iterable $sequence, $N, $step = 1) { - args\expects(args\traversable, $sequence); args\expects(args\int, $N); args\expects(args\int, $step, 3); @@ -335,13 +320,11 @@ function takeKeys($sequence, array $keys) * Returns the longest sequence prefix of all items which satisfy the predicate * * @param callable $predicate - * @param array|\Traversable $sequence + * @param iterable $sequence * @return array */ -function takeWhile(callable $predicate, $sequence) +function takeWhile(callable $predicate, iterable $sequence) { - args\expects(args\traversable, $sequence); - $result = []; foreach ($sequence as $item) { if ($predicate($item)) { @@ -359,13 +342,11 @@ function takeWhile(callable $predicate, $sequence) /** * Returns the first sequence item * - * @param array|\Traversable $sequence + * @param iterable $sequence * @return mixed */ -function first($sequence) +function first(iterable $sequence) { - args\expects(args\traversable, $sequence); - $counter = 0; foreach ($sequence as $item) { ++$counter; @@ -383,13 +364,11 @@ function first($sequence) /** * Returns the second sequence item * - * @param array|\Traversable $sequence + * @param iterable $sequence * @return mixed */ -function second($sequence) +function second(iterable $sequence) { - args\expects(args\traversable, $sequence); - $counter = 0; foreach ($sequence as $item) { if (++$counter < 2) { @@ -409,13 +388,11 @@ function second($sequence) /** * Returns the last sequence item * - * @param array|\Traversable $sequence + * @param iterable $sequence * @return mixed */ -function last($sequence) +function last(iterable $sequence) { - args\expects(args\traversable, $sequence); - if (!$sequence) { throw new \InvalidArgumentException('Can not return the last item of an empty sequence'); } @@ -433,13 +410,12 @@ function last($sequence) /** * Drops the first N sequence items * - * @param array|\Traversable $sequence + * @param iterable $sequence * @param int $N * @return array */ -function drop($sequence, $N) +function drop(iterable $sequence, $N) { - args\expects(args\traversable, $sequence); args\expects(args\int, $N); if (is_array($sequence)) { @@ -465,13 +441,11 @@ function drop($sequence, $N) * Drops the longest sequence prefix of all items which satisfy the predicate * * @param callable $predicate - * @param array|\Traversable $sequence + * @param iterable $sequence * @return array */ -function dropWhile(callable $predicate, $sequence) +function dropWhile(callable $predicate, iterable $sequence) { - args\expects(args\traversable, $sequence); - $drop = true; $result = []; foreach ($sequence as $item) { @@ -517,13 +491,11 @@ function dropKeys($sequence, array $keys) * the items that returned false * * @param callable $predicate - * @param array|\Traversable $sequence + * @param iterable $sequence * @return array */ -function partition(callable $predicate, $sequence) +function partition(callable $predicate, iterable $sequence) { - args\expects(args\traversable, $sequence); - $isList = isList($sequence); $result = [[], []]; @@ -547,13 +519,11 @@ function partition(callable $predicate, $sequence) * false, and the other containing all the items that left * * @param callable $predicate - * @param array|\Traversable $sequence + * @param iterable $sequence * @return array */ -function span(callable $predicate, $sequence) +function span(callable $predicate, iterable $sequence) { - args\expects(args\traversable, $sequence); - $isList = isList($sequence); $result = [[], []]; @@ -578,15 +548,14 @@ function span(callable $predicate, $sequence) /** * Returns array which contains indexed sequence items * - * @param array|\Traversable $sequence List of arrays or objects + * @param iterable $sequence List of arrays or objects * @param int|string|callable $by An array key or a function * @param bool $keepLast If true only the last item with the key will be returned otherwise list of items which share the same key value will be returned * @param callable|null $transform A function that transforms list item after indexing * @return array */ -function indexed($sequence, $by, $keepLast = true, callable $transform = null) +function indexed(iterable $sequence, $by, $keepLast = true, callable $transform = null) { - args\expects(args\traversable, $sequence); args\expects([args\arrayKey, args\callable_], $by); args\expects(args\bool, $keepLast); @@ -617,16 +586,15 @@ function indexed($sequence, $by, $keepLast = true, callable $transform = null) /** * Returns array which contains sorted items from the passed sequence * - * @param array|\Traversable $sequence + * @param iterable $sequence * @param bool|callable $reversed If true then return reversed sorted sequence. If not boolean and $key was not passed then acts as a $key parameter * @param callable $key Function of one argument that is used to extract a comparison key from each item * @param callable $cmp Function of two arguments which returns a negative number, zero or positive number depending on * whether the first argument is smaller than, equal to, or larger than the second argument * @return array */ -function sorted($sequence, $reversed = false, callable $key = null, callable $cmp = null) +function sorted(iterable $sequence, $reversed = false, callable $key = null, callable $cmp = null) { - args\expects(args\traversable, $sequence); args\expects([args\bool, args\callable_], $reversed); if (!$cmp) { @@ -661,13 +629,12 @@ function sorted($sequence, $reversed = false, callable $key = null, callable $cm /** * Returns array which contains sequence items sorted by keys * - * @param array|\Traversable $sequence + * @param iterable $sequence * @param bool $reversed * @return array */ -function keySorted($sequence, $reversed = false) +function keySorted(iterable $sequence, $reversed = false) { - args\expects(args\traversable, $sequence); args\expects(args\bool, $reversed); if ($sequence instanceof \Iterator) { @@ -688,13 +655,12 @@ function keySorted($sequence, $reversed = false) /** * Flattens multidimensional sequence * - * @param array|\Traversable $sequence + * @param iterable $sequence * @param int|null $depth * @return array */ -function flatten($sequence, $depth = null) +function flatten(iterable $sequence, $depth = null) { - args\expects(args\traversable, $sequence); args\expectsOptional(args\int, $depth); if (null === $depth) { @@ -734,13 +700,12 @@ function flatten($sequence, $depth = null) /** * Returns a list of (key, value) pairs * - * @param array|\Traversable $sequence + * @param iterable $sequence * @param bool $valueKey If true then returns (value, key) pairs * @return array */ -function pairs($sequence, $valueKey = false) +function pairs(iterable $sequence, $valueKey = false) { - args\expects(args\traversable, $sequence); args\expects(args\bool, $valueKey); if (!$sequence) { @@ -759,15 +724,12 @@ function pairs($sequence, $valueKey = false) /** * Returns array containing $sequence1 items and $sequence2 items * - * @param array|\Traversable $sequence1 - * @param array|\Traversable $sequence2 + * @param iterable $sequence1 + * @param iterable $sequence2 * @return array */ -function merge($sequence1, $sequence2) +function merge(iterable $sequence1, iterable $sequence2) { - args\expects(args\traversable, $sequence1); - args\expects(args\traversable, $sequence2, 2); - $result = $sequence1 instanceof \Iterator ? iterator_to_array($sequence1) : $sequence1; @@ -837,10 +799,10 @@ function value($array, $key, $default = null) /** * Returns list of the sequence keys * - * @param array|\Traversable $sequence + * @param iterable $sequence * @return array */ -function keys($sequence) +function keys(iterable $sequence) { if (is_array($sequence)) { return array_keys($sequence); @@ -858,10 +820,10 @@ function keys($sequence) /** * Returns list of the sequence values * - * @param array|\Traversable $sequence + * @param iterable $sequence * @return array */ -function values($sequence) +function values(iterable $sequence) { if (is_array($sequence)) { return array_values($sequence); @@ -889,13 +851,13 @@ function isList($var) const isList = '\nspl\a\isList'; /** - * Checks if the item is present in array or traversable object + * Checks if the item is present in iterable (array or traversable object) * * @param mixed $item - * @param array|\Traversable $sequence + * @param iterable $sequence * @return bool */ -function in($item, $sequence) +function in($item, iterable $sequence) { if (is_array($sequence)) { return in_array($item, $sequence); @@ -905,7 +867,6 @@ function in($item, $sequence) return in_array($item, $sequence->toArray()); } - args\expects(args\traversable, $sequence); foreach ($sequence as $sequenceItem) { if ($sequenceItem === $item) { return true; @@ -917,13 +878,13 @@ function in($item, $sequence) const in = '\nspl\a\in'; /** - * Computes the difference of arrays or traversable objects + * Computes the difference of iterables (arrays or traversable objects) * - * @param array|\Traversable $sequence1 - * @param array|\Traversable $sequence2 + * @param iterable $sequence1 + * @param iterable $sequence2 * @return array */ -function diff($sequence1, $sequence2) +function diff(iterable $sequence1, iterable $sequence2) { if (is_array($sequence1)) { $toDiff1 = $sequence1; @@ -950,13 +911,13 @@ function diff($sequence1, $sequence2) const diff = '\nspl\a\diff'; /** - * Computes the intersection of arrays or traversable objects + * Computes the intersection of iterables (arrays or traversable objects) * - * @param array|\Traversable $sequence1 - * @param array|\Traversable $sequence2 + * @param iterable $sequence1 + * @param iterable $sequence2 * @return array */ -function intersect($sequence1, $sequence2) +function intersect(iterable $sequence1, iterable $sequence2) { if (is_array($sequence1)) { $toDiff1 = $sequence1; @@ -983,12 +944,12 @@ function intersect($sequence1, $sequence2) const intersect = '\nspl\a\intersect'; /** - * Computes the cartesian product of two or more arrays or traversable objects + * Computes the cartesian product of two or more iterables (arrays or traversable objects) * - * @param array|\Traversable $sequences + * @param iterable $sequences * @return array */ -function cartesianProduct($sequences) +function cartesianProduct(iterable $sequences) { $count = func_num_args(); if ($count > 1) { @@ -1028,10 +989,10 @@ function with($sequence) //region deprecated /** * @deprecated - * @param array|\Traversable $var + * @param iterable $var * @return array */ -function traversableToArray($var) +function traversableToArray(iterable $var) { return $var instanceof \Iterator ? iterator_to_array($var) @@ -1043,15 +1004,12 @@ function traversableToArray($var) * @see \nspl\a\merge * Returns arrays containing $sequence1 items and $sequence2 items * - * @param array|\Traversable $sequence1 - * @param array|\Traversable $sequence2 + * @param iterable $sequence1 + * @param iterable $sequence2 * @return array */ -function extend($sequence1, $sequence2) +function extend(iterable $sequence1, iterable $sequence2) { - args\expects(args\traversable, $sequence1); - args\expects(args\traversable, $sequence2, 2); - return array_merge(traversableToArray($sequence1), traversableToArray(($sequence2))); } const extend = '\nspl\a\merge'; diff --git a/nspl/a/ChainableSequence.php b/nspl/a/ChainableSequence.php index ed4359f..0b73a21 100644 --- a/nspl/a/ChainableSequence.php +++ b/nspl/a/ChainableSequence.php @@ -162,10 +162,10 @@ public function flatMap(callable $function) /** * Zips sequence with a sequence * - * @param array|\Traversable $sequence + * @param iterable $sequence * @return $this */ - public function zip($sequence) + public function zip(iterable $sequence) { return new self(zip($this->sequence, $sequence)); } @@ -174,10 +174,10 @@ public function zip($sequence) * Generalises zip by zipping with the function given as the first argument * * @param callable $function - * @param array|\Traversable $sequence + * @param iterable $sequence * @return $this */ - public function zipWith(callable $function, $sequence) + public function zipWith(callable $function, iterable $sequence) { return new self(zipWith($function, $this->sequence, $sequence)); } @@ -415,10 +415,10 @@ public function pairs($valueKey = false) /** * Merges sequence with the given sequence * - * @param array|\Traversable $sequence + * @param iterable $sequence * @return $this */ - public function merge($sequence) + public function merge(iterable $sequence) { return new self(merge($this->sequence, $sequence)); } @@ -481,10 +481,10 @@ public function contains($item) /** * Computes the difference with the given sequence * - * @param array|\Traversable $sequence + * @param iterable $sequence * @return $this */ - public function diff($sequence) + public function diff(iterable $sequence) { return new self(diff($this->sequence, $sequence)); } @@ -492,18 +492,18 @@ public function diff($sequence) /** * Computes the intersection with the given sequence * - * @param array|\Traversable $sequence + * @param iterable $sequence * @return $this */ - public function intersect($sequence) + public function intersect(iterable $sequence) { return new self(intersect($this->sequence, $sequence)); } /** - * Computes the cartesian product of two or more arrays or traversable objects + * Computes the cartesian product of two or more iterables (arrays or traversable objects) * - * @param array|\Traversable $sequences + * @param iterable $sequences * @return $this */ public function cartesianProduct() diff --git a/nspl/a/lazy.php b/nspl/a/lazy.php index b00c2df..f3994bd 100644 --- a/nspl/a/lazy.php +++ b/nspl/a/lazy.php @@ -8,13 +8,11 @@ * Applies function of one argument to each sequence item lazily * * @param callable $function - * @param array|\Traversable $sequence + * @param iterable $sequence * @return \Generator */ -function map(callable $function, $sequence) +function map(callable $function, iterable $sequence) { - args\expects(args\traversable, $sequence); - foreach ($sequence as $key => $item) { yield $key => $function($item); } @@ -25,13 +23,11 @@ function map(callable $function, $sequence) * Lazily applies function of one argument to each sequence item and flattens the result * * @param callable $function - * @param array|\Traversable $sequence + * @param iterable $sequence * @return \Generator */ -function flatMap(callable $function, $sequence) +function flatMap(callable $function, iterable $sequence) { - args\expects(args\traversable, $sequence); - foreach ($sequence as $item) { foreach ($function($item) as $resultValue) { yield $resultValue; @@ -43,17 +39,17 @@ function flatMap(callable $function, $sequence) /** * Zips two or more sequences lazily * - * @param array|\Traversable $sequence1 - * @param array|\Traversable $sequence2 + * @param iterable $sequence1 + * @param iterable $sequence2 * @return \Generator */ -function zip($sequence1, $sequence2) +function zip(iterable $sequence1, iterable $sequence2) { $sequences = func_get_args(); $count = func_num_args(); for ($j = 0; $j < $count; ++$j) { - args\expects(args\traversable, $sequences[$j], $j + 1); + args\expects(args\iterable_, $sequences[$j], $j + 1); if (is_array($sequences[$j])) { $sequences[$j] = new \ArrayIterator($sequences[$j]); } @@ -80,18 +76,18 @@ function zip($sequence1, $sequence2) * Generalises zip by zipping with the function given as the first argument, instead of an array-creating function * * @param callable $function - * @param array|\Traversable $sequence1 - * @param array|\Traversable $sequence2 + * @param iterable $sequence1 + * @param iterable $sequence2 * @return \Generator */ -function zipWith(callable $function, $sequence1, $sequence2) +function zipWith(callable $function, iterable $sequence1, iterable $sequence2) { $sequences = func_get_args(); array_shift($sequences); $count = count($sequences); for ($j = 0; $j < $count; ++$j) { - args\expects(args\traversable, $sequences[$j], $j + 1); + args\expects(args\iterable_, $sequences[$j], $j + 1); if (is_array($sequences[$j])) { $sequences[$j] = new \ArrayIterator($sequences[$j]); } @@ -120,13 +116,11 @@ function zipWith(callable $function, $sequence1, $sequence2) * Lazily returns sequence items that satisfy the predicate * * @param callable $predicate - * @param array|\Traversable $sequence + * @param iterable $sequence * @return \Generator */ -function filter(callable $predicate, $sequence) +function filter(callable $predicate, iterable $sequence) { - args\expects(args\traversable, $sequence); - foreach ($sequence as $key => $item) { if ($predicate($item)) { yield $key => $item; @@ -139,13 +133,11 @@ function filter(callable $predicate, $sequence) * Lazily returns sequence items that don't satisfy the predicate * * @param callable $predicate - * @param array|\Traversable $sequence + * @param iterable $sequence * @return \Generator */ -function filterNot(callable $predicate, $sequence) +function filterNot(callable $predicate, iterable $sequence) { - args\expects(args\traversable, $sequence); - foreach ($sequence as $key => $item) { if (!$predicate($item)) { yield $key => $item; @@ -157,14 +149,13 @@ function filterNot(callable $predicate, $sequence) /** * Returns first N sequence items with given step * - * @param array|\Traversable $sequence + * @param iterable $sequence * @param int $N * @param int $step * @return \Generator */ -function take($sequence, $N, $step = 1) +function take(iterable $sequence, $N, $step = 1) { - args\expects(args\traversable, $sequence); args\expects(args\int, $N); args\expects(args\int, $step, 3); @@ -191,13 +182,11 @@ function take($sequence, $N, $step = 1) * Returns the longest sequence prefix of all items which satisfy the predicate * * @param callable $predicate - * @param array|\Traversable $sequence + * @param iterable $sequence * @return \Generator */ -function takeWhile(callable $predicate, $sequence) +function takeWhile(callable $predicate, iterable $sequence) { - args\expects(args\traversable, $sequence); - foreach ($sequence as $item) { if ($predicate($item)) { yield $item; @@ -212,13 +201,12 @@ function takeWhile(callable $predicate, $sequence) /** * Drops first N sequence items * - * @param array|\Traversable $sequence + * @param iterable $sequence * @param int $N * @return \Generator */ -function drop($sequence, $N) +function drop(iterable $sequence, $N) { - args\expects(args\traversable, $sequence); args\expects(args\int, $N); $counter = 0; @@ -236,13 +224,11 @@ function drop($sequence, $N) * Drops the longest sequence prefix of all items which satisfy the predicate * * @param callable $predicate - * @param array|\Traversable $sequence + * @param iterable $sequence * @return \Generator */ -function dropWhile(callable $predicate, $sequence) +function dropWhile(callable $predicate, iterable $sequence) { - args\expects(args\traversable, $sequence); - $drop = true; foreach ($sequence as $item) { if ($drop) { @@ -263,13 +249,11 @@ function dropWhile(callable $predicate, $sequence) * the items that returned false * * @param callable $predicate - * @param array|\Traversable $sequence + * @param iterable $sequence * @return \Generator[] */ -function partition(callable $predicate, $sequence) +function partition(callable $predicate, iterable $sequence) { - args\expects(args\traversable, $sequence); - $checked = array(); $first = function() use ($sequence, $predicate, &$checked) { @@ -303,13 +287,12 @@ function partition(callable $predicate, $sequence) /** * Flattens multidimensional sequence * - * @param array|\Traversable $sequence + * @param iterable $sequence * @param int|null $depth * @return \Generator */ -function flatten($sequence, $depth = null) +function flatten(iterable $sequence, $depth = null) { - args\expects(args\traversable, $sequence); args\expectsOptional(args\int, $depth); foreach ($sequence as $value) { @@ -332,13 +315,12 @@ function flatten($sequence, $depth = null) /** * Returns list of (key, value) pairs - * @param array|\Traversable $sequence + * @param iterable $sequence * @param bool $valueKey If true then convert array to (value, key) pairs * @return \Generator */ -function pairs($sequence, $valueKey = false) +function pairs(iterable $sequence, $valueKey = false) { - args\expects(args\traversable, $sequence); args\expects(args\bool, $valueKey); foreach ($sequence as $key => $value) { @@ -349,10 +331,10 @@ function pairs($sequence, $valueKey = false) /** * Returns list of the sequence keys - * @param array|\Traversable $sequence + * @param iterable $sequence * @return \Generator */ -function keys($sequence) +function keys(iterable $sequence) { foreach ($sequence as $key => $_) { yield $key; @@ -366,7 +348,7 @@ function keys($sequence) * @param array|\Iterator|\IteratorAggregate $sequence * @return LazyChainableSequence */ -function with($sequence) +function with(iterable $sequence) { return new LazyChainableSequence($sequence); } diff --git a/nspl/a/lazy/LazyChainableSequence.php b/nspl/a/lazy/LazyChainableSequence.php index 2342416..de1f5c7 100644 --- a/nspl/a/lazy/LazyChainableSequence.php +++ b/nspl/a/lazy/LazyChainableSequence.php @@ -31,10 +31,10 @@ public function flatMap(callable $function) /** * Zips sequence with a sequence * - * @param array|\Traversable $sequence + * @param iterable $sequence * @return $this */ - public function zip($sequence) + public function zip(iterable $sequence) { return new self(zip($this->sequence, $sequence)); } @@ -43,10 +43,10 @@ public function zip($sequence) * Generalises zip by zipping with the function given as the first argument * * @param callable $function - * @param array|\Traversable $sequence + * @param iterable $sequence * @return $this */ - public function zipWith(callable $function, $sequence) + public function zipWith(callable $function, iterable $sequence) { return new self(zipWith($function, $this->sequence, $sequence)); } diff --git a/nspl/args.php b/nspl/args.php index 814ae11..a239aaf 100644 --- a/nspl/args.php +++ b/nspl/args.php @@ -92,8 +92,12 @@ function expectsOptional($constraints, $arg, $atPosition = null, $otherwiseThrow const object = 'is_object'; const callable_ = 'is_callable'; const arrayKey = '\nspl\args\_p\isArrayKey'; -const traversable = '\nspl\args\_p\isTraversable'; +const iterable_ = 'is_iterable'; const arrayAccess = '\nspl\args\_p\isArrayAccess'; +/** + * @deprecated + */ +const traversable = 'is_iterable'; // And-constraints const nonEmpty = '\nspl\args\_p\isNotEmpty'; @@ -371,8 +375,8 @@ function expectsArrayKey($arg, $atPosition = null, $otherwiseThrow = '\InvalidAr */ function expectsTraversable($arg, $atPosition = null, $otherwiseThrow = '\InvalidArgumentException') { - if (!is_array($arg) && !$arg instanceof \Traversable) { - _p\throwExpectsException($arg, 'be an array or traversable', $atPosition, $otherwiseThrow); + if (!is_iterable($arg)) { + _p\throwExpectsException($arg, 'be iterable', $atPosition, $otherwiseThrow); } } @@ -507,7 +511,6 @@ function expectsWithKeys(array $array, array $keys, $atPosition = null, $otherwi function isNotEmpty($value) { return (bool) $value; } function isArrayKey($value) { return is_int($value) || is_string($value); } -function isTraversable($value) { return is_array($value) || $value instanceof \Traversable; } function isArrayAccess($value) { return is_array($value) || $value instanceof \ArrayAccess; } function isPositive($value) { return $value > 0; } function isNotNegative($value) { return $value >= 0; } @@ -615,6 +618,7 @@ class Checker \nspl\args\object => true, \nspl\args\arrayKey => true, \nspl\args\callable_ => true, + \nspl\args\iterable_ => true, \nspl\args\traversable => true, \nspl\args\arrayAccess => true, ); @@ -633,7 +637,8 @@ class ErrorMessage \nspl\args\array_ => 'be an array', \nspl\args\object => 'be an object', \nspl\args\callable_ => 'be callable', - \nspl\args\traversable => 'be an array or traversable', + \nspl\args\iterable_ => 'be iterable', + \nspl\args\traversable => 'be iterable', \nspl\args\arrayAccess => 'be an array or implement array access', \nspl\args\nonEmpty => 'not be empty', @@ -1034,4 +1039,4 @@ public function __toString() }, $this->values)); } -} \ No newline at end of file +} diff --git a/nspl/ds/Set.php b/nspl/ds/Set.php index 0fd32e9..b3cd3d5 100644 --- a/nspl/ds/Set.php +++ b/nspl/ds/Set.php @@ -25,17 +25,12 @@ public function add($element) } /** - * @param array|\Traversable $sequence1 - * @param array|\Traversable $sequence2 - * @param ... - * @param array|\Traversable $sequenceN + * @param iterable[] ...$sequences * @return $this */ - public function update($sequence1 /*, $sequence2, ..., $sequenceN */) + public function update(iterable ...$sequences) { - foreach (func_get_args() as $position => $sequence) { - args\expects(args\traversable, $sequence, $position); - + foreach ($sequences as $sequence) { foreach ($sequence as $element) { $this->array[static::getElementKey($element)] = $element; } @@ -65,10 +60,10 @@ public function delete($element) } /** - * @param Set|array|\Traversable $sequence + * @param iterable $sequence * @return Set */ - public function intersection($sequence) + public function intersection(iterable $sequence) { if ($sequence instanceof Set) { $result = new Set(); @@ -77,8 +72,6 @@ public function intersection($sequence) return $result; } - args\expects(args\traversable, $sequence); - $result = new Set(); foreach ($sequence as $element) { $elementKey = static::getElementKey($element); @@ -91,10 +84,10 @@ public function intersection($sequence) } /** - * @param Set|array|\Traversable $sequence + * @param iterable $sequence * @return Set */ - public function difference($sequence) + public function difference(iterable $sequence) { if ($sequence instanceof Set) { $result = new Set(); @@ -103,8 +96,6 @@ public function difference($sequence) return $result; } - args\expects(args\traversable, $sequence); - $result = new Set(); $intersection = $this->intersection($sequence); foreach ($this->array as $element) { @@ -118,10 +109,10 @@ public function difference($sequence) } /** - * @param Set|array|\Traversable $sequence + * @param iterable $sequence * @return Set */ - public function union($sequence) + public function union(iterable $sequence) { if ($sequence instanceof Set) { $result = new Set(); @@ -130,8 +121,6 @@ public function union($sequence) return $result; } - args\expects(args\traversable, $sequence); - $result = $this->copy(); foreach ($sequence as $element) { $result->array[static::getElementKey($element)] = $element; @@ -141,17 +130,15 @@ public function union($sequence) } /** - * @param Set|array|\Traversable $sequence + * @param iterable $sequence * @return bool */ - public function isSuperset($sequence) + public function isSuperset(iterable $sequence) { if ($sequence instanceof Set) { return array_intersect_key($this->array, $sequence->array) === $sequence->array; } - args\expects(args\traversable, $sequence); - foreach ($sequence as $element) { $elementKey = static::getElementKey($element); if (!isset($this->array[$elementKey]) && !array_key_exists($element, $this->array)) { @@ -164,17 +151,15 @@ public function isSuperset($sequence) } /** - * @param Set|array|\Traversable $sequence + * @param iterable $sequence * @return bool */ - public function isSubset($sequence) + public function isSubset(iterable $sequence) { if ($sequence instanceof Set) { return array_intersect_key($this->array, $sequence->array) === $this->array; } - args\expects(args\traversable, $sequence); - $size = count($this->array); $present = array(); foreach ($sequence as $element) { diff --git a/nspl/f.php b/nspl/f.php index 2bd41f1..1e6d3ac 100644 --- a/nspl/f.php +++ b/nspl/f.php @@ -261,12 +261,11 @@ function throttled(callable $function, $wait) * Applies function of one argument to each sequence item * * @param callable $function - * @param array|\Traversable $sequence + * @param iterable $sequence * @return array */ -function map(callable $function, $sequence) +function map(callable $function, iterable $sequence) { - args\expects(args\traversable, $sequence); return array_map($function, a\traversableToArray($sequence)); } const map = '\nspl\a\map'; @@ -279,13 +278,12 @@ function map(callable $function, $sequence) * to a single value. * * @param callable $function - * @param array|\Traversable $sequence + * @param iterable $sequence * @param mixed $initial * @return array */ -function reduce(callable $function, $sequence, $initial = 0) +function reduce(callable $function, iterable $sequence, $initial = 0) { - args\expects(args\traversable, $sequence); return array_reduce(a\traversableToArray($sequence), $function, $initial); } const reduce = '\nspl\a\reduce'; @@ -297,13 +295,11 @@ function reduce(callable $function, $sequence, $initial = 0) * Returns sequence items that satisfy the predicate * * @param callable $predicate - * @param array|\Traversable $sequence + * @param iterable $sequence * @return array */ -function filter(callable $predicate, $sequence) +function filter(callable $predicate, iterable $sequence) { - args\expects(args\traversable, $sequence); - $sequence = a\traversableToArray($sequence); $filtered = array_filter($sequence, $predicate); return a\isList($sequence) ? array_values($filtered) : $filtered; @@ -318,13 +314,11 @@ function filter(callable $predicate, $sequence) * the elements that returned false * * @param callable $predicate - * @param array|\Traversable $sequence + * @param iterable $sequence * @return array */ -function partition(callable $predicate, $sequence) +function partition(callable $predicate, iterable $sequence) { - args\expects(args\traversable, $sequence); - $isList = a\isList($sequence); $result = [[], []]; foreach ($sequence as $k => $v) { @@ -348,13 +342,11 @@ function partition(callable $predicate, $sequence) * false, and the other containing all the elements that left * * @param callable $predicate - * @param array|\Traversable $sequence + * @param iterable $sequence * @return array */ -function span(callable $predicate, $sequence) +function span(callable $predicate, iterable $sequence) { - args\expects(args\traversable, $sequence); - $isList = a\isList($sequence); $result = [[], []]; diff --git a/nspl/rnd.php b/nspl/rnd.php index 93cb5e0..efd110f 100644 --- a/nspl/rnd.php +++ b/nspl/rnd.php @@ -21,14 +21,13 @@ function randomString($length, $source = ALPHA_NUM) /** * Returns a k length list of unique items chosen from the population sequence * - * @param array|\Traversable $population + * @param iterable $population * @param int $length * @param bool $preserveKeys * @return array */ -function sample($population, $length, $preserveKeys = false) +function sample(iterable $population, $length, $preserveKeys = false) { - args\expects(args\traversable, $population); args\expects(args\int, $length); args\expects(args\bool, $preserveKeys); @@ -56,17 +55,15 @@ function sample($population, $length, $preserveKeys = false) /** * Returns a random item from a non-empty sequence * - * @param array|\Traversable $sequence + * @param iterable $sequence * @return mixed */ -function choice($sequence) +function choice(iterable $sequence) { if (!$sequence) { throw new \InvalidArgumentException('Sequence is empty'); } - args\expects(args\traversable, $sequence); - if ($sequence instanceof \Iterator) { $sequence = iterator_to_array($sequence); } diff --git a/tests/NsplTest/ArgsTest.php b/tests/NsplTest/ArgsTest.php index cb6bc0d..7ef321c 100644 --- a/tests/NsplTest/ArgsTest.php +++ b/tests/NsplTest/ArgsTest.php @@ -14,7 +14,7 @@ use const \nspl\args\string; use const \nspl\args\callable_; use const \nspl\args\arrayKey; -use const \nspl\args\traversable; +use const \nspl\args\iterable_; use const \nspl\args\arrayAccess; // And-constraints @@ -36,6 +36,7 @@ use function \nspl\args\smallerThan; // @todo Move deprecated stuff into a separate test +use const \nspl\args\traversable; use function \nspl\args\expectsNotEmpty; use function \nspl\args\expectsBool; use function \nspl\args\expectsInt; @@ -157,20 +158,20 @@ function expectsArrayKeyNegativeTest($arg1, $arg2) { expects(arrayKey, $arg1); } } #endregion - #region traversable - public function testExpectsTraversable_Positive() + #region iterable_ + public function testExpectsIterable_Positive() { - function expectsTraversablePositiveTest($arg1) { expects(traversable, $arg1); } - $this->assertNull(expectsTraversablePositiveTest(array('hello', 'world'))); - $this->assertNull(expectsTraversablePositiveTest(new \ArrayIterator(array('hello', 'world')))); + function expectsIterablePositiveTest($arg1) { expects(iterable_, $arg1); } + $this->assertNull(expectsIterablePositiveTest(array('hello', 'world'))); + $this->assertNull(expectsIterablePositiveTest(new \ArrayIterator(array('hello', 'world')))); } - public function testExpectsTraversable_Negative() + public function testExpectsIterable_Negative() { $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsTraversableNegativeTest() must be an array or traversable, string 'hello world' given"); - function expectsTraversableNegativeTest($arg1) { expects(traversable, $arg1); } - $this->assertNull(expectsTraversableNegativeTest('hello world')); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsIterableNegativeTest() must be iterable, string 'hello world' given"); + function expectsIterableNegativeTest($arg1) { expects(iterable_, $arg1); } + $this->assertNull(expectsIterableNegativeTest('hello world')); } #endregion @@ -178,8 +179,8 @@ function expectsTraversableNegativeTest($arg1) { expects(traversable, $arg1); } public function testExpectsArrayAccess_Positive() { function expectsArrayAccessPositiveTest($arg1) { expects(arrayAccess, $arg1); } - $this->assertNull(expectsTraversablePositiveTest(array('hello', 'world'))); - $this->assertNull(expectsTraversablePositiveTest(new \ArrayObject(array('hello', 'world')))); + $this->assertNull(expectsArrayAccessPositiveTest(array('hello', 'world'))); + $this->assertNull(expectsArrayAccessPositiveTest(new \ArrayObject(array('hello', 'world')))); } public function testExpectsArrayAccess_Negative() @@ -644,6 +645,20 @@ function expectsExceptionFileAndLineTest($arg) { expects(bool, $arg); } #endregion #region Deprecated + public function testExpectsTraversable_Positive() + { + function expectsTraversablePositiveTest($arg1) { expects(traversable, $arg1); } + $this->assertNull(expectsTraversablePositiveTest(array('hello', 'world'))); + $this->assertNull(expectsTraversablePositiveTest(new \ArrayIterator(array('hello', 'world')))); + } + + public function testExpectsTraversable_Negative() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\expectsTraversableNegativeTest() must be iterable, string 'hello world' given"); + function expectsTraversableNegativeTest($arg1) { expects(traversable, $arg1); } + $this->assertNull(expectsTraversableNegativeTest('hello world')); + } public function testDeprecatedExpectsNotEmpty_Positive() { function deprecatedExpectsNotEmptyPositiveTest($arg) { expectsNotEmpty($arg); } @@ -757,7 +772,7 @@ function deprecatedExpectsTraversablePositiveTest($arg1) { expectsTraversable($a public function testDeprecatedExpectsTraversable_Negative() { $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage("Argument 1 passed to NsplTest\\deprecatedExpectsTraversableNegativeTest() must be an array or traversable, string 'hello world' given"); + $this->expectExceptionMessage("Argument 1 passed to NsplTest\\deprecatedExpectsTraversableNegativeTest() must be iterable, string 'hello world' given"); function deprecatedExpectsTraversableNegativeTest($arg1) { expectsTraversable($arg1); } $this->assertNull(deprecatedExpectsTraversableNegativeTest('hello world')); } From dfb45a3cbf6904a7cdcde5681585633a7ac94328 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Mon, 30 May 2022 21:21:21 +0200 Subject: [PATCH 09/10] a,a\lazy: use explicit variadic arguments It will be clearer that the functions support 2+ arguments and PHP will check for us the type matches the type hint. --- nspl/a.php | 6 ++---- nspl/a/lazy.php | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/nspl/a.php b/nspl/a.php index 43f7b10..44cdfd8 100644 --- a/nspl/a.php +++ b/nspl/a.php @@ -91,13 +91,12 @@ function flatMap(callable $function, iterable $sequence) * @param iterable $sequence2 * @return array */ -function zip(iterable $sequence1, iterable $sequence2) +function zip(iterable $sequence1, iterable $sequence2, iterable ...$moreSequences) { $sequences = func_get_args(); $count = func_num_args(); for ($j = 0; $j < $count; ++$j) { - args\expects(args\iterable_, $sequences[$j], $j + 1); if ($sequences[$j] instanceof \Iterator) { $sequences[$j] = iterator_to_array($sequences[$j]); } @@ -133,14 +132,13 @@ function zip(iterable $sequence1, iterable $sequence2) * @param iterable $sequence2 * @return array */ -function zipWith(callable $function, iterable $sequence1, iterable $sequence2) +function zipWith(callable $function, iterable $sequence1, iterable $sequence2, iterable ...$moreSequences) { $sequences = func_get_args(); array_shift($sequences); $count = count($sequences); for ($j = 0; $j < $count; ++$j) { - args\expects(args\iterable_, $sequences[$j], $j + 1); if ($sequences[$j] instanceof \Iterator) { $sequences[$j] = iterator_to_array($sequences[$j]); } diff --git a/nspl/a/lazy.php b/nspl/a/lazy.php index f3994bd..11ea2a8 100644 --- a/nspl/a/lazy.php +++ b/nspl/a/lazy.php @@ -43,13 +43,12 @@ function flatMap(callable $function, iterable $sequence) * @param iterable $sequence2 * @return \Generator */ -function zip(iterable $sequence1, iterable $sequence2) +function zip(iterable $sequence1, iterable $sequence2, iterable ...$moreSequences) { $sequences = func_get_args(); $count = func_num_args(); for ($j = 0; $j < $count; ++$j) { - args\expects(args\iterable_, $sequences[$j], $j + 1); if (is_array($sequences[$j])) { $sequences[$j] = new \ArrayIterator($sequences[$j]); } @@ -80,14 +79,13 @@ function zip(iterable $sequence1, iterable $sequence2) * @param iterable $sequence2 * @return \Generator */ -function zipWith(callable $function, iterable $sequence1, iterable $sequence2) +function zipWith(callable $function, iterable $sequence1, iterable $sequence2, iterable ...$moreSequences) { $sequences = func_get_args(); array_shift($sequences); $count = count($sequences); for ($j = 0; $j < $count; ++$j) { - args\expects(args\iterable_, $sequences[$j], $j + 1); if (is_array($sequences[$j])) { $sequences[$j] = new \ArrayIterator($sequences[$j]); } From b5129c1a01c438e796dfc709b67beabca8df0117 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Mon, 30 May 2022 21:24:45 +0200 Subject: [PATCH 10/10] Fix missing return type hint deprecation warnings on PHP 8.1 With PHP 8.1 the following warning will be printed: Deprecated: Return type of nspl\a\ChainableSequence::key() should either be compatible with Iterator::key(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice To fix it, we are adding proper type hints where we can and the attribute where it is not possible (like when the type hint would be mixed, which is only available since PHP 8.0). --- nspl/a/ChainableSequence.php | 8 +++++--- nspl/ds/Collection.php | 17 ++++++++++------- nspl/ds/DefaultArray.php | 1 + nspl/ds/Set.php | 8 +++++--- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/nspl/a/ChainableSequence.php b/nspl/a/ChainableSequence.php index 0b73a21..10cbe7a 100644 --- a/nspl/a/ChainableSequence.php +++ b/nspl/a/ChainableSequence.php @@ -45,6 +45,7 @@ public function __construct($sequence) * @link http://php.net/manual/en/iterator.current.php * @return mixed Can return any type. */ + #[\ReturnTypeWillChange] public function current() { return $this->isArray @@ -58,7 +59,7 @@ public function current() * @link http://php.net/manual/en/iterator.next.php * @return void Any returned value is ignored. */ - public function next() + public function next(): void { if ($this->isArray) { $this->isValid = (bool) next($this->sequence); @@ -74,6 +75,7 @@ public function next() * @link http://php.net/manual/en/iterator.key.php * @return mixed scalar on success, or null on failure. */ + #[\ReturnTypeWillChange] public function key() { return $this->isArray @@ -88,7 +90,7 @@ public function key() * @return boolean The return value will be casted to boolean and then evaluated. * Returns true on success or false on failure. */ - public function valid() + public function valid(): bool { return $this->isArray ? $this->isValid @@ -101,7 +103,7 @@ public function valid() * @link http://php.net/manual/en/iterator.rewind.php * @return void Any returned value is ignored. */ - public function rewind() + public function rewind(): void { if ($this->isArray) { reset($this->sequence); diff --git a/nspl/ds/Collection.php b/nspl/ds/Collection.php index 90803f8..9f42b92 100644 --- a/nspl/ds/Collection.php +++ b/nspl/ds/Collection.php @@ -25,7 +25,7 @@ abstract class Collection implements \ArrayAccess, \Iterator, \Countable *

* The return value will be casted to boolean if non-boolean was returned. */ - public function offsetExists($index) + public function offsetExists($index): bool { return isset($this->array[$index]); } @@ -40,6 +40,7 @@ public function offsetExists($index) * @throws \Exception * @return mixed Can return all value types. */ + #[\ReturnTypeWillChange] public function &offsetGet($index) { if (!isset($this->array[$index])) { @@ -62,7 +63,7 @@ public function &offsetGet($index) * @throws \Exception * @return void */ - public function offsetSet($index, $value) + public function offsetSet($index, $value): void { if (null === $index) { $this->array[] = $value; @@ -82,7 +83,7 @@ public function offsetSet($index, $value) * @throws \Exception * @return void */ - public function offsetUnset($index) + public function offsetUnset($index): void { if (!isset($this->array[$index])) { throw new \Exception('Index out of range'); // @todo Throw IndexException @@ -104,6 +105,7 @@ public function offsetUnset($index) * @link http://php.net/manual/en/iterator.current.php * @return mixed Can return any type. */ + #[\ReturnTypeWillChange] public function current() { return current($this->array); @@ -115,7 +117,7 @@ public function current() * @link http://php.net/manual/en/iterator.next.php * @return void Any returned value is ignored. */ - public function next() + public function next(): void { $this->valid = (bool) next($this->array); } @@ -126,6 +128,7 @@ public function next() * @link http://php.net/manual/en/iterator.key.php * @return mixed scalar on success, or null on failure. */ + #[\ReturnTypeWillChange] public function key() { return key($this->array); @@ -138,7 +141,7 @@ public function key() * @return boolean The return value will be casted to boolean and then evaluated. * Returns true on success or false on failure. */ - public function valid() + public function valid(): bool { return $this->valid; } @@ -149,7 +152,7 @@ public function valid() * @link http://php.net/manual/en/iterator.rewind.php * @return void Any returned value is ignored. */ - public function rewind() + public function rewind(): void { reset($this->array); $this->valid = true; @@ -160,7 +163,7 @@ public function rewind() /** * @return int */ - public function count() + public function count(): int { return count($this->array); } diff --git a/nspl/ds/DefaultArray.php b/nspl/ds/DefaultArray.php index 59c4725..49591ef 100644 --- a/nspl/ds/DefaultArray.php +++ b/nspl/ds/DefaultArray.php @@ -53,6 +53,7 @@ public static function fromArray($default, array $array) *

* @return mixed Can return all value types. */ + #[\ReturnTypeWillChange] public function &offsetGet($index) { if (!$this->offsetExists($index)) { diff --git a/nspl/ds/Set.php b/nspl/ds/Set.php index b3cd3d5..a3c4041 100644 --- a/nspl/ds/Set.php +++ b/nspl/ds/Set.php @@ -252,7 +252,7 @@ public function toArray() *

* The return value will be casted to boolean if non-boolean was returned. */ - public function offsetExists($index) + public function offsetExists($index): bool { throw new \BadMethodCallException('Set does not support indexing'); } @@ -267,6 +267,7 @@ public function offsetExists($index) * @throws \Exception * @return mixed Can return all value types. */ + #[\ReturnTypeWillChange] public function &offsetGet($index) { throw new \BadMethodCallException('Set does not support indexing'); @@ -285,7 +286,7 @@ public function &offsetGet($index) * @throws \Exception * @return void */ - public function offsetSet($index, $value) + public function offsetSet($index, $value): void { if (null === $index) { $this->array[static::getElementKey($value)] = $value; @@ -305,7 +306,7 @@ public function offsetSet($index, $value) * @throws \Exception * @return void */ - public function offsetUnset($index) + public function offsetUnset($index): void { throw new \BadMethodCallException('Set does not support indexing'); } @@ -318,6 +319,7 @@ public function offsetUnset($index) * @link http://php.net/manual/en/iterator.key.php * @return mixed scalar on success, or null on failure. */ + #[\ReturnTypeWillChange] public function key() { throw new \BadMethodCallException('Set does not have keys');