Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Zend/tests/context_managers/basic.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
Context manager basic
--FILE--
<?php

function some_context() {
yield 'hello';
}

using (some_context() => $var) {
echo "Body is executed once\n";
echo "\$var: ";
var_dump($var);
};

echo "\$var is unset() after the block: ";
var_dump(!isset($var));

?>
--EXPECT--
Body is executed once
$var: string(5) "hello"
$var is unset() after the block: bool(true)
22 changes: 22 additions & 0 deletions Zend/tests/context_managers/basic_manager.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

class Manager implements ContextManager
{
public function __construct(private bool $rethrow = true)
{
}

public function enterContext(): mixed
{
echo __METHOD__, "()\n";
return new stdClass;
}

public function exitContext(?\Throwable $e = null): ?bool
{
echo __METHOD__, "(", ($e ? get_class($e) . '(' . $e->getMessage() . ')' : 'null'), ")\n";
return !$this->rethrow;
}
}

?>
16 changes: 16 additions & 0 deletions Zend/tests/context_managers/error_must_be_a_generator.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Context manager: manager must be a Generator
--FILE--
<?php

try {
using (new stdClass) {
echo "Not executed\n";
};
} catch (TypeError $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
}

?>
--EXPECT--
TypeError: Context Manager must be an instance of Generator (TODO: proper error message)
22 changes: 22 additions & 0 deletions Zend/tests/context_managers/error_must_not_yield_twice.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Context manager: manager must not yield twice
--FILE--
<?php

function invalid_manager() {
yield new stdClass;
yield new stdClass;
}

try {
using (invalid_manager() => $var) {
echo "Body executed\n";
};
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
}

?>
--EXPECT--
Body executed
Error: Context Manager must yield only once, but it yielded a second time when fetching the return value
22 changes: 22 additions & 0 deletions Zend/tests/context_managers/error_must_yield.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Context manager: manager must yield once
--FILE--
<?php

function invalid_manager() {
if (false) {
yield;
}
}

try {
using (invalid_manager() => $var) {
echo "Not executed\n";
};
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
}

?>
--EXPECT--
Error: Context Manager must yield once, but it returned without yielding
22 changes: 22 additions & 0 deletions Zend/tests/context_managers/evaluates_to_manager_return_value.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Context manager: manager must not yield twice
--FILE--
<?php

function some_context() {
yield 'hello';
return 'world';
}

$result = using (some_context() => $var) {
echo "Body executed: ";
var_dump($var);
};

echo "Result: ";
var_dump($result);

?>
--EXPECT--
Body executed: string(5) "hello"
Result: string(5) "world"
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
Context manager: exception is forwarded to manager
--FILE--
<?php

function some_context() {
try {
yield 'hello';
} catch (Exception $e) {
echo "From context manager: ", $e::class, ": ", $e->getMessage(), "\n";
}
}

using (some_context() => $var) {
throw new Exception('Thrown from body');
};

echo "\$var is unset() after the block: ";
var_dump(!isset($var));

?>
--EXPECT--
From context manager: Exception: Thrown from body
$var is unset() after the block: bool(true)
30 changes: 30 additions & 0 deletions Zend/tests/context_managers/exception_is_forwarded_to_manager.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
--TEST--
Context manager: exception is forwarded to manager
--FILE--
<?php

function some_context() {
try {
yield 'hello';
} catch (Exception $e) {
echo "From context manager: ", $e::class, ": ", $e->getMessage(), "\n";
throw $e;
}
}

try {
using (some_context() => $var) {
throw new Exception('Thrown from body');
};
} catch (Exception $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
}

echo "\$var is unset() after the block: ";
var_dump(!isset($var));

?>
--EXPECT--
From context manager: Exception: Thrown from body
Exception: Thrown from body
$var is unset() after the block: bool(true)
16 changes: 16 additions & 0 deletions Zend/tests/context_managers/var_is_optional.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Context manager: VAR is optional
--FILE--
<?php

function some_context() {
yield new stdClass;
}

using (some_context()) {
echo "Body is executed once\n";
};

?>
--EXPECT--
Body is executed once
6 changes: 6 additions & 0 deletions Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_znode(const znode *node) {
return (zend_ast *) ast;
}

ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_copy_znode(const znode *node) {
zend_ast *ast = zend_ast_create_znode(node);
ast->attr = 1;
return ast;
}

ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(zend_ast *args) {
zend_ast_fcc *ast;

Expand Down
3 changes: 3 additions & 0 deletions Zend/zend_ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ enum _zend_ast_kind {
ZEND_AST_ATTRIBUTE_GROUP,
ZEND_AST_MATCH_ARM_LIST,
ZEND_AST_MODIFIER_LIST,
ZEND_AST_USING_LIST,

/* 0 child nodes */
ZEND_AST_MAGIC_CONST = 0 << ZEND_AST_NUM_CHILDREN_SHIFT,
Expand Down Expand Up @@ -153,6 +154,8 @@ enum _zend_ast_kind {
ZEND_AST_MATCH_ARM,
ZEND_AST_NAMED_ARG,
ZEND_AST_PIPE,
ZEND_AST_USING,
ZEND_AST_USING_ELEM,

/* 3 child nodes */
ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT,
Expand Down
3 changes: 3 additions & 0 deletions Zend/zend_builtin_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "zend_autoload.h"
#include "zend_builtin_functions_arginfo.h"
#include "zend_smart_str.h"
#include "zend_context_managers.h"

/* }}} */

Expand All @@ -41,6 +42,8 @@ ZEND_MINIT_FUNCTION(core) { /* {{{ */

zend_standard_class_def = register_class_stdClass();

ZEND_MINIT(context_managers)(INIT_FUNC_ARGS_PASSTHRU);

return SUCCESS;
}
/* }}} */
Expand Down
Loading
Loading