Skip to content

Commit 4cb52c7

Browse files
authored
Merge pull request #102 from akoryak/closures
Add filter for Closure functions
2 parents d5ea1dd + 51969ea commit 4cb52c7

File tree

5 files changed

+133
-1
lines changed

5 files changed

+133
-1
lines changed

.github/workflows/php.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
php-versions: ['7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
1515
steps:
1616
- name: Checkout
17-
uses: actions/checkout@v2
17+
uses: actions/checkout@v4
1818
- name: Install PHP
1919
uses: shivammathur/setup-php@v2
2020
with:

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,17 @@ The following checks that `$value` is a URL.
644644
\TraderInteractive\Filter\Url::filter($value);
645645
```
646646

647+
#### Closures::filter
648+
Aliased in the filterer as `closure`, this filter verifies that the argument is a closure function.
649+
650+
The following checks that `$closureMethod` is a closure.
651+
```php
652+
$closureMethod = function () {
653+
doSomething();
654+
};
655+
\TraderInteractive\Filter\Closures::filter($closureMethod);
656+
```
657+
647658
#### Email::filter
648659
Aliased in the filterer as `email`, this filter verifies that the argument is an email.
649660

src/Filter/Closures.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace TraderInteractive\Filter;
4+
5+
use TraderInteractive\Exceptions\FilterException;
6+
7+
/**
8+
* A collection of filters for Closures.
9+
*/
10+
class Closures
11+
{
12+
/**
13+
* Filters $value to a Closure strictly.
14+
* The return value is \Closure, as expected by the \TraderInteractive\Filterer class.
15+
*
16+
* @param mixed $value the value to filter to a closure function
17+
* @param bool $allowNull Set to true if NULL values are allowed. The filtered result of a NULL value is NULL
18+
*
19+
* @return \Closure|null the filtered $value
20+
*
21+
* @throws FilterException
22+
*/
23+
public static function filter($value, bool $allowNull = false)
24+
{
25+
if ($allowNull === true && $value === null) {
26+
return null;
27+
}
28+
29+
if ($value instanceof \Closure) {
30+
return $value;
31+
}
32+
33+
if (is_callable($value)) {
34+
return \Closure::fromCallable($value);
35+
}
36+
37+
throw new FilterException('Value "' . var_export($value, true) . '" is not Closure or $allowNull is not set');
38+
}
39+
}

src/Filterer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ final class Filterer implements FiltererInterface
2828
'arrayize' => '\\TraderInteractive\\Filter\\Arrays::arrayize',
2929
'bool' => '\\TraderInteractive\\Filter\\Booleans::filter',
3030
'bool-convert' => '\\TraderInteractive\\Filter\\Booleans::convert',
31+
'closure' => Filter\Closures::class . '::filter',
3132
'compress-string' => '\\TraderInteractive\\Filter\\Strings::compress',
3233
'concat' => '\\TraderInteractive\\Filter\\Strings::concat',
3334
'date' => '\\TraderInteractive\\Filter\\DateTime::filter',

tests/Filter/ClosuresTest.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
namespace TraderInteractive\Filter;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use TraderInteractive\Exceptions\FilterException;
7+
8+
/**
9+
* @coversDefaultClass \TraderInteractive\Filter\Closures
10+
* @covers ::<private>
11+
*/
12+
final class ClosuresTest extends TestCase
13+
{
14+
/**
15+
* @test
16+
* @covers ::filter
17+
*/
18+
public function filterAllowNullAndNullValue()
19+
{
20+
$result = Closures::filter(null, true);
21+
$this->assertSame(null, $result);
22+
}
23+
24+
/**
25+
* @test
26+
* @covers ::filter
27+
*/
28+
public function filterAllowNullIsFalseAndNullValue()
29+
{
30+
$this->expectException(FilterException::class);
31+
Closures::filter(null, false);
32+
}
33+
34+
/**
35+
* @test
36+
* @covers ::filter
37+
*/
38+
public function filterClosure()
39+
{
40+
$closureFunction = function () {
41+
return 'do nothing';
42+
};
43+
$result = Closures::filter($closureFunction);
44+
$this->assertTrue($result instanceof \Closure);
45+
}
46+
47+
public static function myCallableFunction()
48+
{
49+
return 'do nothing';
50+
}
51+
52+
/**
53+
* @test
54+
* @covers ::filter
55+
*/
56+
public function filterCallable()
57+
{
58+
$result = Closures::filter(['\\TraderInteractive\\Filter\\ClosuresTest', 'myCallableFunction']);
59+
$this->assertTrue($result instanceof \Closure);
60+
}
61+
62+
/**
63+
* @test
64+
* @covers ::filter
65+
*/
66+
public function filterNotCallableString()
67+
{
68+
$this->expectException(FilterException::class);
69+
Closures::filter('string');
70+
}
71+
72+
/**
73+
* @test
74+
* @covers ::filter
75+
*/
76+
public function filterNotCallableInt()
77+
{
78+
$this->expectException(FilterException::class);
79+
Closures::filter(123);
80+
}
81+
}

0 commit comments

Comments
 (0)