Closed
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
3 changes: 3 additions & 0 deletions Zend/tests/grammar/semi_reserved_001.phpt
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,6 +45,7 @@ class Obj
function array(){ echo __METHOD__, PHP_EOL; }
function print(){ echo __METHOD__, PHP_EOL; }
function echo(){ echo __METHOD__, PHP_EOL; }
function readonly(){ echo __METHOD__, PHP_EOL; }
function require(){ echo __METHOD__, PHP_EOL; }
function require_once(){ echo __METHOD__, PHP_EOL; }
function return(){ echo __METHOD__, PHP_EOL; }
Expand DownExpand Up@@ -125,6 +126,7 @@ $obj->throw();
$obj->array();
$obj->print();
$obj->echo();
$obj->readonly();
$obj->require();
$obj->require_once();
$obj->return();
Expand DownExpand Up@@ -205,6 +207,7 @@ Obj::throw
Obj::array
Obj::print
Obj::echo
Obj::readonly
Obj::require
Obj::require_once
Obj::return
Expand Down
29 changes: 29 additions & 0 deletions Zend/tests/readonly_props/by_ref_foreach.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
--TEST--
By-ref foreach over readonly property
--FILE--
<?php

class Test {
public readonly int $prop;

public function init() {
$this->prop = 1;
}
}

$test = new Test;

// Okay, as foreach skips over uninitialized properties.
foreach ($test as &$prop) {}

$test->init();

try {
foreach ($test as &$prop) {}
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot acquire reference to readonly property Test::$prop
122 changes: 122 additions & 0 deletions Zend/tests/readonly_props/cache_slot.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
--TEST--
Test interaction with cache slots
--FILE--
<?php

class Test {
public readonly string $prop;
public readonly array $prop2;
public readonly object $prop3;
public function setProp(string $prop) {
$this->prop = $prop;
}
public function initAndAppendProp2() {
$this->prop2 = [];
$this->prop2[] = 1;
}
public function initProp3() {
$this->prop3 = new stdClass;
$this->prop3->foo = 1;
}
public function replaceProp3() {
$ref =& $this->prop3;
$ref = new stdClass;
}
}

$test = new Test;
$test->setProp("a");
var_dump($test->prop);
try {
$test->setProp("b");
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop);
echo "\n";

$test = new Test;
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$test->initProp3();
$test->replaceProp3();
var_dump($test->prop3);
$test->replaceProp3();
var_dump($test->prop3);
echo "\n";

// Test variations using closure rebinding, so we have unknown property_info in JIT.
$test = new Test;
(function() { $this->prop2 = []; })->call($test);
$appendProp2 = (function() {
$this->prop2[] = 1;
})->bindTo($test, Test::class);
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$replaceProp3 = (function() {
$ref =& $this->prop3;
$ref = new stdClass;
})->bindTo($test, Test::class);
$test->initProp3();
$replaceProp3();
var_dump($test->prop3);
$replaceProp3();
var_dump($test->prop3);

?>
--EXPECT--
string(1) "a"
Cannot modify readonly property Test::$prop
string(1) "a"

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#3 (1) {
["foo"]=>
int(1)
}
object(stdClass)#3 (1) {
["foo"]=>
int(1)
}

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
72 changes: 72 additions & 0 deletions Zend/tests/readonly_props/initialization_scope.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
--TEST--
Initialization can only happen from private scope
--FILE--
<?php

class A {
public readonly int $prop;

public function initPrivate() {
$this->prop = 3;
}
}
class B extends A {
public function initProtected() {
$this->prop = 2;
}
}

$test = new B;
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initProtected();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->initPrivate();
var_dump($test->prop);

// Rebinding bypass works.
$test = new B;
(function() {
$this->prop = 1;
})->bindTo($test, A::class)();
var_dump($test->prop);

class C extends A {
public readonly int $prop;
}

$test = new C;
$test->initPrivate();
var_dump($test->prop);

class X {
public function initFromParent() {
$this->prop = 1;
}
}
class Y extends X {
public readonly int $prop;
}

$test = new Y;
try {
$test->initFromParent();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot initialize readonly property A::$prop from global scope
Cannot initialize readonly property A::$prop from scope B
int(3)
int(1)
int(3)
Cannot initialize readonly property Y::$prop from scope X
74 changes: 74 additions & 0 deletions Zend/tests/readonly_props/magic_get_set.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
--TEST--
Interaction with magic get/set
--FILE--
<?php

class Test {
public readonly int $prop;

public function unsetProp() {
unset($this->prop);
}

public function __get($name) {
echo __METHOD__, "($name)\n";
return 1;
}

public function __set($name, $value) {
echo __METHOD__, "($name, $value)\n";
}

public function __unset($name) {
echo __METHOD__, "($name)\n";
}

public function __isset($name) {
echo __METHOD__, "($name)\n";
return true;
}
}

$test = new Test;

// The property is in uninitialized state, no magic methods should be invoked.
var_dump(isset($test->prop));
try {
var_dump($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->unsetProp();

var_dump(isset($test->prop));
var_dump($test->prop);
$test->prop = 2;
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
bool(false)
Typed property Test::$prop must not be accessed before initialization
Cannot initialize readonly property Test::$prop from global scope
Cannot unset readonly property Test::$prop from global scope
Test::__isset(prop)
bool(true)
Test::__get(prop)
int(1)
Test::__set(prop, 2)
Test::__unset(prop)
26 changes: 26 additions & 0 deletions Zend/tests/readonly_props/override_with_attributes.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
--TEST--
Can override readonly property with attributes
--FILE--
<?php

#[Attribute]
class FooAttribute {}

class A {
public readonly int $prop;

public function __construct() {
$this->prop = 42;
}
}
class B extends A {
#[FooAttribute]
public readonly int $prop;
}

var_dump((new ReflectionProperty(B::class, 'prop'))->getAttributes()[0]->newInstance());

?>
--EXPECT--
object(FooAttribute)#1 (0) {
}
41 changes: 41 additions & 0 deletions Zend/tests/readonly_props/promotion.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
--TEST--
Promoted readonly property
--FILE--
<?php

class Point {
public function __construct(
public readonly float $x = 0.0,
public readonly float $y = 0.0,
public readonly float $z = 0.0,
) {}
}

var_dump(new Point);
$point = new Point(1.0, 2.0, 3.0);
try {
$point->x = 4.0;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($point);

?>
--EXPECT--
object(Point)#1 (3) {
["x"]=>
float(0)
["y"]=>
float(0)
["z"]=>
float(0)
}
Cannot modify readonly property Point::$x
object(Point)#1 (3) {
["x"]=>
float(1)
["y"]=>
float(2)
["z"]=>
float(3)
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Closed
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
3 changes: 3 additions & 0 deletions Zend/tests/grammar/semi_reserved_001.phpt
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,6 +45,7 @@ class Obj
function array(){ echo __METHOD__, PHP_EOL; }
function print(){ echo __METHOD__, PHP_EOL; }
function echo(){ echo __METHOD__, PHP_EOL; }
function readonly(){ echo __METHOD__, PHP_EOL; }
function require(){ echo __METHOD__, PHP_EOL; }
function require_once(){ echo __METHOD__, PHP_EOL; }
function return(){ echo __METHOD__, PHP_EOL; }
Expand DownExpand Up@@ -125,6 +126,7 @@ $obj->throw();
$obj->array();
$obj->print();
$obj->echo();
$obj->readonly();
$obj->require();
$obj->require_once();
$obj->return();
Expand DownExpand Up@@ -205,6 +207,7 @@ Obj::throw
Obj::array
Obj::print
Obj::echo
Obj::readonly
Obj::require
Obj::require_once
Obj::return
Expand Down
29 changes: 29 additions & 0 deletions Zend/tests/readonly_props/by_ref_foreach.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
--TEST--
By-ref foreach over readonly property
--FILE--
<?php

class Test {
public readonly int $prop;

public function init() {
$this->prop = 1;
}
}

$test = new Test;

// Okay, as foreach skips over uninitialized properties.
foreach ($test as &$prop) {}

$test->init();

try {
foreach ($test as &$prop) {}
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot acquire reference to readonly property Test::$prop
122 changes: 122 additions & 0 deletions Zend/tests/readonly_props/cache_slot.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
--TEST--
Test interaction with cache slots
--FILE--
<?php

class Test {
public readonly string $prop;
public readonly array $prop2;
public readonly object $prop3;
public function setProp(string $prop) {
$this->prop = $prop;
}
public function initAndAppendProp2() {
$this->prop2 = [];
$this->prop2[] = 1;
}
public function initProp3() {
$this->prop3 = new stdClass;
$this->prop3->foo = 1;
}
public function replaceProp3() {
$ref =& $this->prop3;
$ref = new stdClass;
}
}

$test = new Test;
$test->setProp("a");
var_dump($test->prop);
try {
$test->setProp("b");
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop);
echo "\n";

$test = new Test;
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$test->initProp3();
$test->replaceProp3();
var_dump($test->prop3);
$test->replaceProp3();
var_dump($test->prop3);
echo "\n";

// Test variations using closure rebinding, so we have unknown property_info in JIT.
$test = new Test;
(function() { $this->prop2 = []; })->call($test);
$appendProp2 = (function() {
$this->prop2[] = 1;
})->bindTo($test, Test::class);
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$replaceProp3 = (function() {
$ref =& $this->prop3;
$ref = new stdClass;
})->bindTo($test, Test::class);
$test->initProp3();
$replaceProp3();
var_dump($test->prop3);
$replaceProp3();
var_dump($test->prop3);

?>
--EXPECT--
string(1) "a"
Cannot modify readonly property Test::$prop
string(1) "a"

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#3 (1) {
["foo"]=>
int(1)
}
object(stdClass)#3 (1) {
["foo"]=>
int(1)
}

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
72 changes: 72 additions & 0 deletions Zend/tests/readonly_props/initialization_scope.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
--TEST--
Initialization can only happen from private scope
--FILE--
<?php

class A {
public readonly int $prop;

public function initPrivate() {
$this->prop = 3;
}
}
class B extends A {
public function initProtected() {
$this->prop = 2;
}
}

$test = new B;
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initProtected();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->initPrivate();
var_dump($test->prop);

// Rebinding bypass works.
$test = new B;
(function() {
$this->prop = 1;
})->bindTo($test, A::class)();
var_dump($test->prop);

class C extends A {
public readonly int $prop;
}

$test = new C;
$test->initPrivate();
var_dump($test->prop);

class X {
public function initFromParent() {
$this->prop = 1;
}
}
class Y extends X {
public readonly int $prop;
}

$test = new Y;
try {
$test->initFromParent();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot initialize readonly property A::$prop from global scope
Cannot initialize readonly property A::$prop from scope B
int(3)
int(1)
int(3)
Cannot initialize readonly property Y::$prop from scope X
74 changes: 74 additions & 0 deletions Zend/tests/readonly_props/magic_get_set.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
--TEST--
Interaction with magic get/set
--FILE--
<?php

class Test {
public readonly int $prop;

public function unsetProp() {
unset($this->prop);
}

public function __get($name) {
echo __METHOD__, "($name)\n";
return 1;
}

public function __set($name, $value) {
echo __METHOD__, "($name, $value)\n";
}

public function __unset($name) {
echo __METHOD__, "($name)\n";
}

public function __isset($name) {
echo __METHOD__, "($name)\n";
return true;
}
}

$test = new Test;

// The property is in uninitialized state, no magic methods should be invoked.
var_dump(isset($test->prop));
try {
var_dump($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->unsetProp();

var_dump(isset($test->prop));
var_dump($test->prop);
$test->prop = 2;
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
bool(false)
Typed property Test::$prop must not be accessed before initialization
Cannot initialize readonly property Test::$prop from global scope
Cannot unset readonly property Test::$prop from global scope
Test::__isset(prop)
bool(true)
Test::__get(prop)
int(1)
Test::__set(prop, 2)
Test::__unset(prop)
26 changes: 26 additions & 0 deletions Zend/tests/readonly_props/override_with_attributes.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
--TEST--
Can override readonly property with attributes
--FILE--
<?php

#[Attribute]
class FooAttribute {}

class A {
public readonly int $prop;

public function __construct() {
$this->prop = 42;
}
}
class B extends A {
#[FooAttribute]
public readonly int $prop;
}

var_dump((new ReflectionProperty(B::class, 'prop'))->getAttributes()[0]->newInstance());

?>
--EXPECT--
object(FooAttribute)#1 (0) {
}
41 changes: 41 additions & 0 deletions Zend/tests/readonly_props/promotion.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
--TEST--
Promoted readonly property
--FILE--
<?php

class Point {
public function __construct(
public readonly float $x = 0.0,
public readonly float $y = 0.0,
public readonly float $z = 0.0,
) {}
}

var_dump(new Point);
$point = new Point(1.0, 2.0, 3.0);
try {
$point->x = 4.0;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($point);

?>
--EXPECT--
object(Point)#1 (3) {
["x"]=>
float(0)
["y"]=>
float(0)
["z"]=>
float(0)
}
Cannot modify readonly property Point::$x
object(Point)#1 (3) {
["x"]=>
float(1)
["y"]=>
float(2)
["z"]=>
float(3)
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
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
3 changes: 3 additions & 0 deletions Zend/tests/grammar/semi_reserved_001.phpt
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,6 +45,7 @@ class Obj
function array(){ echo __METHOD__, PHP_EOL; }
function print(){ echo __METHOD__, PHP_EOL; }
function echo(){ echo __METHOD__, PHP_EOL; }
function readonly(){ echo __METHOD__, PHP_EOL; }
function require(){ echo __METHOD__, PHP_EOL; }
function require_once(){ echo __METHOD__, PHP_EOL; }
function return(){ echo __METHOD__, PHP_EOL; }
Expand DownExpand Up@@ -125,6 +126,7 @@ $obj->throw();
$obj->array();
$obj->print();
$obj->echo();
$obj->readonly();
$obj->require();
$obj->require_once();
$obj->return();
Expand DownExpand Up@@ -205,6 +207,7 @@ Obj::throw
Obj::array
Obj::print
Obj::echo
Obj::readonly
Obj::require
Obj::require_once
Obj::return
Expand Down
29 changes: 29 additions & 0 deletions Zend/tests/readonly_props/by_ref_foreach.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
--TEST--
By-ref foreach over readonly property
--FILE--
<?php

class Test {
public readonly int $prop;

public function init() {
$this->prop = 1;
}
}

$test = new Test;

// Okay, as foreach skips over uninitialized properties.
foreach ($test as &$prop) {}

$test->init();

try {
foreach ($test as &$prop) {}
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot acquire reference to readonly property Test::$prop
122 changes: 122 additions & 0 deletions Zend/tests/readonly_props/cache_slot.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
--TEST--
Test interaction with cache slots
--FILE--
<?php

class Test {
public readonly string $prop;
public readonly array $prop2;
public readonly object $prop3;
public function setProp(string $prop) {
$this->prop = $prop;
}
public function initAndAppendProp2() {
$this->prop2 = [];
$this->prop2[] = 1;
}
public function initProp3() {
$this->prop3 = new stdClass;
$this->prop3->foo = 1;
}
public function replaceProp3() {
$ref =& $this->prop3;
$ref = new stdClass;
}
}

$test = new Test;
$test->setProp("a");
var_dump($test->prop);
try {
$test->setProp("b");
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop);
echo "\n";

$test = new Test;
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$test->initProp3();
$test->replaceProp3();
var_dump($test->prop3);
$test->replaceProp3();
var_dump($test->prop3);
echo "\n";

// Test variations using closure rebinding, so we have unknown property_info in JIT.
$test = new Test;
(function() { $this->prop2 = []; })->call($test);
$appendProp2 = (function() {
$this->prop2[] = 1;
})->bindTo($test, Test::class);
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$replaceProp3 = (function() {
$ref =& $this->prop3;
$ref = new stdClass;
})->bindTo($test, Test::class);
$test->initProp3();
$replaceProp3();
var_dump($test->prop3);
$replaceProp3();
var_dump($test->prop3);

?>
--EXPECT--
string(1) "a"
Cannot modify readonly property Test::$prop
string(1) "a"

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#3 (1) {
["foo"]=>
int(1)
}
object(stdClass)#3 (1) {
["foo"]=>
int(1)
}

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
72 changes: 72 additions & 0 deletions Zend/tests/readonly_props/initialization_scope.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
--TEST--
Initialization can only happen from private scope
--FILE--
<?php

class A {
public readonly int $prop;

public function initPrivate() {
$this->prop = 3;
}
}
class B extends A {
public function initProtected() {
$this->prop = 2;
}
}

$test = new B;
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initProtected();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->initPrivate();
var_dump($test->prop);

// Rebinding bypass works.
$test = new B;
(function() {
$this->prop = 1;
})->bindTo($test, A::class)();
var_dump($test->prop);

class C extends A {
public readonly int $prop;
}

$test = new C;
$test->initPrivate();
var_dump($test->prop);

class X {
public function initFromParent() {
$this->prop = 1;
}
}
class Y extends X {
public readonly int $prop;
}

$test = new Y;
try {
$test->initFromParent();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot initialize readonly property A::$prop from global scope
Cannot initialize readonly property A::$prop from scope B
int(3)
int(1)
int(3)
Cannot initialize readonly property Y::$prop from scope X
74 changes: 74 additions & 0 deletions Zend/tests/readonly_props/magic_get_set.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
--TEST--
Interaction with magic get/set
--FILE--
<?php

class Test {
public readonly int $prop;

public function unsetProp() {
unset($this->prop);
}

public function __get($name) {
echo __METHOD__, "($name)\n";
return 1;
}

public function __set($name, $value) {
echo __METHOD__, "($name, $value)\n";
}

public function __unset($name) {
echo __METHOD__, "($name)\n";
}

public function __isset($name) {
echo __METHOD__, "($name)\n";
return true;
}
}

$test = new Test;

// The property is in uninitialized state, no magic methods should be invoked.
var_dump(isset($test->prop));
try {
var_dump($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->unsetProp();

var_dump(isset($test->prop));
var_dump($test->prop);
$test->prop = 2;
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
bool(false)
Typed property Test::$prop must not be accessed before initialization
Cannot initialize readonly property Test::$prop from global scope
Cannot unset readonly property Test::$prop from global scope
Test::__isset(prop)
bool(true)
Test::__get(prop)
int(1)
Test::__set(prop, 2)
Test::__unset(prop)
26 changes: 26 additions & 0 deletions Zend/tests/readonly_props/override_with_attributes.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
--TEST--
Can override readonly property with attributes
--FILE--
<?php

#[Attribute]
class FooAttribute {}

class A {
public readonly int $prop;

public function __construct() {
$this->prop = 42;
}
}
class B extends A {
#[FooAttribute]
public readonly int $prop;
}

var_dump((new ReflectionProperty(B::class, 'prop'))->getAttributes()[0]->newInstance());

?>
--EXPECT--
object(FooAttribute)#1 (0) {
}
41 changes: 41 additions & 0 deletions Zend/tests/readonly_props/promotion.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
--TEST--
Promoted readonly property
--FILE--
<?php

class Point {
public function __construct(
public readonly float $x = 0.0,
public readonly float $y = 0.0,
public readonly float $z = 0.0,
) {}
}

var_dump(new Point);
$point = new Point(1.0, 2.0, 3.0);
try {
$point->x = 4.0;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($point);

?>
--EXPECT--
object(Point)#1 (3) {
["x"]=>
float(0)
["y"]=>
float(0)
["z"]=>
float(0)
}
Cannot modify readonly property Point::$x
object(Point)#1 (3) {
["x"]=>
float(1)
["y"]=>
float(2)
["z"]=>
float(3)
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
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
3 changes: 3 additions & 0 deletions Zend/tests/grammar/semi_reserved_001.phpt
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,6 +45,7 @@ class Obj
function array(){ echo __METHOD__, PHP_EOL; }
function print(){ echo __METHOD__, PHP_EOL; }
function echo(){ echo __METHOD__, PHP_EOL; }
function readonly(){ echo __METHOD__, PHP_EOL; }
function require(){ echo __METHOD__, PHP_EOL; }
function require_once(){ echo __METHOD__, PHP_EOL; }
function return(){ echo __METHOD__, PHP_EOL; }
Expand DownExpand Up@@ -125,6 +126,7 @@ $obj->throw();
$obj->array();
$obj->print();
$obj->echo();
$obj->readonly();
$obj->require();
$obj->require_once();
$obj->return();
Expand DownExpand Up@@ -205,6 +207,7 @@ Obj::throw
Obj::array
Obj::print
Obj::echo
Obj::readonly
Obj::require
Obj::require_once
Obj::return
Expand Down
29 changes: 29 additions & 0 deletions Zend/tests/readonly_props/by_ref_foreach.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
--TEST--
By-ref foreach over readonly property
--FILE--
<?php

class Test {
public readonly int $prop;

public function init() {
$this->prop = 1;
}
}

$test = new Test;

// Okay, as foreach skips over uninitialized properties.
foreach ($test as &$prop) {}

$test->init();

try {
foreach ($test as &$prop) {}
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot acquire reference to readonly property Test::$prop
122 changes: 122 additions & 0 deletions Zend/tests/readonly_props/cache_slot.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
--TEST--
Test interaction with cache slots
--FILE--
<?php

class Test {
public readonly string $prop;
public readonly array $prop2;
public readonly object $prop3;
public function setProp(string $prop) {
$this->prop = $prop;
}
public function initAndAppendProp2() {
$this->prop2 = [];
$this->prop2[] = 1;
}
public function initProp3() {
$this->prop3 = new stdClass;
$this->prop3->foo = 1;
}
public function replaceProp3() {
$ref =& $this->prop3;
$ref = new stdClass;
}
}

$test = new Test;
$test->setProp("a");
var_dump($test->prop);
try {
$test->setProp("b");
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop);
echo "\n";

$test = new Test;
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$test->initProp3();
$test->replaceProp3();
var_dump($test->prop3);
$test->replaceProp3();
var_dump($test->prop3);
echo "\n";

// Test variations using closure rebinding, so we have unknown property_info in JIT.
$test = new Test;
(function() { $this->prop2 = []; })->call($test);
$appendProp2 = (function() {
$this->prop2[] = 1;
})->bindTo($test, Test::class);
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$replaceProp3 = (function() {
$ref =& $this->prop3;
$ref = new stdClass;
})->bindTo($test, Test::class);
$test->initProp3();
$replaceProp3();
var_dump($test->prop3);
$replaceProp3();
var_dump($test->prop3);

?>
--EXPECT--
string(1) "a"
Cannot modify readonly property Test::$prop
string(1) "a"

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#3 (1) {
["foo"]=>
int(1)
}
object(stdClass)#3 (1) {
["foo"]=>
int(1)
}

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
72 changes: 72 additions & 0 deletions Zend/tests/readonly_props/initialization_scope.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
--TEST--
Initialization can only happen from private scope
--FILE--
<?php

class A {
public readonly int $prop;

public function initPrivate() {
$this->prop = 3;
}
}
class B extends A {
public function initProtected() {
$this->prop = 2;
}
}

$test = new B;
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initProtected();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->initPrivate();
var_dump($test->prop);

// Rebinding bypass works.
$test = new B;
(function() {
$this->prop = 1;
})->bindTo($test, A::class)();
var_dump($test->prop);

class C extends A {
public readonly int $prop;
}

$test = new C;
$test->initPrivate();
var_dump($test->prop);

class X {
public function initFromParent() {
$this->prop = 1;
}
}
class Y extends X {
public readonly int $prop;
}

$test = new Y;
try {
$test->initFromParent();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot initialize readonly property A::$prop from global scope
Cannot initialize readonly property A::$prop from scope B
int(3)
int(1)
int(3)
Cannot initialize readonly property Y::$prop from scope X
74 changes: 74 additions & 0 deletions Zend/tests/readonly_props/magic_get_set.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
--TEST--
Interaction with magic get/set
--FILE--
<?php

class Test {
public readonly int $prop;

public function unsetProp() {
unset($this->prop);
}

public function __get($name) {
echo __METHOD__, "($name)\n";
return 1;
}

public function __set($name, $value) {
echo __METHOD__, "($name, $value)\n";
}

public function __unset($name) {
echo __METHOD__, "($name)\n";
}

public function __isset($name) {
echo __METHOD__, "($name)\n";
return true;
}
}

$test = new Test;

// The property is in uninitialized state, no magic methods should be invoked.
var_dump(isset($test->prop));
try {
var_dump($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->unsetProp();

var_dump(isset($test->prop));
var_dump($test->prop);
$test->prop = 2;
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
bool(false)
Typed property Test::$prop must not be accessed before initialization
Cannot initialize readonly property Test::$prop from global scope
Cannot unset readonly property Test::$prop from global scope
Test::__isset(prop)
bool(true)
Test::__get(prop)
int(1)
Test::__set(prop, 2)
Test::__unset(prop)
26 changes: 26 additions & 0 deletions Zend/tests/readonly_props/override_with_attributes.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
--TEST--
Can override readonly property with attributes
--FILE--
<?php

#[Attribute]
class FooAttribute {}

class A {
public readonly int $prop;

public function __construct() {
$this->prop = 42;
}
}
class B extends A {
#[FooAttribute]
public readonly int $prop;
}

var_dump((new ReflectionProperty(B::class, 'prop'))->getAttributes()[0]->newInstance());

?>
--EXPECT--
object(FooAttribute)#1 (0) {
}
41 changes: 41 additions & 0 deletions Zend/tests/readonly_props/promotion.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
--TEST--
Promoted readonly property
--FILE--
<?php

class Point {
public function __construct(
public readonly float $x = 0.0,
public readonly float $y = 0.0,
public readonly float $z = 0.0,
) {}
}

var_dump(new Point);
$point = new Point(1.0, 2.0, 3.0);
try {
$point->x = 4.0;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($point);

?>
--EXPECT--
object(Point)#1 (3) {
["x"]=>
float(0)
["y"]=>
float(0)
["z"]=>
float(0)
}
Cannot modify readonly property Point::$x
object(Point)#1 (3) {
["x"]=>
float(1)
["y"]=>
float(2)
["z"]=>
float(3)
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Closed
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
3 changes: 3 additions & 0 deletions Zend/tests/grammar/semi_reserved_001.phpt
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,6 +45,7 @@ class Obj
function array(){ echo __METHOD__, PHP_EOL; }
function print(){ echo __METHOD__, PHP_EOL; }
function echo(){ echo __METHOD__, PHP_EOL; }
function readonly(){ echo __METHOD__, PHP_EOL; }
function require(){ echo __METHOD__, PHP_EOL; }
function require_once(){ echo __METHOD__, PHP_EOL; }
function return(){ echo __METHOD__, PHP_EOL; }
Expand DownExpand Up@@ -125,6 +126,7 @@ $obj->throw();
$obj->array();
$obj->print();
$obj->echo();
$obj->readonly();
$obj->require();
$obj->require_once();
$obj->return();
Expand DownExpand Up@@ -205,6 +207,7 @@ Obj::throw
Obj::array
Obj::print
Obj::echo
Obj::readonly
Obj::require
Obj::require_once
Obj::return
Expand Down
29 changes: 29 additions & 0 deletions Zend/tests/readonly_props/by_ref_foreach.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
--TEST--
By-ref foreach over readonly property
--FILE--
<?php

class Test {
public readonly int $prop;

public function init() {
$this->prop = 1;
}
}

$test = new Test;

// Okay, as foreach skips over uninitialized properties.
foreach ($test as &$prop) {}

$test->init();

try {
foreach ($test as &$prop) {}
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot acquire reference to readonly property Test::$prop
122 changes: 122 additions & 0 deletions Zend/tests/readonly_props/cache_slot.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
--TEST--
Test interaction with cache slots
--FILE--
<?php

class Test {
public readonly string $prop;
public readonly array $prop2;
public readonly object $prop3;
public function setProp(string $prop) {
$this->prop = $prop;
}
public function initAndAppendProp2() {
$this->prop2 = [];
$this->prop2[] = 1;
}
public function initProp3() {
$this->prop3 = new stdClass;
$this->prop3->foo = 1;
}
public function replaceProp3() {
$ref =& $this->prop3;
$ref = new stdClass;
}
}

$test = new Test;
$test->setProp("a");
var_dump($test->prop);
try {
$test->setProp("b");
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop);
echo "\n";

$test = new Test;
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$test->initProp3();
$test->replaceProp3();
var_dump($test->prop3);
$test->replaceProp3();
var_dump($test->prop3);
echo "\n";

// Test variations using closure rebinding, so we have unknown property_info in JIT.
$test = new Test;
(function() { $this->prop2 = []; })->call($test);
$appendProp2 = (function() {
$this->prop2[] = 1;
})->bindTo($test, Test::class);
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$replaceProp3 = (function() {
$ref =& $this->prop3;
$ref = new stdClass;
})->bindTo($test, Test::class);
$test->initProp3();
$replaceProp3();
var_dump($test->prop3);
$replaceProp3();
var_dump($test->prop3);

?>
--EXPECT--
string(1) "a"
Cannot modify readonly property Test::$prop
string(1) "a"

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#3 (1) {
["foo"]=>
int(1)
}
object(stdClass)#3 (1) {
["foo"]=>
int(1)
}

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
72 changes: 72 additions & 0 deletions Zend/tests/readonly_props/initialization_scope.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
--TEST--
Initialization can only happen from private scope
--FILE--
<?php

class A {
public readonly int $prop;

public function initPrivate() {
$this->prop = 3;
}
}
class B extends A {
public function initProtected() {
$this->prop = 2;
}
}

$test = new B;
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initProtected();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->initPrivate();
var_dump($test->prop);

// Rebinding bypass works.
$test = new B;
(function() {
$this->prop = 1;
})->bindTo($test, A::class)();
var_dump($test->prop);

class C extends A {
public readonly int $prop;
}

$test = new C;
$test->initPrivate();
var_dump($test->prop);

class X {
public function initFromParent() {
$this->prop = 1;
}
}
class Y extends X {
public readonly int $prop;
}

$test = new Y;
try {
$test->initFromParent();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot initialize readonly property A::$prop from global scope
Cannot initialize readonly property A::$prop from scope B
int(3)
int(1)
int(3)
Cannot initialize readonly property Y::$prop from scope X
74 changes: 74 additions & 0 deletions Zend/tests/readonly_props/magic_get_set.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
--TEST--
Interaction with magic get/set
--FILE--
<?php

class Test {
public readonly int $prop;

public function unsetProp() {
unset($this->prop);
}

public function __get($name) {
echo __METHOD__, "($name)\n";
return 1;
}

public function __set($name, $value) {
echo __METHOD__, "($name, $value)\n";
}

public function __unset($name) {
echo __METHOD__, "($name)\n";
}

public function __isset($name) {
echo __METHOD__, "($name)\n";
return true;
}
}

$test = new Test;

// The property is in uninitialized state, no magic methods should be invoked.
var_dump(isset($test->prop));
try {
var_dump($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->unsetProp();

var_dump(isset($test->prop));
var_dump($test->prop);
$test->prop = 2;
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
bool(false)
Typed property Test::$prop must not be accessed before initialization
Cannot initialize readonly property Test::$prop from global scope
Cannot unset readonly property Test::$prop from global scope
Test::__isset(prop)
bool(true)
Test::__get(prop)
int(1)
Test::__set(prop, 2)
Test::__unset(prop)
26 changes: 26 additions & 0 deletions Zend/tests/readonly_props/override_with_attributes.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
--TEST--
Can override readonly property with attributes
--FILE--
<?php

#[Attribute]
class FooAttribute {}

class A {
public readonly int $prop;

public function __construct() {
$this->prop = 42;
}
}
class B extends A {
#[FooAttribute]
public readonly int $prop;
}

var_dump((new ReflectionProperty(B::class, 'prop'))->getAttributes()[0]->newInstance());

?>
--EXPECT--
object(FooAttribute)#1 (0) {
}
41 changes: 41 additions & 0 deletions Zend/tests/readonly_props/promotion.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
--TEST--
Promoted readonly property
--FILE--
<?php

class Point {
public function __construct(
public readonly float $x = 0.0,
public readonly float $y = 0.0,
public readonly float $z = 0.0,
) {}
}

var_dump(new Point);
$point = new Point(1.0, 2.0, 3.0);
try {
$point->x = 4.0;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($point);

?>
--EXPECT--
object(Point)#1 (3) {
["x"]=>
float(0)
["y"]=>
float(0)
["z"]=>
float(0)
}
Cannot modify readonly property Point::$x
object(Point)#1 (3) {
["x"]=>
float(1)
["y"]=>
float(2)
["z"]=>
float(3)
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
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
3 changes: 3 additions & 0 deletions Zend/tests/grammar/semi_reserved_001.phpt
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,6 +45,7 @@ class Obj
function array(){ echo __METHOD__, PHP_EOL; }
function print(){ echo __METHOD__, PHP_EOL; }
function echo(){ echo __METHOD__, PHP_EOL; }
function readonly(){ echo __METHOD__, PHP_EOL; }
function require(){ echo __METHOD__, PHP_EOL; }
function require_once(){ echo __METHOD__, PHP_EOL; }
function return(){ echo __METHOD__, PHP_EOL; }
Expand DownExpand Up@@ -125,6 +126,7 @@ $obj->throw();
$obj->array();
$obj->print();
$obj->echo();
$obj->readonly();
$obj->require();
$obj->require_once();
$obj->return();
Expand DownExpand Up@@ -205,6 +207,7 @@ Obj::throw
Obj::array
Obj::print
Obj::echo
Obj::readonly
Obj::require
Obj::require_once
Obj::return
Expand Down
29 changes: 29 additions & 0 deletions Zend/tests/readonly_props/by_ref_foreach.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
--TEST--
By-ref foreach over readonly property
--FILE--
<?php

class Test {
public readonly int $prop;

public function init() {
$this->prop = 1;
}
}

$test = new Test;

// Okay, as foreach skips over uninitialized properties.
foreach ($test as &$prop) {}

$test->init();

try {
foreach ($test as &$prop) {}
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot acquire reference to readonly property Test::$prop
122 changes: 122 additions & 0 deletions Zend/tests/readonly_props/cache_slot.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
--TEST--
Test interaction with cache slots
--FILE--
<?php

class Test {
public readonly string $prop;
public readonly array $prop2;
public readonly object $prop3;
public function setProp(string $prop) {
$this->prop = $prop;
}
public function initAndAppendProp2() {
$this->prop2 = [];
$this->prop2[] = 1;
}
public function initProp3() {
$this->prop3 = new stdClass;
$this->prop3->foo = 1;
}
public function replaceProp3() {
$ref =& $this->prop3;
$ref = new stdClass;
}
}

$test = new Test;
$test->setProp("a");
var_dump($test->prop);
try {
$test->setProp("b");
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop);
echo "\n";

$test = new Test;
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$test->initProp3();
$test->replaceProp3();
var_dump($test->prop3);
$test->replaceProp3();
var_dump($test->prop3);
echo "\n";

// Test variations using closure rebinding, so we have unknown property_info in JIT.
$test = new Test;
(function() { $this->prop2 = []; })->call($test);
$appendProp2 = (function() {
$this->prop2[] = 1;
})->bindTo($test, Test::class);
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$replaceProp3 = (function() {
$ref =& $this->prop3;
$ref = new stdClass;
})->bindTo($test, Test::class);
$test->initProp3();
$replaceProp3();
var_dump($test->prop3);
$replaceProp3();
var_dump($test->prop3);

?>
--EXPECT--
string(1) "a"
Cannot modify readonly property Test::$prop
string(1) "a"

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#3 (1) {
["foo"]=>
int(1)
}
object(stdClass)#3 (1) {
["foo"]=>
int(1)
}

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
72 changes: 72 additions & 0 deletions Zend/tests/readonly_props/initialization_scope.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
--TEST--
Initialization can only happen from private scope
--FILE--
<?php

class A {
public readonly int $prop;

public function initPrivate() {
$this->prop = 3;
}
}
class B extends A {
public function initProtected() {
$this->prop = 2;
}
}

$test = new B;
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initProtected();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->initPrivate();
var_dump($test->prop);

// Rebinding bypass works.
$test = new B;
(function() {
$this->prop = 1;
})->bindTo($test, A::class)();
var_dump($test->prop);

class C extends A {
public readonly int $prop;
}

$test = new C;
$test->initPrivate();
var_dump($test->prop);

class X {
public function initFromParent() {
$this->prop = 1;
}
}
class Y extends X {
public readonly int $prop;
}

$test = new Y;
try {
$test->initFromParent();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot initialize readonly property A::$prop from global scope
Cannot initialize readonly property A::$prop from scope B
int(3)
int(1)
int(3)
Cannot initialize readonly property Y::$prop from scope X
74 changes: 74 additions & 0 deletions Zend/tests/readonly_props/magic_get_set.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
--TEST--
Interaction with magic get/set
--FILE--
<?php

class Test {
public readonly int $prop;

public function unsetProp() {
unset($this->prop);
}

public function __get($name) {
echo __METHOD__, "($name)\n";
return 1;
}

public function __set($name, $value) {
echo __METHOD__, "($name, $value)\n";
}

public function __unset($name) {
echo __METHOD__, "($name)\n";
}

public function __isset($name) {
echo __METHOD__, "($name)\n";
return true;
}
}

$test = new Test;

// The property is in uninitialized state, no magic methods should be invoked.
var_dump(isset($test->prop));
try {
var_dump($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->unsetProp();

var_dump(isset($test->prop));
var_dump($test->prop);
$test->prop = 2;
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
bool(false)
Typed property Test::$prop must not be accessed before initialization
Cannot initialize readonly property Test::$prop from global scope
Cannot unset readonly property Test::$prop from global scope
Test::__isset(prop)
bool(true)
Test::__get(prop)
int(1)
Test::__set(prop, 2)
Test::__unset(prop)
26 changes: 26 additions & 0 deletions Zend/tests/readonly_props/override_with_attributes.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
--TEST--
Can override readonly property with attributes
--FILE--
<?php

#[Attribute]
class FooAttribute {}

class A {
public readonly int $prop;

public function __construct() {
$this->prop = 42;
}
}
class B extends A {
#[FooAttribute]
public readonly int $prop;
}

var_dump((new ReflectionProperty(B::class, 'prop'))->getAttributes()[0]->newInstance());

?>
--EXPECT--
object(FooAttribute)#1 (0) {
}
41 changes: 41 additions & 0 deletions Zend/tests/readonly_props/promotion.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
--TEST--
Promoted readonly property
--FILE--
<?php

class Point {
public function __construct(
public readonly float $x = 0.0,
public readonly float $y = 0.0,
public readonly float $z = 0.0,
) {}
}

var_dump(new Point);
$point = new Point(1.0, 2.0, 3.0);
try {
$point->x = 4.0;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($point);

?>
--EXPECT--
object(Point)#1 (3) {
["x"]=>
float(0)
["y"]=>
float(0)
["z"]=>
float(0)
}
Cannot modify readonly property Point::$x
object(Point)#1 (3) {
["x"]=>
float(1)
["y"]=>
float(2)
["z"]=>
float(3)
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
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
3 changes: 3 additions & 0 deletions Zend/tests/grammar/semi_reserved_001.phpt
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,6 +45,7 @@ class Obj
function array(){ echo __METHOD__, PHP_EOL; }
function print(){ echo __METHOD__, PHP_EOL; }
function echo(){ echo __METHOD__, PHP_EOL; }
function readonly(){ echo __METHOD__, PHP_EOL; }
function require(){ echo __METHOD__, PHP_EOL; }
function require_once(){ echo __METHOD__, PHP_EOL; }
function return(){ echo __METHOD__, PHP_EOL; }
Expand DownExpand Up@@ -125,6 +126,7 @@ $obj->throw();
$obj->array();
$obj->print();
$obj->echo();
$obj->readonly();
$obj->require();
$obj->require_once();
$obj->return();
Expand DownExpand Up@@ -205,6 +207,7 @@ Obj::throw
Obj::array
Obj::print
Obj::echo
Obj::readonly
Obj::require
Obj::require_once
Obj::return
Expand Down
29 changes: 29 additions & 0 deletions Zend/tests/readonly_props/by_ref_foreach.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
--TEST--
By-ref foreach over readonly property
--FILE--
<?php

class Test {
public readonly int $prop;

public function init() {
$this->prop = 1;
}
}

$test = new Test;

// Okay, as foreach skips over uninitialized properties.
foreach ($test as &$prop) {}

$test->init();

try {
foreach ($test as &$prop) {}
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot acquire reference to readonly property Test::$prop
122 changes: 122 additions & 0 deletions Zend/tests/readonly_props/cache_slot.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
--TEST--
Test interaction with cache slots
--FILE--
<?php

class Test {
public readonly string $prop;
public readonly array $prop2;
public readonly object $prop3;
public function setProp(string $prop) {
$this->prop = $prop;
}
public function initAndAppendProp2() {
$this->prop2 = [];
$this->prop2[] = 1;
}
public function initProp3() {
$this->prop3 = new stdClass;
$this->prop3->foo = 1;
}
public function replaceProp3() {
$ref =& $this->prop3;
$ref = new stdClass;
}
}

$test = new Test;
$test->setProp("a");
var_dump($test->prop);
try {
$test->setProp("b");
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop);
echo "\n";

$test = new Test;
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$test->initProp3();
$test->replaceProp3();
var_dump($test->prop3);
$test->replaceProp3();
var_dump($test->prop3);
echo "\n";

// Test variations using closure rebinding, so we have unknown property_info in JIT.
$test = new Test;
(function() { $this->prop2 = []; })->call($test);
$appendProp2 = (function() {
$this->prop2[] = 1;
})->bindTo($test, Test::class);
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$replaceProp3 = (function() {
$ref =& $this->prop3;
$ref = new stdClass;
})->bindTo($test, Test::class);
$test->initProp3();
$replaceProp3();
var_dump($test->prop3);
$replaceProp3();
var_dump($test->prop3);

?>
--EXPECT--
string(1) "a"
Cannot modify readonly property Test::$prop
string(1) "a"

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#3 (1) {
["foo"]=>
int(1)
}
object(stdClass)#3 (1) {
["foo"]=>
int(1)
}

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
72 changes: 72 additions & 0 deletions Zend/tests/readonly_props/initialization_scope.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
--TEST--
Initialization can only happen from private scope
--FILE--
<?php

class A {
public readonly int $prop;

public function initPrivate() {
$this->prop = 3;
}
}
class B extends A {
public function initProtected() {
$this->prop = 2;
}
}

$test = new B;
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initProtected();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->initPrivate();
var_dump($test->prop);

// Rebinding bypass works.
$test = new B;
(function() {
$this->prop = 1;
})->bindTo($test, A::class)();
var_dump($test->prop);

class C extends A {
public readonly int $prop;
}

$test = new C;
$test->initPrivate();
var_dump($test->prop);

class X {
public function initFromParent() {
$this->prop = 1;
}
}
class Y extends X {
public readonly int $prop;
}

$test = new Y;
try {
$test->initFromParent();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot initialize readonly property A::$prop from global scope
Cannot initialize readonly property A::$prop from scope B
int(3)
int(1)
int(3)
Cannot initialize readonly property Y::$prop from scope X
74 changes: 74 additions & 0 deletions Zend/tests/readonly_props/magic_get_set.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
--TEST--
Interaction with magic get/set
--FILE--
<?php

class Test {
public readonly int $prop;

public function unsetProp() {
unset($this->prop);
}

public function __get($name) {
echo __METHOD__, "($name)\n";
return 1;
}

public function __set($name, $value) {
echo __METHOD__, "($name, $value)\n";
}

public function __unset($name) {
echo __METHOD__, "($name)\n";
}

public function __isset($name) {
echo __METHOD__, "($name)\n";
return true;
}
}

$test = new Test;

// The property is in uninitialized state, no magic methods should be invoked.
var_dump(isset($test->prop));
try {
var_dump($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->unsetProp();

var_dump(isset($test->prop));
var_dump($test->prop);
$test->prop = 2;
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
bool(false)
Typed property Test::$prop must not be accessed before initialization
Cannot initialize readonly property Test::$prop from global scope
Cannot unset readonly property Test::$prop from global scope
Test::__isset(prop)
bool(true)
Test::__get(prop)
int(1)
Test::__set(prop, 2)
Test::__unset(prop)
26 changes: 26 additions & 0 deletions Zend/tests/readonly_props/override_with_attributes.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
--TEST--
Can override readonly property with attributes
--FILE--
<?php

#[Attribute]
class FooAttribute {}

class A {
public readonly int $prop;

public function __construct() {
$this->prop = 42;
}
}
class B extends A {
#[FooAttribute]
public readonly int $prop;
}

var_dump((new ReflectionProperty(B::class, 'prop'))->getAttributes()[0]->newInstance());

?>
--EXPECT--
object(FooAttribute)#1 (0) {
}
41 changes: 41 additions & 0 deletions Zend/tests/readonly_props/promotion.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
--TEST--
Promoted readonly property
--FILE--
<?php

class Point {
public function __construct(
public readonly float $x = 0.0,
public readonly float $y = 0.0,
public readonly float $z = 0.0,
) {}
}

var_dump(new Point);
$point = new Point(1.0, 2.0, 3.0);
try {
$point->x = 4.0;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($point);

?>
--EXPECT--
object(Point)#1 (3) {
["x"]=>
float(0)
["y"]=>
float(0)
["z"]=>
float(0)
}
Cannot modify readonly property Point::$x
object(Point)#1 (3) {
["x"]=>
float(1)
["y"]=>
float(2)
["z"]=>
float(3)
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Closed
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
3 changes: 3 additions & 0 deletions Zend/tests/grammar/semi_reserved_001.phpt
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,6 +45,7 @@ class Obj
function array(){ echo __METHOD__, PHP_EOL; }
function print(){ echo __METHOD__, PHP_EOL; }
function echo(){ echo __METHOD__, PHP_EOL; }
function readonly(){ echo __METHOD__, PHP_EOL; }
function require(){ echo __METHOD__, PHP_EOL; }
function require_once(){ echo __METHOD__, PHP_EOL; }
function return(){ echo __METHOD__, PHP_EOL; }
Expand DownExpand Up@@ -125,6 +126,7 @@ $obj->throw();
$obj->array();
$obj->print();
$obj->echo();
$obj->readonly();
$obj->require();
$obj->require_once();
$obj->return();
Expand DownExpand Up@@ -205,6 +207,7 @@ Obj::throw
Obj::array
Obj::print
Obj::echo
Obj::readonly
Obj::require
Obj::require_once
Obj::return
Expand Down
29 changes: 29 additions & 0 deletions Zend/tests/readonly_props/by_ref_foreach.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
--TEST--
By-ref foreach over readonly property
--FILE--
<?php

class Test {
public readonly int $prop;

public function init() {
$this->prop = 1;
}
}

$test = new Test;

// Okay, as foreach skips over uninitialized properties.
foreach ($test as &$prop) {}

$test->init();

try {
foreach ($test as &$prop) {}
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot acquire reference to readonly property Test::$prop
122 changes: 122 additions & 0 deletions Zend/tests/readonly_props/cache_slot.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
--TEST--
Test interaction with cache slots
--FILE--
<?php

class Test {
public readonly string $prop;
public readonly array $prop2;
public readonly object $prop3;
public function setProp(string $prop) {
$this->prop = $prop;
}
public function initAndAppendProp2() {
$this->prop2 = [];
$this->prop2[] = 1;
}
public function initProp3() {
$this->prop3 = new stdClass;
$this->prop3->foo = 1;
}
public function replaceProp3() {
$ref =& $this->prop3;
$ref = new stdClass;
}
}

$test = new Test;
$test->setProp("a");
var_dump($test->prop);
try {
$test->setProp("b");
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop);
echo "\n";

$test = new Test;
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initAndAppendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$test->initProp3();
$test->replaceProp3();
var_dump($test->prop3);
$test->replaceProp3();
var_dump($test->prop3);
echo "\n";

// Test variations using closure rebinding, so we have unknown property_info in JIT.
$test = new Test;
(function() { $this->prop2 = []; })->call($test);
$appendProp2 = (function() {
$this->prop2[] = 1;
})->bindTo($test, Test::class);
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$appendProp2();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop2);
echo "\n";

$test = new Test;
$replaceProp3 = (function() {
$ref =& $this->prop3;
$ref = new stdClass;
})->bindTo($test, Test::class);
$test->initProp3();
$replaceProp3();
var_dump($test->prop3);
$replaceProp3();
var_dump($test->prop3);

?>
--EXPECT--
string(1) "a"
Cannot modify readonly property Test::$prop
string(1) "a"

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#3 (1) {
["foo"]=>
int(1)
}
object(stdClass)#3 (1) {
["foo"]=>
int(1)
}

Cannot modify readonly property Test::$prop2
Cannot modify readonly property Test::$prop2
array(0) {
}

object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
object(stdClass)#5 (1) {
["foo"]=>
int(1)
}
72 changes: 72 additions & 0 deletions Zend/tests/readonly_props/initialization_scope.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
--TEST--
Initialization can only happen from private scope
--FILE--
<?php

class A {
public readonly int $prop;

public function initPrivate() {
$this->prop = 3;
}
}
class B extends A {
public function initProtected() {
$this->prop = 2;
}
}

$test = new B;
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->initProtected();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->initPrivate();
var_dump($test->prop);

// Rebinding bypass works.
$test = new B;
(function() {
$this->prop = 1;
})->bindTo($test, A::class)();
var_dump($test->prop);

class C extends A {
public readonly int $prop;
}

$test = new C;
$test->initPrivate();
var_dump($test->prop);

class X {
public function initFromParent() {
$this->prop = 1;
}
}
class Y extends X {
public readonly int $prop;
}

$test = new Y;
try {
$test->initFromParent();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot initialize readonly property A::$prop from global scope
Cannot initialize readonly property A::$prop from scope B
int(3)
int(1)
int(3)
Cannot initialize readonly property Y::$prop from scope X
74 changes: 74 additions & 0 deletions Zend/tests/readonly_props/magic_get_set.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
--TEST--
Interaction with magic get/set
--FILE--
<?php

class Test {
public readonly int $prop;

public function unsetProp() {
unset($this->prop);
}

public function __get($name) {
echo __METHOD__, "($name)\n";
return 1;
}

public function __set($name, $value) {
echo __METHOD__, "($name, $value)\n";
}

public function __unset($name) {
echo __METHOD__, "($name)\n";
}

public function __isset($name) {
echo __METHOD__, "($name)\n";
return true;
}
}

$test = new Test;

// The property is in uninitialized state, no magic methods should be invoked.
var_dump(isset($test->prop));
try {
var_dump($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$test->prop = 1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

$test->unsetProp();

var_dump(isset($test->prop));
var_dump($test->prop);
$test->prop = 2;
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
bool(false)
Typed property Test::$prop must not be accessed before initialization
Cannot initialize readonly property Test::$prop from global scope
Cannot unset readonly property Test::$prop from global scope
Test::__isset(prop)
bool(true)
Test::__get(prop)
int(1)
Test::__set(prop, 2)
Test::__unset(prop)
26 changes: 26 additions & 0 deletions Zend/tests/readonly_props/override_with_attributes.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
--TEST--
Can override readonly property with attributes
--FILE--
<?php

#[Attribute]
class FooAttribute {}

class A {
public readonly int $prop;

public function __construct() {
$this->prop = 42;
}
}
class B extends A {
#[FooAttribute]
public readonly int $prop;
}

var_dump((new ReflectionProperty(B::class, 'prop'))->getAttributes()[0]->newInstance());

?>
--EXPECT--
object(FooAttribute)#1 (0) {
}
41 changes: 41 additions & 0 deletions Zend/tests/readonly_props/promotion.phpt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
--TEST--
Promoted readonly property
--FILE--
<?php

class Point {
public function __construct(
public readonly float $x = 0.0,
public readonly float $y = 0.0,
public readonly float $z = 0.0,
) {}
}

var_dump(new Point);
$point = new Point(1.0, 2.0, 3.0);
try {
$point->x = 4.0;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($point);

?>
--EXPECT--
object(Point)#1 (3) {
["x"]=>
float(0)
["y"]=>
float(0)
["z"]=>
float(0)
}
Cannot modify readonly property Point::$x
object(Point)#1 (3) {
["x"]=>
float(1)
["y"]=>
float(2)
["z"]=>
float(3)
}
Loading