Skip to content

Commit f7b047b

Browse files
committed
cli: Automatically execute Closures returned from primary script
1 parent e79d7e5 commit f7b047b

6 files changed

Lines changed: 84 additions & 1 deletion

sapi/cli/php_cli.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "zend_hash.h"
2424
#include "zend_modules.h"
2525
#include "zend_interfaces.h"
26+
#include "zend_closures.h"
2627

2728
#include "ext/reflection/php_reflection.h"
2829

@@ -930,7 +931,21 @@ static int do_cli(int argc, char **argv) /* {{{ */
930931
if (interactive) {
931932
EG(exit_status) = cli_shell_callbacks.cli_shell_run();
932933
} else {
933-
php_execute_script(&file_handle);
934+
zval retval;
935+
ZVAL_UNDEF(&retval);
936+
php_execute_script_ex(&file_handle, &retval);
937+
938+
/* Check if the primary script returned a Closure and execute it: This allows
939+
* a script to act both as a library and as an executable when executed directly.
940+
*/
941+
if (Z_TYPE(retval) == IS_OBJECT && Z_OBJCE(retval) == zend_ce_closure) {
942+
zend_fcall_info_cache fcc = {0};
943+
Z_OBJ_HANDLER(retval, get_closure)(Z_OBJ(retval), &fcc.calling_scope, &fcc.function_handler, &fcc.object, /* check_only */ false);
944+
fcc.called_scope = fcc.calling_scope;
945+
fcc.closure = Z_OBJ(retval);
946+
zend_call_known_fcc(&fcc, /* retval */ NULL, /* param_count */ 0, /* params */ NULL, /* named_params */ NULL);
947+
}
948+
zval_ptr_dtor(&retval);
934949
}
935950
break;
936951
case PHP_CLI_MODE_LINT:
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
return function () {
4+
echo "Called", PHP_EOL;
5+
};
6+
7+
?>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
Execute closures returned by the primary script
3+
--SKIPIF--
4+
<?php
5+
include "skipif.inc";
6+
?>
7+
--FILE--
8+
<?php
9+
10+
return function () {
11+
echo "Called", PHP_EOL;
12+
};
13+
14+
?>
15+
--EXPECT--
16+
Called
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Execute closures returned by the primary script (First Class Callable)
3+
--SKIPIF--
4+
<?php
5+
include "skipif.inc";
6+
?>
7+
--FILE--
8+
<?php
9+
10+
function main() {
11+
echo "Called", PHP_EOL;
12+
}
13+
14+
return main(...);
15+
16+
?>
17+
--EXPECT--
18+
Called
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
Execute closures returned by the primary script (From different file)
3+
--SKIPIF--
4+
<?php
5+
include "skipif.inc";
6+
?>
7+
--FILE--
8+
<?php
9+
10+
return include "primary_script_closure.inc";
11+
12+
?>
13+
--EXPECT--
14+
Called
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
Execute closures returned by the primary script (Included file returns closure, but primary script does not)
3+
--SKIPIF--
4+
<?php
5+
include "skipif.inc";
6+
?>
7+
--FILE--
8+
<?php
9+
10+
$closure = include "primary_script_closure.inc";
11+
12+
?>
13+
--EXPECT--

0 commit comments

Comments
 (0)