Skip to content

Commit

Permalink
Wrap pipe LHS in a QM_ASSIGN opcode to implicitly block references.
Browse files Browse the repository at this point in the history
  • Loading branch information
Crell committed Jan 14, 2025
1 parent 80ba4df commit c8c0365
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 32 deletions.
34 changes: 26 additions & 8 deletions Zend/tests/pipe_operator/call_by_ref.phpt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
--TEST--
Pipe operator accepts by-reference functions
Pipe operator rejects by-reference functions.
--FILE--
<?php

Expand All @@ -8,12 +8,30 @@ function _modify(int &$a): string {
return "foo";
}

$a = 5;
$res1 = $a |> '_modify';
function _append(array &$a): string {
$a['bar'] = 'beep';
}

// Simple variables
try {
$a = 5;
$res1 = $a |> _modify(...);
var_dump($res1);
} catch (\Error $e) {
print $e->getMessage() . PHP_EOL;
}

// Complex variables.
try {
$a = ['foo' => 'beep'];
$res2 = $a |> _append(...);
var_dump($res2);
} catch (\Error $e) {
print $e->getMessage() . PHP_EOL;
}


var_dump($res1);
var_dump($a);
?>
--EXPECT--
string(3) "foo"
int(6)
--EXPECTF--
_modify(): Argument #1 ($a) could not be passed by reference
_append(): Argument #1 ($a) could not be passed by reference
50 changes: 26 additions & 24 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -6412,6 +6412,32 @@ static bool can_match_use_jumptable(zend_ast_list *arms) {
return 1;
}

static void zend_compile_pipe(znode *result, zend_ast *ast)
{
zend_ast *operand_ast = ast->child[0];
zend_ast *callable_ast = ast->child[1];

znode operand_result;
zend_compile_expr(&operand_result, operand_ast);
znode wrapped_operand_result;
zend_emit_op_tmp(&wrapped_operand_result, ZEND_QM_ASSIGN, &operand_result, NULL);

/* Turn $foo |> bar(...) into bar($foo). */
if (callable_ast->kind == ZEND_AST_CALL
&& callable_ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT) {
callable_ast = callable_ast->child[0];
}

znode callable_result;
zend_compile_expr(&callable_result, callable_ast);

zend_ast *fcall_ast = zend_ast_create(ZEND_AST_CALL,
zend_ast_create_znode(&callable_result),
zend_ast_create_list(1, ZEND_AST_ARG_LIST, zend_ast_create_znode(&wrapped_operand_result)));

zend_compile_expr(result, fcall_ast);
}

static void zend_compile_match(znode *result, zend_ast *ast)
{
zend_ast *expr_ast = ast->child[0];
Expand Down Expand Up @@ -6598,30 +6624,6 @@ static void zend_compile_match(znode *result, zend_ast *ast)
efree(jmp_end_opnums);
}

static void zend_compile_pipe(znode *result, zend_ast *ast)
{
zend_ast *operand_ast = ast->child[0];
zend_ast *callable_ast = ast->child[1];

znode operand_result;
zend_compile_expr(&operand_result, operand_ast);

/* Turn $foo |> bar(...) into bar($foo). */
if (callable_ast->kind == ZEND_AST_CALL
&& callable_ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT) {
callable_ast = callable_ast->child[0];
}

znode callable_result;
zend_compile_expr(&callable_result, callable_ast);

zend_ast *fcall_ast = zend_ast_create(ZEND_AST_CALL,
zend_ast_create_znode(&callable_result),
zend_ast_create_list(1, ZEND_AST_ARG_LIST, zend_ast_create_znode(&operand_result)));

zend_compile_expr(result, fcall_ast);
}

static void zend_compile_try(zend_ast *ast) /* {{{ */
{
zend_ast *try_ast = ast->child[0];
Expand Down

0 comments on commit c8c0365

Please sign in to comment.