Skip to content

Commit 63ca65d

Browse files
committed
Add Closure::fromCallable().
Add the ability to create closures from callable as part of RFC: https://wiki.php.net/rfc/closurefromcallable
1 parent bc63879 commit 63ca65d

File tree

5 files changed

+659
-14
lines changed

5 files changed

+659
-14
lines changed
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
<?php
2+
3+
function bar($param1)
4+
{
5+
return $param1;
6+
}
7+
8+
9+
$closure = function($param1) {
10+
return $param1;
11+
};
12+
13+
function test($fn)
14+
{
15+
static $count = 0;
16+
$input = "foo".$count;
17+
$count++;
18+
19+
$output = $fn($input);
20+
return $input === $output;
21+
}
22+
23+
class Foo
24+
{
25+
public static function publicStaticFunction($param1)
26+
{
27+
return $param1;
28+
}
29+
30+
private static function privateStaticFunction($param1)
31+
{
32+
return $param1;
33+
}
34+
35+
protected static function protectedStaticFunction($param1)
36+
{
37+
return $param1;
38+
}
39+
40+
private function privateInstanceFunc($param1)
41+
{
42+
return $param1;
43+
}
44+
45+
protected function protectedInstanceFunc($param1)
46+
{
47+
return $param1;
48+
}
49+
50+
51+
public function publicInstanceFunc($param1)
52+
{
53+
return $param1;
54+
}
55+
56+
public function closePrivateValid()
57+
{
58+
return Closure::fromCallable([$this, 'privateInstanceFunc']);
59+
}
60+
61+
public function closePrivateStatic()
62+
{
63+
return Closure::fromCallable([__CLASS__, 'privateStaticFunction']);
64+
}
65+
66+
public function bar($param1)
67+
{
68+
echo "this is bar\n";
69+
}
70+
71+
public function getCallable()
72+
{
73+
return Closure::fromCallable([$this, 'publicInstanceFunc']);
74+
}
75+
76+
public function getSelfPublicInstance()
77+
{
78+
return Closure::fromCallable([$this, 'publicInstanceFunc']);
79+
}
80+
81+
public function getSelfColonPublicInstanceMethod()
82+
{
83+
return Closure::fromCallable('self::publicInstanceFunc');
84+
}
85+
}
86+
87+
88+
89+
class SubFoo extends Foo {
90+
91+
public function closePrivateStaticInvalid()
92+
{
93+
return Closure::fromCallable([__CLASS__, 'privateStaticFunction']);
94+
}
95+
96+
97+
public function closePrivateInvalid()
98+
{
99+
return Closure::fromCallable([$this, 'privateInstanceFunc']);
100+
}
101+
102+
public function closeProtectedStaticMethod()
103+
{
104+
return Closure::fromCallable([__CLASS__, 'protectedStaticFunction']);
105+
}
106+
107+
public function closeProtectedValid()
108+
{
109+
return Closure::fromCallable([$this, 'protectedInstanceFunc']);
110+
}
111+
112+
public function getParentPublicInstanceMethod()
113+
{
114+
return Closure::fromCallable('parent::publicInstanceFunc');
115+
}
116+
117+
public function getSelfColonParentPublicInstanceMethod()
118+
{
119+
return Closure::fromCallable('self::publicInstanceFunc');
120+
}
121+
122+
123+
public function getSelfColonParentProtectedInstanceMethod()
124+
{
125+
return Closure::fromCallable('self::protectedInstanceFunc');
126+
}
127+
128+
public function getSelfColonParentPrivateInstanceMethod()
129+
{
130+
return Closure::fromCallable('self::privateInstanceFunc');
131+
}
132+
}
133+
134+
135+
class MagicCall
136+
{
137+
public function __call($name, $arguments)
138+
{
139+
$info = ['__call'];
140+
$info[] = $name;
141+
$info = array_merge($info, $arguments);
142+
return implode(',', $info);
143+
}
144+
145+
public static function __callStatic($name, $arguments)
146+
{
147+
$info = ['__callStatic'];
148+
$info[] = $name;
149+
$info = array_merge($info, $arguments);
150+
return implode(',', $info);
151+
}
152+
}
153+
154+
155+
156+
class PublicInvokable
157+
{
158+
public function __invoke($param1)
159+
{
160+
return $param1;
161+
}
162+
}
163+
164+
165+
function functionAccessProtected()
166+
{
167+
$foo = new Foo;
168+
169+
return Closure::fromCallable([$foo, 'protectedStaticFunction']);
170+
}
171+
172+
function functionAccessPrivate()
173+
{
174+
$foo = new Foo;
175+
176+
return Closure::fromCallable([$foo, 'privateStaticFunction']);
177+
}
178+
179+
180+
function functionAccessMethodDoesntExist()
181+
{
182+
$foo = new Foo;
183+
184+
return Closure::fromCallable([$foo, 'thisDoesNotExist']);
185+
}
186+
187+
?>
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
--TEST--
2+
Testing closure() functionality
3+
--FILE--
4+
<?php
5+
6+
include('closureFunction.inc');
7+
8+
echo 'Access public static function';
9+
$fn = Closure::fromCallable(['Foo', 'publicStaticFunction']);
10+
echo $fn(" OK".PHP_EOL);
11+
12+
echo 'Access public static function with different case';
13+
$fn = Closure::fromCallable(['fOo', 'publicStaticfUNCTION']);
14+
echo $fn(" OK".PHP_EOL);
15+
16+
echo 'Access public static function with colon scheme';
17+
$fn = Closure::fromCallable('Foo::publicStaticFunction');
18+
echo $fn(" OK".PHP_EOL);
19+
20+
echo 'Access public instance method of object';
21+
$fn = Closure::fromCallable([new Foo, 'publicInstanceFunc']);
22+
echo $fn(" OK".PHP_EOL);
23+
24+
echo 'Access public instance method of parent object through parent:: ';
25+
$fn = Closure::fromCallable([new Foo, 'publicInstanceFunc']);
26+
echo $fn(" OK".PHP_EOL);
27+
28+
echo 'Function that exists';
29+
$fn = Closure::fromCallable('bar');
30+
echo $fn(" OK".PHP_EOL);
31+
32+
echo 'Function that exists with different spelling';
33+
$fn = Closure::fromCallable('BAR');
34+
echo $fn(" OK".PHP_EOL);
35+
36+
echo 'Closure is already a closure';
37+
$fn = Closure::fromCallable($closure);
38+
echo $fn(" OK".PHP_EOL);
39+
40+
echo 'Class with public invokable';
41+
$fn = Closure::fromCallable(new PublicInvokable);
42+
echo $fn(" OK".PHP_EOL);
43+
44+
echo "Instance return private method as callable";
45+
$foo = new Foo;
46+
$fn = $foo->closePrivateValid();
47+
echo $fn(" OK".PHP_EOL);
48+
49+
echo "Instance return private static method as callable";
50+
$foo = new Foo;
51+
$fn = $foo->closePrivateStatic();
52+
echo $fn(" OK".PHP_EOL);
53+
54+
echo 'Instance return protected static method as callable';
55+
$subFoo = new SubFoo;
56+
$fn = $subFoo->closeProtectedStaticMethod();
57+
echo $fn(" OK".PHP_EOL);
58+
59+
echo 'Subclass closure over parent class protected method';
60+
$subFoo = new SubFoo;
61+
$fn = $subFoo->closeProtectedValid();
62+
echo $fn(" OK".PHP_EOL);
63+
64+
echo 'Subclass closure over parent class static protected method';
65+
$subFoo = new SubFoo;
66+
$fn = $subFoo->closeProtectedStaticMethod();
67+
echo $fn(" OK".PHP_EOL);
68+
69+
echo 'Access public instance method of parent object through "parent::" ';
70+
$subFoo = new SubFoo;
71+
$fn = $subFoo->getParentPublicInstanceMethod();
72+
echo $fn(" OK".PHP_EOL);
73+
74+
echo 'Access public instance method of self object through "self::" ';
75+
$foo = new Foo;
76+
$fn = $foo->getSelfColonPublicInstanceMethod();
77+
echo $fn(" OK".PHP_EOL);
78+
79+
echo 'Access public instance method of parent object through "self::" to parent method';
80+
$foo = new SubFoo;
81+
$fn = $foo->getSelfColonParentPublicInstanceMethod();
82+
echo $fn(" OK".PHP_EOL);
83+
84+
echo 'Access proteced instance method of parent object through "self::" to parent method';
85+
$foo = new SubFoo;
86+
$fn = $foo->getSelfColonParentProtectedInstanceMethod();
87+
echo $fn(" OK".PHP_EOL);
88+
89+
echo 'MagicCall __call instance method ';
90+
$fn = Closure::fromCallable([new MagicCall, 'nonExistentMethod']);
91+
echo $fn(" OK".PHP_EOL);
92+
93+
echo 'MagicCall __callStatic static method ';
94+
$fn = Closure::fromCallable(['MagicCall', 'nonExistentMethod']);
95+
echo $fn(" OK".PHP_EOL);
96+
97+
98+
?>
99+
===DONE===
100+
--EXPECT--
101+
102+
Access public static function OK
103+
Access public static function with different case OK
104+
Access public static function with colon scheme OK
105+
Access public instance method of object OK
106+
Access public instance method of parent object through parent:: OK
107+
Function that exists OK
108+
Function that exists with different spelling OK
109+
Closure is already a closure OK
110+
Class with public invokable OK
111+
Instance return private method as callable OK
112+
Instance return private static method as callable OK
113+
Instance return protected static method as callable OK
114+
Subclass closure over parent class protected method OK
115+
Subclass closure over parent class static protected method OK
116+
Access public instance method of parent object through "parent::" OK
117+
Access public instance method of self object through "self::" OK
118+
Access public instance method of parent object through "self::" to parent method OK
119+
Access proteced instance method of parent object through "self::" to parent method OK
120+
MagicCall __call instance method __call,nonExistentMethod, OK
121+
MagicCall __callStatic static method __callStatic,nonExistentMethod, OK
122+
===DONE===

0 commit comments

Comments
 (0)