Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b0d95ab
Add namespace_name field to zend_op_array for visibility checks
withinboredom Nov 7, 2025
9de32b4
Define visibility flags for namespace-scoped members
withinboredom Nov 7, 2025
210d6e1
Implement namespace extraction utilities for visibility checks
withinboredom Nov 7, 2025
51415d9
Parse private(namespace) visibility modifier
withinboredom Nov 7, 2025
0f92c6a
Validate private(namespace) visibility modifier combinations
withinboredom Nov 7, 2025
21fd83c
Update tokenizer data for private(namespace) tokens
withinboredom Nov 7, 2025
22194b3
Prevent reducing visibility to private(namespace) in child classes
withinboredom Nov 7, 2025
4f70a41
Enforce namespace-scoped visibility for method calls
withinboredom Nov 7, 2025
50eec2e
Enforce namespace-scoped visibility for property access
withinboredom Nov 7, 2025
b8d8a49
Add Reflection support for namespace visibility
withinboredom Nov 7, 2025
e9e815b
Fix critical bug and implement property/method namespace visibility
withinboredom Nov 7, 2025
69ca92c
Add comprehensive test suite for private(namespace) visibility
withinboredom Nov 7, 2025
6ca49d4
fix memory leak in arena-alloc'd classes
withinboredom Nov 8, 2025
2247bec
Fix memory leak, callable validation, and edge cases for private(name…
withinboredom Nov 8, 2025
f478640
Fix heap-use-after-free in closure namespace_name handling
withinboredom Nov 8, 2025
63053a2
Fix namespace_name memory leaks in function copying/binding
withinboredom Nov 8, 2025
48bf396
Fix private(namespace) callable validation error messages
withinboredom Nov 8, 2025
7c17076
Fix private(namespace) visibility checks for callables and update tests
withinboredom Nov 8, 2025
5bc45dc
Add comprehensive property hooks tests for private(namespace)
withinboredom Nov 8, 2025
3ae5252
Fix memory leak in lexer token string allocation
withinboredom Nov 8, 2025
76003c4
Fix private(namespace) visibility in eval with explicit namespace
withinboredom Nov 8, 2025
b11fb62
Fix opcache memory leak for namespace_name in op_arrays
withinboredom Nov 8, 2025
f8e98aa
Fix memory leak in lexer by cleaning up tokens at restart label
withinboredom Nov 8, 2025
9b1fb32
Fix asymmetric visibility validation for private(namespace)
withinboredom Nov 9, 2025
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
27 changes: 27 additions & 0 deletions Zend/tests/access_modifiers/private_namespace_array_map_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
private(namespace) method with array_map - same namespace
--FILE--
<?php

namespace Foo;

class A {
private(namespace) static function double($x) {
return $x * 2;
}
}

// Same namespace - should work
$result = array_map([A::class, 'double'], [1, 2, 3]);
var_dump($result);

?>
--EXPECT--
array(3) {
[0]=>
int(2)
[1]=>
int(4)
[2]=>
int(6)
}
26 changes: 26 additions & 0 deletions Zend/tests/access_modifiers/private_namespace_array_map_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
private(namespace) method with array_map - different namespace
--FILE--
<?php

namespace Foo {
class A {
private(namespace) static function double($x) {
return $x * 2;
}
}
}

namespace Bar {
// Different namespace - should fail
$result = array_map(['\Foo\A', 'double'], [1, 2, 3]);
var_dump($result);
}

?>
--EXPECTF--
Fatal error: Uncaught TypeError: array_map(): Argument #1 ($callback) must be a valid callback or null, cannot access private(namespace) method Foo\A::double() in %s:%d
Stack trace:
#0 %s(%d): array_map(Array, Array)
#1 {main}
thrown in %s on line %d
24 changes: 24 additions & 0 deletions Zend/tests/access_modifiers/private_namespace_asymmetric_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
Asymmetric visibility: public private(namespace)(set) property - read from same namespace
--FILE--
<?php

namespace App {
class Config {
public private(namespace)(set) int $value = 100;
}

class ConfigManager {
public function getValue(): int {
$config = new Config();
return $config->value;
}
}

$manager = new ConfigManager();
echo $manager->getValue() . "\n";
}

?>
--EXPECT--
100
25 changes: 25 additions & 0 deletions Zend/tests/access_modifiers/private_namespace_asymmetric_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
Asymmetric visibility: public private(namespace)(set) property - write from same namespace
--FILE--
<?php

namespace App {
class Config {
public private(namespace)(set) int $value = 100;
}

class ConfigManager {
public function setValue(): void {
$config = new Config();
$config->value = 200;
echo $config->value . "\n";
}
}

$manager = new ConfigManager();
$manager->setValue();
}

?>
--EXPECT--
200
30 changes: 30 additions & 0 deletions Zend/tests/access_modifiers/private_namespace_asymmetric_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
--TEST--
Asymmetric visibility: public private(namespace)(set) property - write from different namespace fails
--FILE--
<?php

namespace App {
class Config {
public private(namespace)(set) int $value = 100;
}
}

namespace Other {
class Consumer {
public function tryWrite(): void {
$config = new \App\Config();
$config->value = 200;
}
}

$consumer = new Consumer();
$consumer->tryWrite();
}

?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot modify private(namespace)(set) property App\Config::$value from scope Other\Consumer in %s:%d
Stack trace:
#0 %s(%d): Other\Consumer->tryWrite()
#1 {main}
thrown in %s on line %d
26 changes: 26 additions & 0 deletions Zend/tests/access_modifiers/private_namespace_asymmetric_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
Asymmetric visibility: public private(namespace)(set) property - read from different namespace succeeds
--FILE--
<?php

namespace App {
class Config {
public private(namespace)(set) int $value = 100;
}
}

namespace Other {
class Consumer {
public function readValue(): int {
$config = new \App\Config();
return $config->value;
}
}

$consumer = new Consumer();
echo $consumer->readValue() . "\n";
}

?>
--EXPECT--
100
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Asymmetric visibility with incompatible modifiers (protected and private(namespace))
--FILE--
<?php

namespace Test;

// This should fail: protected and private(namespace) are incomparable
class A {
protected private(namespace)(set) string $prop1;
}

?>
--EXPECTF--
Fatal error: Property Test\A::$prop1 has incompatible visibility modifiers: protected and private(namespace) operate on different axes (inheritance vs namespace) and cannot be combined in asymmetric visibility in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Asymmetric visibility with incompatible modifiers (private(namespace) and protected(set))
--FILE--
<?php

namespace Test;

// This should fail: private(namespace) and protected(set) are incomparable
class A {
private(namespace) protected(set) string $prop1;
}

?>
--EXPECTF--
Fatal error: Property Test\A::$prop1 has incompatible visibility modifiers: protected and private(namespace) operate on different axes (inheritance vs namespace) and cannot be combined in asymmetric visibility in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Asymmetric visibility: private(namespace) private(set) is invalid (reversed hierarchy)
--FILE--
<?php

namespace Test;

// This should fail: C[private] ⊈ C[ns] (set is more restrictive than get)
class A {
private(namespace) private(set) string $prop1;
}

?>
--EXPECTF--
Fatal error: Visibility of property Test\A::$prop1 must not be weaker than set visibility in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
--TEST--
Valid asymmetric visibility combinations with private(namespace)
--FILE--
<?php

namespace Test;

class A {
// Valid: public base allows any set visibility
public private(namespace)(set) string $prop1 = "test1";
public protected(set) string $prop2 = "test2";
public private(set) string $prop3 = "test3";

// Valid: protected base allows protected(set) or private(set) (inheritance axis only)
protected protected(set) string $prop4 = "test4";
protected private(set) string $prop5 = "test5";

// Valid: private(namespace) base allows only private(namespace)(set) (namespace axis only)
private(namespace) private(namespace)(set) string $prop6 = "test6";

// Valid: private base allows only private(set)
private private(set) string $prop7 = "test7";

public function test() {
echo "prop1: " . $this->prop1 . "\n";
echo "prop2: " . $this->prop2 . "\n";
echo "prop3: " . $this->prop3 . "\n";
echo "prop4: " . $this->prop4 . "\n";
echo "prop5: " . $this->prop5 . "\n";
echo "prop6: " . $this->prop6 . "\n";
echo "prop7: " . $this->prop7 . "\n";
}
}

$a = new A();
$a->test();

echo "All valid combinations work correctly!\n";

?>
--EXPECT--
prop1: test1
prop2: test2
prop3: test3
prop4: test4
prop5: test5
prop6: test6
prop7: test7
All valid combinations work correctly!
23 changes: 23 additions & 0 deletions Zend/tests/access_modifiers/private_namespace_basic_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
private(namespace) method access from same namespace (same class)
--FILE--
<?php

namespace App\Services {
class UserService {
private(namespace) function validate(): string {
return "validated";
}

public function process(): string {
return $this->validate();
}
}

$service = new UserService();
echo $service->process() . "\n";
}

?>
--EXPECT--
validated
26 changes: 26 additions & 0 deletions Zend/tests/access_modifiers/private_namespace_basic_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
private(namespace) method access from same namespace (different class)
--FILE--
<?php

namespace App\Services {
class UserService {
private(namespace) function validate(): string {
return "validated";
}
}

class AuthService {
public function authenticate(): string {
$service = new UserService();
return $service->validate();
}
}

$auth = new AuthService();
echo $auth->authenticate() . "\n";
}

?>
--EXPECT--
validated
32 changes: 32 additions & 0 deletions Zend/tests/access_modifiers/private_namespace_basic_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
--TEST--
private(namespace) method access from different namespace fails
--FILE--
<?php

namespace App\Services {
class UserService {
private(namespace) function validate(): string {
return "validated";
}
}
}

namespace App\Controllers {
class UserController {
public function process(): void {
$service = new \App\Services\UserService();
$service->validate();
}
}

$controller = new UserController();
$controller->process();
}

?>
--EXPECTF--
Fatal error: Uncaught Error: Call to private(namespace) method App\Services\UserService::validate() from scope App\Controllers\UserController in %s:%d
Stack trace:
#0 %s(%d): App\Controllers\UserController->process()
#1 {main}
thrown in %s on line %d
21 changes: 21 additions & 0 deletions Zend/tests/access_modifiers/private_namespace_basic_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
private(namespace) property access from same namespace (same class)
--FILE--
<?php

namespace App\Models {
class User {
private(namespace) int $id = 42;

public function getId(): int {
return $this->id;
}
}

$user = new User();
echo $user->getId() . "\n";
}

?>
--EXPECT--
42
24 changes: 24 additions & 0 deletions Zend/tests/access_modifiers/private_namespace_basic_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
private(namespace) property access from same namespace (different class)
--FILE--
<?php

namespace App\Models {
class User {
private(namespace) int $id = 42;
}

class UserRepository {
public function getUserId(): int {
$user = new User();
return $user->id;
}
}

$repo = new UserRepository();
echo $repo->getUserId() . "\n";
}

?>
--EXPECT--
42
Loading
Loading