Skip to content

Commit 64f1bb5

Browse files
committed
handle instantiation protections
1 parent 4fc91b2 commit 64f1bb5

File tree

6 files changed

+220
-2
lines changed

6 files changed

+220
-2
lines changed

Zend/zend_API.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1816,6 +1816,27 @@ static zend_always_inline zend_result _object_and_properties_init(zval *arg, zen
18161816
return FAILURE;
18171817
}
18181818

1819+
if (class_type->required_scope) {
1820+
const zend_class_entry *scope = zend_get_executed_scope();
1821+
if (UNEXPECTED(scope == NULL)) {
1822+
zend_type_error("Cannot instantiate class %s from the global scope", ZSTR_VAL(class_type->name));
1823+
ZVAL_NULL(arg);
1824+
Z_OBJ_P(arg) = NULL;
1825+
return FAILURE;
1826+
}
1827+
1828+
if (class_type->required_scope_absolute) {
1829+
if (scope != class_type->required_scope && scope->lexical_scope != class_type->required_scope) {
1830+
zend_type_error("Cannot instantiate private class %s from scope %s", ZSTR_VAL(class_type->name), ZSTR_VAL(scope->name));
1831+
ZVAL_NULL(arg);
1832+
Z_OBJ_P(arg) = NULL;
1833+
return FAILURE;
1834+
}
1835+
} else if (!instanceof_function(scope, class_type->required_scope) && !instanceof_function(scope->lexical_scope, class_type->required_scope)) {
1836+
zend_type_error("Cannot instantiate protected class %s from scope %s", ZSTR_VAL(class_type->name), ZSTR_VAL(scope->name));
1837+
}
1838+
}
1839+
18191840
if (UNEXPECTED(!(class_type->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) {
18201841
if (UNEXPECTED(zend_update_class_constants(class_type) != SUCCESS)) {
18211842
ZVAL_NULL(arg);

Zend/zend_vm_def.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4427,6 +4427,24 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
44274427
}
44284428

44294429
SAVE_OPLINE();
4430+
4431+
if (Z_TYPE_P(retval_ptr) == IS_OBJECT && Z_OBJCE_P(retval_ptr)->required_scope) {
4432+
if (EX(func)->common.fn_flags & ZEND_ACC_PUBLIC) {
4433+
if (Z_OBJCE_P(retval_ptr)->required_scope_absolute) {
4434+
zend_type_error("Public method %s cannot return private class %s", ZSTR_VAL(EX(func)->common.function_name), ZSTR_VAL(Z_OBJCE_P(retval_ptr)->name));
4435+
HANDLE_EXCEPTION();
4436+
} else {
4437+
zend_type_error("Public method %s cannot return protected class %s", ZSTR_VAL(EX(func)->common.function_name), ZSTR_VAL(Z_OBJCE_P(retval_ptr)->name));
4438+
HANDLE_EXCEPTION();
4439+
}
4440+
} else if (EX(func)->common.fn_flags & ZEND_ACC_PROTECTED) {
4441+
if (Z_OBJCE_P(retval_ptr)->required_scope_absolute && Z_OBJCE_P(retval_ptr)->required_scope != EX(func)->common.scope) {
4442+
zend_type_error("Protected method %s cannot return private class %s", ZSTR_VAL(EX(func)->common.function_name), ZSTR_VAL(Z_OBJCE_P(retval_ptr)->name));
4443+
HANDLE_EXCEPTION();
4444+
}
4445+
}
4446+
}
4447+
44304448
if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) {
44314449
zend_verify_return_error(EX(func), retval_ptr);
44324450
HANDLE_EXCEPTION();

Zend/zend_vm_execute.h

Lines changed: 90 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/classes/inner_classes/autoload_002.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ var_dump($line);
1313
--EXPECTF--
1414
autoload(inner_classes\Line)
1515

16-
Fatal error: Uncaught Error: Cannot instantiate class inner_classes\Line from the global scope in %s:%d
16+
Fatal error: Uncaught TypeError: Cannot instantiate class inner_classes\Line from the global scope in %s:%d
1717
Stack trace:
1818
#0 {main}
1919
thrown in %s on line %d
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
--TEST--
2+
instantiation from various scopes
3+
--FILE--
4+
<?php
5+
6+
class Outer {
7+
private class PrivateInner {
8+
}
9+
protected class ProtectedInner {
10+
}
11+
public class PublicInner {
12+
}
13+
}
14+
15+
try {
16+
var_dump(new Outer\PrivateInner());
17+
} catch (Throwable $e) {
18+
echo "Failed to instantiate Outer\PrivateInner: {$e->getMessage()}\n";
19+
}
20+
21+
try {
22+
var_dump(new Outer\ProtectedInner());
23+
} catch (Throwable $e) {
24+
echo "Failed to instantiate Outer\ProtectedInner: {$e->getMessage()}\n";
25+
}
26+
27+
try {
28+
var_dump(new Outer\PublicInner());
29+
} catch (Throwable $e) {
30+
echo "Failed to instantiate Outer\PublicInner: {$e->getMessage()}\n";
31+
}
32+
33+
class Other {
34+
public function testPrivate() {
35+
var_dump(new Outer\PrivateInner());
36+
}
37+
public function testProtected() {
38+
var_dump(new Outer\ProtectedInner());
39+
}
40+
public function testPublic() {
41+
var_dump(new Outer\PublicInner());
42+
}
43+
}
44+
45+
$other = new Other();
46+
foreach (['Private', 'Protected', 'Public'] as $type) {
47+
try {
48+
$other->{"test$type"}();
49+
} catch(Throwable $e) {
50+
echo "Failed to instantiate Outer\\$type: {$e->getMessage()}\n";
51+
}
52+
}
53+
54+
class Child extends Outer {
55+
public function testPrivate() {
56+
var_dump(new Outer\PrivateInner());
57+
}
58+
public function testProtected() {
59+
var_dump(new Outer\ProtectedInner());
60+
}
61+
public function testPublic() {
62+
var_dump(new Outer\PublicInner());
63+
}
64+
}
65+
66+
$other = new Child();
67+
foreach (['Private', 'Protected', 'Public'] as $type) {
68+
try {
69+
$other->{"test$type"}();
70+
} catch(Throwable $e) {
71+
echo "Failed to instantiate Outer\\$type: {$e->getMessage()}\n";
72+
}
73+
}
74+
75+
?>
76+
--EXPECT--
77+
Failed to instantiate Outer\PrivateInner: Cannot instantiate class Outer\PrivateInner from the global scope
78+
Failed to instantiate Outer\ProtectedInner: Cannot instantiate class Outer\ProtectedInner from the global scope
79+
object(Outer\PublicInner)#1 (0) {
80+
}
81+
Failed to instantiate Outer\Private: Cannot instantiate private class Outer\PrivateInner from scope Other
82+
Failed to instantiate Outer\Protected: Cannot instantiate protected class Outer\ProtectedInner from scope Other
83+
object(Outer\PublicInner)#3 (0) {
84+
}
85+
Failed to instantiate Outer\Private: Cannot instantiate private class Outer\PrivateInner from scope Child
86+
object(Outer\ProtectedInner)#2 (0) {
87+
}
88+
object(Outer\PublicInner)#2 (0) {
89+
}

tests/classes/inner_classes/return_types_002.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ var_dump($outer->getInner());
2929
object(Outer\Inner)#2 (0) {
3030
}
3131

32-
Fatal error: Uncaught Error: Cannot instantiate private class Outer\Inner from Foo in %s:%d
32+
Fatal error: Uncaught TypeError: Cannot instantiate private class Outer\Inner from scope Foo in %s:%d
3333
Stack trace:
3434
#0 %s(%d): Foo->getInner()
3535
#1 {main}

0 commit comments

Comments
 (0)