23 changed files with 718 additions and 28 deletions
-
1NEWS
-
74Zend/tests/closure_005.phpt
-
38Zend/tests/closure_007.phpt
-
6Zend/tests/closure_020.phpt
-
26Zend/tests/closure_024.phpt
-
11Zend/tests/closure_026.phpt
-
33Zend/tests/closure_036.phpt
-
123Zend/zend_closures.c
-
3Zend/zend_closures.h
-
5Zend/zend_compile.c
-
2Zend/zend_compile.h
-
4Zend/zend_language_parser.y
-
2Zend/zend_vm_def.h
-
2Zend/zend_vm_execute.h
-
86ext/reflection/php_reflection.c
-
17ext/reflection/tests/ReflectionFunction_getClosureThis.phpt
-
37ext/reflection/tests/ReflectionFunction_getClosure_basic.phpt
-
27ext/reflection/tests/ReflectionFunction_getClosure_error.phpt
-
53ext/reflection/tests/ReflectionMethod_getClosureThis.phpt
-
55ext/reflection/tests/ReflectionMethod_getClosure_basic.phpt
-
73ext/reflection/tests/ReflectionMethod_getClosure_error.phpt
-
25ext/reflection/tests/closures_003_v1.phpt
-
43ext/reflection/tests/closures_004.phpt
@ -0,0 +1,74 @@ |
|||
--TEST-- |
|||
Closure 005: Lambda inside class, lifetime of $this |
|||
--FILE-- |
|||
<?php |
|||
|
|||
class A { |
|||
private $x; |
|||
|
|||
function __construct($x) { |
|||
$this->x = $x; |
|||
} |
|||
|
|||
function __destruct() { |
|||
echo "Destroyed\n"; |
|||
} |
|||
|
|||
function getIncer($val) { |
|||
return function() use ($val) { |
|||
$this->x += $val; |
|||
}; |
|||
} |
|||
|
|||
function getPrinter() { |
|||
return function() { |
|||
echo $this->x."\n"; |
|||
}; |
|||
} |
|||
|
|||
function getError() { |
|||
return static function() { |
|||
echo $this->x."\n"; |
|||
}; |
|||
} |
|||
|
|||
function printX() { |
|||
echo $this->x."\n"; |
|||
} |
|||
} |
|||
|
|||
$a = new A(3); |
|||
$incer = $a->getIncer(2); |
|||
$printer = $a->getPrinter(); |
|||
$error = $a->getError(); |
|||
|
|||
$a->printX(); |
|||
$printer(); |
|||
$incer(); |
|||
$a->printX(); |
|||
$printer(); |
|||
|
|||
unset($a); |
|||
|
|||
$incer(); |
|||
$printer(); |
|||
|
|||
unset($incer); |
|||
$printer(); |
|||
|
|||
unset($printer); |
|||
|
|||
$error(); |
|||
|
|||
echo "Done\n"; |
|||
?> |
|||
--EXPECTF-- |
|||
3 |
|||
3 |
|||
5 |
|||
5 |
|||
7 |
|||
7 |
|||
Destroyed |
|||
|
|||
Fatal error: Using $this when not in object context in %sclosure_005.php on line 28 |
|||
@ -0,0 +1,38 @@ |
|||
--TEST-- |
|||
Closure 007: Nested lambdas in classes |
|||
--FILE-- |
|||
<?php |
|||
|
|||
class A { |
|||
private $x = 0; |
|||
|
|||
function getClosureGetter () { |
|||
return function () { |
|||
return function () { |
|||
$this->x++; |
|||
}; |
|||
}; |
|||
} |
|||
|
|||
function printX () { |
|||
echo $this->x."\n"; |
|||
} |
|||
} |
|||
|
|||
$a = new A; |
|||
$a->printX(); |
|||
$getClosure = $a->getClosureGetter(); |
|||
$a->printX(); |
|||
$closure = $getClosure(); |
|||
$a->printX(); |
|||
$closure(); |
|||
$a->printX(); |
|||
|
|||
echo "Done\n"; |
|||
?> |
|||
--EXPECT-- |
|||
0 |
|||
0 |
|||
0 |
|||
1 |
|||
Done |
|||
@ -1,16 +1,26 @@ |
|||
--TEST-- |
|||
Closure 024: Trying to clone the Closure object |
|||
Closure 024: Clone the Closure object |
|||
--FILE-- |
|||
<?php |
|||
|
|||
$a = function () { |
|||
return clone function () { |
|||
return 1; |
|||
}; |
|||
}; |
|||
$a = 1; |
|||
$c = function($add) use(&$a) { return $a+$add; }; |
|||
|
|||
$a(); |
|||
$cc = clone $c; |
|||
|
|||
echo $c(10)."\n"; |
|||
echo $cc(10)."\n"; |
|||
|
|||
$a++; |
|||
|
|||
echo $c(10)."\n"; |
|||
echo $cc(10)."\n"; |
|||
|
|||
echo "Done.\n"; |
|||
?> |
|||
--EXPECTF-- |
|||
Fatal error: Trying to clone an uncloneable object of class Closure in %s on line %d |
|||
11 |
|||
11 |
|||
12 |
|||
12 |
|||
Done. |
|||
@ -0,0 +1,33 @@ |
|||
--TEST-- |
|||
Closure 036: Rebinding closures |
|||
--FILE-- |
|||
<?php |
|||
|
|||
class A { |
|||
private $x; |
|||
|
|||
public function __construct($v) { |
|||
$this->x = $v; |
|||
} |
|||
|
|||
public function getIncrementor() { |
|||
return function() { return ++$this->x; }; |
|||
} |
|||
} |
|||
|
|||
$a = new A(0); |
|||
$b = new A(10); |
|||
|
|||
$ca = $a->getIncrementor(); |
|||
$cb = $ca->bindTo($b); |
|||
$cb2 = Closure::bind($b, $ca); |
|||
|
|||
var_dump($ca()); |
|||
var_dump($cb()); |
|||
var_dump($cb2()); |
|||
|
|||
?> |
|||
--EXPECTF-- |
|||
int(1) |
|||
int(11) |
|||
int(12) |
|||
@ -0,0 +1,17 @@ |
|||
--TEST-- |
|||
Reflection::getClosureThis() |
|||
--SKIPIF-- |
|||
<?php |
|||
if (!extension_loaded('reflection') || !defined('PHP_VERSION_ID') || PHP_VERSION_ID < 50300) { |
|||
print 'skip'; |
|||
} |
|||
?> |
|||
--FILE-- |
|||
<?php |
|||
$closure = function($param) { return "this is a closure"; }; |
|||
$rf = new ReflectionFunction($closure); |
|||
var_dump($rf->getClosureThis()); |
|||
echo "Done!\n"; |
|||
--EXPECTF-- |
|||
NULL |
|||
Done! |
|||
@ -0,0 +1,37 @@ |
|||
--TEST-- |
|||
Test ReflectionFunction::getClosure() function : basic functionality |
|||
--FILE-- |
|||
<?php |
|||
/* Prototype : public mixed ReflectionFunction::getClosure() |
|||
* Description: Returns a dynamically created closure for the function |
|||
* Source code: ext/reflection/php_reflection.c |
|||
* Alias to functions: |
|||
*/ |
|||
|
|||
echo "*** Testing ReflectionFunction::getClosure() : basic functionality ***\n"; |
|||
|
|||
function foo() |
|||
{ |
|||
var_dump( "Inside foo function" ); |
|||
} |
|||
|
|||
function bar( $arg ) |
|||
{ |
|||
var_dump( "Arg is " . $arg ); |
|||
} |
|||
|
|||
$func = new ReflectionFunction( 'foo' ); |
|||
$closure = $func->getClosure(); |
|||
$closure(); |
|||
|
|||
$func = new ReflectionFunction( 'bar' ); |
|||
$closure = $func->getClosure(); |
|||
$closure( 'succeeded' ); |
|||
|
|||
?> |
|||
===DONE=== |
|||
--EXPECTF-- |
|||
*** Testing ReflectionFunction::getClosure() : basic functionality *** |
|||
string(19) "Inside foo function" |
|||
string(16) "Arg is succeeded" |
|||
===DONE=== |
|||
@ -0,0 +1,27 @@ |
|||
--TEST-- |
|||
Test ReflectionFunction::getClosure() function : error functionality |
|||
--FILE-- |
|||
<?php |
|||
/* Prototype : public mixed ReflectionFunction::getClosure() |
|||
* Description: Returns a dynamically created closure for the function |
|||
* Source code: ext/reflection/php_reflection.c |
|||
* Alias to functions: |
|||
*/ |
|||
|
|||
echo "*** Testing ReflectionFunction::getClosure() : error conditions ***\n"; |
|||
|
|||
function foo() |
|||
{ |
|||
var_dump( "Inside foo function" ); |
|||
} |
|||
|
|||
$func = new ReflectionFunction( 'foo' ); |
|||
$closure = $func->getClosure('bar'); |
|||
|
|||
?> |
|||
===DONE=== |
|||
--EXPECTF-- |
|||
*** Testing ReflectionFunction::getClosure() : error conditions *** |
|||
|
|||
Warning: ReflectionFunction::getClosure() expects exactly 0 parameters, 1 given in %s on line %d |
|||
===DONE=== |
|||
@ -0,0 +1,53 @@ |
|||
--TEST-- |
|||
Reflection::getClosureThis() |
|||
--SKIPIF-- |
|||
<?php |
|||
if (!extension_loaded('reflection') || !defined('PHP_VERSION_ID') || PHP_VERSION_ID < 50300) { |
|||
print 'skip'; |
|||
} |
|||
?> |
|||
--FILE-- |
|||
<?php |
|||
class StaticExample |
|||
{ |
|||
static function foo() |
|||
{ |
|||
var_dump( "Static Example class, Hello World!" ); |
|||
} |
|||
} |
|||
|
|||
class Example |
|||
{ |
|||
public $bar = 42; |
|||
public function foo() |
|||
{ |
|||
var_dump( "Example class, bar: " . $this->bar ); |
|||
} |
|||
} |
|||
|
|||
// Initialize classes |
|||
$class = new ReflectionClass( 'Example' ); |
|||
$staticclass = new ReflectionClass( 'StaticExample' ); |
|||
$object = new Example(); |
|||
|
|||
$method = $staticclass->getMethod( 'foo' ); |
|||
$closure = $method->getClosure(); |
|||
$rf = new ReflectionFunction($closure); |
|||
|
|||
var_dump($rf->getClosureThis()); |
|||
|
|||
$method = $class->getMethod( 'foo' ); |
|||
|
|||
$closure = $method->getClosure( $object ); |
|||
$rf = new ReflectionFunction($closure); |
|||
|
|||
var_dump($rf->getClosureThis()); |
|||
|
|||
echo "Done!\n"; |
|||
--EXPECTF-- |
|||
NULL |
|||
object(Example)#%d (1) { |
|||
["bar"]=> |
|||
int(42) |
|||
} |
|||
Done! |
|||
@ -0,0 +1,55 @@ |
|||
--TEST-- |
|||
Test ReflectionMethod::getClosure() function : basic functionality |
|||
--FILE-- |
|||
<?php |
|||
/* Prototype : public mixed ReflectionFunction::getClosure() |
|||
* Description: Returns a dynamically created closure for the method |
|||
* Source code: ext/reflection/php_reflection.c |
|||
* Alias to functions: |
|||
*/ |
|||
|
|||
echo "*** Testing ReflectionMethod::getClosure() : basic functionality ***\n"; |
|||
|
|||
class StaticExample |
|||
{ |
|||
static function foo() |
|||
{ |
|||
var_dump( "Static Example class, Hello World!" ); |
|||
} |
|||
} |
|||
|
|||
class Example |
|||
{ |
|||
public $bar = 42; |
|||
public function foo() |
|||
{ |
|||
var_dump( "Example class, bar: " . $this->bar ); |
|||
} |
|||
} |
|||
|
|||
// Initialize classes |
|||
$class = new ReflectionClass( 'Example' ); |
|||
$staticclass = new ReflectionClass( 'StaticExample' ); |
|||
$object = new Example(); |
|||
$fakeobj = new StdClass(); |
|||
|
|||
|
|||
$method = $staticclass->getMethod( 'foo' ); |
|||
$closure = $method->getClosure(); |
|||
$closure(); |
|||
|
|||
$method = $class->getMethod( 'foo' ); |
|||
|
|||
$closure = $method->getClosure( $object ); |
|||
$closure(); |
|||
$object->bar = 34; |
|||
$closure(); |
|||
|
|||
?> |
|||
===DONE=== |
|||
--EXPECTF-- |
|||
*** Testing ReflectionMethod::getClosure() : basic functionality *** |
|||
string(34) "Static Example class, Hello World!" |
|||
string(22) "Example class, bar: 42" |
|||
string(22) "Example class, bar: 34" |
|||
===DONE=== |
|||
@ -0,0 +1,73 @@ |
|||
--TEST-- |
|||
Test ReflectionMethod::getClosure() function : error functionality |
|||
--FILE-- |
|||
<?php |
|||
/* Prototype : public mixed ReflectionFunction::getClosure() |
|||
* Description: Returns a dynamically created closure for the method |
|||
* Source code: ext/reflection/php_reflection.c |
|||
* Alias to functions: |
|||
*/ |
|||
|
|||
echo "*** Testing ReflectionMethod::getClosure() : error conditions ***\n"; |
|||
|
|||
class StaticExample |
|||
{ |
|||
static function foo() |
|||
{ |
|||
var_dump( "Static Example class, Hello World!" ); |
|||
} |
|||
} |
|||
|
|||
class Example |
|||
{ |
|||
public $bar = 42; |
|||
public function foo() |
|||
{ |
|||
var_dump( "Example class, bar: " . $this->bar ); |
|||
} |
|||
} |
|||
|
|||
// Initialize classes |
|||
$class = new ReflectionClass( 'Example' ); |
|||
$staticclass = new ReflectionClass( 'StaticExample' ); |
|||
$method = $class->getMethod( 'foo' ); |
|||
$staticmethod = $staticclass->getMethod( 'foo' ); |
|||
$object = new Example(); |
|||
$fakeobj = new StdClass(); |
|||
|
|||
echo "\n-- Testing ReflectionMethod::getClosure() function with more than expected no. of arguments --\n"; |
|||
var_dump( $staticmethod->getClosure( 'foobar' ) ); |
|||
var_dump( $staticmethod->getClosure( 'foo', 'bar' ) ); |
|||
var_dump( $method->getClosure( $object, 'foobar' ) ); |
|||
|
|||
echo "\n-- Testing ReflectionMethod::getClosure() function with Zero arguments --\n"; |
|||
$closure = $method->getClosure(); |
|||
|
|||
echo "\n-- Testing ReflectionMethod::getClosure() function with Zero arguments --\n"; |
|||
try { |
|||
var_dump( $method->getClosure( $fakeobj ) ); |
|||
} catch( Exception $e ) { |
|||
var_dump( $e->getMessage() ); |
|||
} |
|||
|
|||
?> |
|||
===DONE=== |
|||
--EXPECTF-- |
|||
*** Testing ReflectionMethod::getClosure() : error conditions *** |
|||
|
|||
-- Testing ReflectionMethod::getClosure() function with more than expected no. of arguments -- |
|||
object(Closure)#%d (0) { |
|||
} |
|||
object(Closure)#%d (0) { |
|||
} |
|||
|
|||
Warning: ReflectionMethod::getClosure() expects exactly 1 parameter, 2 given in %s on line %d |
|||
NULL |
|||
|
|||
-- Testing ReflectionMethod::getClosure() function with Zero arguments -- |
|||
|
|||
Warning: ReflectionMethod::getClosure() expects exactly 1 parameter, 0 given in %s on line %d |
|||
|
|||
-- Testing ReflectionMethod::getClosure() function with Zero arguments -- |
|||
string(72) "Given object is not an instance of the class this method was declared in" |
|||
===DONE=== |
|||
@ -0,0 +1,25 @@ |
|||
--TEST-- |
|||
Reflection on closures: Segfaults with getParameters() and getDeclaringFunction() |
|||
--FILE-- |
|||
<?php |
|||
|
|||
$closure = function($a, $b = 0) { }; |
|||
|
|||
$method = new ReflectionFunction ($closure); |
|||
$params = $method->getParameters (); |
|||
unset ($method); |
|||
$method = $params[0]->getDeclaringFunction (); |
|||
unset ($params); |
|||
echo $method->getName ()."\n"; |
|||
|
|||
$parameter = new ReflectionParameter ($closure, 'b'); |
|||
$method = $parameter->getDeclaringFunction (); |
|||
unset ($parameter); |
|||
echo $method->getName ()."\n"; |
|||
|
|||
?> |
|||
===DONE=== |
|||
--EXPECTF-- |
|||
{closure} |
|||
{closure} |
|||
===DONE=== |
|||
@ -0,0 +1,43 @@ |
|||
--TEST-- |
|||
Reflection on closures: Segfault with getClosure() on closure itself |
|||
--FILE-- |
|||
<?php |
|||
$closure = function() { echo "Invoked!\n"; }; |
|||
|
|||
$method = new ReflectionFunction ($closure); |
|||
|
|||
$closure2 = $method->getClosure (); |
|||
|
|||
$closure2 (); |
|||
$closure2->__invoke (); |
|||
|
|||
unset ($closure); |
|||
|
|||
$closure2 (); |
|||
$closure2->__invoke (); |
|||
|
|||
$closure = function() { echo "Invoked!\n"; }; |
|||
|
|||
$method = new ReflectionMethod ($closure, '__invoke'); |
|||
$closure2 = $method->getClosure ($closure); |
|||
|
|||
$closure2 (); |
|||
$closure2->__invoke (); |
|||
|
|||
unset ($closure); |
|||
|
|||
$closure2 (); |
|||
$closure2->__invoke (); |
|||
|
|||
?> |
|||
===DONE=== |
|||
--EXPECTF-- |
|||
Invoked! |
|||
Invoked! |
|||
Invoked! |
|||
Invoked! |
|||
Invoked! |
|||
Invoked! |
|||
Invoked! |
|||
Invoked! |
|||
===DONE=== |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue