Skip to content

Commit 7af0d0b

Browse files
committed
PoC with() / context managers
1 parent e5d0e62 commit 7af0d0b

23 files changed

+1333
-600
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
with() calls enter and exit
3+
--FILE--
4+
<?php
5+
6+
require 'basic_manager.inc';
7+
8+
with (new Manager() as $value) {
9+
echo "In with() block\n";
10+
var_dump($value);
11+
}
12+
13+
echo "After with() block\n";
14+
15+
?>
16+
--EXPECTF--
17+
Manager::enterContext()
18+
In with() block
19+
object(stdClass)#%d (0) {
20+
}
21+
Manager::exitContext(null)
22+
After with() block
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
class Manager implements ContextManager
4+
{
5+
public function __construct(private bool $rethrow = true)
6+
{
7+
}
8+
9+
public function enterContext(): mixed
10+
{
11+
echo __METHOD__, "()\n";
12+
return new stdClass;
13+
}
14+
15+
public function exitContext(?\Throwable $e = null): ?bool
16+
{
17+
echo __METHOD__, "(", ($e ? get_class($e) . '(' . $e->getMessage() . ')' : 'null'), ")\n";
18+
return !$this->rethrow;
19+
}
20+
}
21+
22+
?>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
with() does not rethrow exception when exitContext() returns true
3+
--FILE--
4+
<?php
5+
6+
require 'basic_manager.inc';
7+
8+
with (new Manager(false) as $value) {
9+
echo "In with() block\n";
10+
var_dump($value);
11+
throw new Exception('exception in with block');
12+
}
13+
14+
echo "After with() block\n";
15+
16+
?>
17+
--EXPECTF--
18+
Manager::enterContext()
19+
In with() block
20+
object(stdClass)#%d (0) {
21+
}
22+
Manager::exitContext(Exception(exception in with block))
23+
After with() block
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
Variable on the rhs of 'as' is optional
3+
--FILE--
4+
<?php
5+
6+
require 'basic_manager.inc';
7+
8+
with (new Manager()) {
9+
echo "In with() block\n";
10+
}
11+
12+
echo "After with() block\n";
13+
14+
?>
15+
--EXPECT--
16+
Manager::enterContext()
17+
In with() block
18+
Manager::exitContext(null)
19+
After with() block
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
with() calls exitContext($exception) on exception and re-throws by default
3+
--FILE--
4+
<?php
5+
6+
require 'basic_manager.inc';
7+
8+
try {
9+
with (new Manager() as $value) {
10+
echo "In with() block\n";
11+
var_dump($value);
12+
throw new Exception('exception in with block');
13+
}
14+
} catch (Exception $e) {
15+
echo $e::class, ": ", $e->getMessage(), "\n";
16+
}
17+
18+
echo "After with() block\n";
19+
20+
?>
21+
--EXPECTF--
22+
Manager::enterContext()
23+
In with() block
24+
object(stdClass)#%d (0) {
25+
}
26+
Manager::exitContext(Exception(exception in with block))
27+
Exception: exception in with block
28+
After with() block
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
with() handles Throwable
3+
--FILE--
4+
<?php
5+
6+
require 'basic_manager.inc';
7+
8+
try {
9+
with (new Manager() as $value) {
10+
echo "In with() block\n";
11+
var_dump($value);
12+
throw new Error('error in with block');
13+
}
14+
} catch (Error $e) {
15+
echo $e::class, ": ", $e->getMessage(), "\n";
16+
}
17+
18+
echo "After with() block\n";
19+
20+
?>
21+
--EXPECTF--
22+
Manager::enterContext()
23+
In with() block
24+
object(stdClass)#%d (0) {
25+
}
26+
Manager::exitContext(Error(error in with block))
27+
Error: error in with block
28+
After with() block

Zend/zend_ast.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,43 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_2(zend_ast_kind kind, zen
400400

401401
return ast;
402402
}
403+
404+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_3(zend_ast_kind kind, zend_ast *child1, zend_ast *child2, zend_ast *child3) {
405+
zend_ast *ast;
406+
zend_ast_list *list;
407+
uint32_t lineno;
408+
409+
ast = zend_ast_alloc(zend_ast_list_size(4));
410+
list = (zend_ast_list *) ast;
411+
list->kind = kind;
412+
list->attr = 0;
413+
list->children = 3;
414+
list->child[0] = child1;
415+
list->child[1] = child2;
416+
list->child[2] = child3;
417+
if (child1) {
418+
lineno = zend_ast_get_lineno(child1);
419+
if (lineno > CG(zend_lineno)) {
420+
lineno = CG(zend_lineno);
421+
}
422+
} else if (child2) {
423+
lineno = zend_ast_get_lineno(child2);
424+
if (lineno > CG(zend_lineno)) {
425+
lineno = CG(zend_lineno);
426+
}
427+
} else if (child3) {
428+
lineno = zend_ast_get_lineno(child3);
429+
if (lineno > CG(zend_lineno)) {
430+
lineno = CG(zend_lineno);
431+
}
432+
} else {
433+
list->children = 0;
434+
lineno = CG(zend_lineno);
435+
}
436+
list->lineno = lineno;
437+
438+
return ast;
439+
}
403440
#else
404441
static zend_ast *zend_ast_create_from_va_list(zend_ast_kind kind, zend_ast_attr attr, va_list va) {
405442
uint32_t i, children = kind >> ZEND_AST_NUM_CHILDREN_SHIFT;

Zend/zend_ast.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ enum _zend_ast_kind {
166166
ZEND_AST_PROP_GROUP,
167167
ZEND_AST_CONST_ELEM,
168168
ZEND_AST_CLASS_CONST_GROUP,
169+
ZEND_AST_WITH,
169170

170171
// Pseudo node for initializing enums
171172
ZEND_AST_CONST_ENUM_INIT,
@@ -306,6 +307,7 @@ static zend_always_inline zend_ast * zend_ast_create_ex_5(zend_ast_kind kind, ze
306307
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_0(zend_ast_kind kind);
307308
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_1(zend_ast_kind kind, zend_ast *child);
308309
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_2(zend_ast_kind kind, zend_ast *child1, zend_ast *child2);
310+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_3(zend_ast_kind kind, zend_ast *child1, zend_ast *child2, zend_ast *child3);
309311

310312
# define zend_ast_create(...) \
311313
ZEND_AST_SPEC_CALL(zend_ast_create, __VA_ARGS__)

Zend/zend_builtin_functions.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,13 @@
3434

3535
/* }}} */
3636

37+
ZEND_API zend_class_entry *zend_ce_context_manager = NULL;
38+
3739
ZEND_MINIT_FUNCTION(core) { /* {{{ */
3840
zend_register_default_classes();
3941

4042
zend_standard_class_def = register_class_stdClass();
43+
zend_ce_context_manager = register_class_ContextManager();
4144

4245
return SUCCESS;
4346
}

Zend/zend_builtin_functions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "zend_types.h"
2424

2525
typedef struct _zval_struct zval;
26+
extern ZEND_API zend_class_entry *zend_ce_context_manager;
2627

2728
zend_result zend_startup_builtin_functions(void);
2829

0 commit comments

Comments
 (0)