-
Notifications
You must be signed in to change notification settings - Fork 8k
Open
Description
Description
The "new" operator and __construct() return values
- To my surprise
return $this;in the__construct()function gives the errorUsing $this when not in object context. If this is the corrrect errortext is debatable, but this is correct behavior, it is not allowed. - But returning anything but $this does not fail. Which is very strange. This is only in combination with the "new" operator. The "new" operator should emit TypeError for all return values.
- When calling __construct() as a regular function a return value might have some purpose. But why not deprecate calling __construct() outside the new operator?
P.S. If it should be a TypeError or some other error is debatable. But at least it should error.
Reproduce
The following code:
<?php
const SOMECONST = 'Const hello';
enum SomeEnum
{
case Hearts;
case Diamonds;
case Clubs;
case Spades;
}
function SomeFunction()
{
}
$SomeFunctionClosure = function()
{
};
$SomeFunctionFirstClassCallable = SomeFunction(...);
$someFunctionClosureFromCallabe = Closure::fromCallable('SomeFunction');
class ShouldTypeError
{
public function __construct($returnValue)
{
if ($returnValue !== 'no-return') {
if ($returnValue === '$this') {
return $this;
} elseif ($returnValue === 'no-return-value') {
return;
} elseif ($returnValue === 'void') {
return void;
} elseif ($returnValue === 'never') {
return never;
}
return $returnValue;
}
}
}
$testcases = [
'no-return',
'no-return-value',
'$this',
new ShouldTypeError('no-return'),
SOMECONST,
'abc',
1,
1.23,
null,
true,
false,
['a', 'b', 'c'],
['a' => 'a', 'b' => 'b', 'c' => 'c', 0 => 'Zero'],
'never',
'void',
SomeEnum::Spades,
function() {
echo 'Hi'.PHP_EOL;
},
$SomeFunctionClosure,
$SomeFunctionFirstClassCallable,
$someFunctionClosureFromCallabe,
new DateTimeImmutable(),
];
foreach($testcases as $testcase) {
echo "--------------[ TESTCASE ]--------------\n";
var_dump($testcase);
echo "\n";
try {
$didReturn = new ShouldTypeError($testcase);
switch($testcase) {
case 'no-return':
echo "Success: without a return statement is always valid.\n";
break;
case 'no-return-value':
echo "Success: a return statement without a value is always valid.\n";
break;
case '$this':
echo "Dubious: return $this is dubious.\n";
echo "- it fullfills the return type, so it could be allowed.\n";
echo "- but returning anything from a constructor is nonsense, because it is discarded.\n";
echo " As shown by the third testcase new SomeTypeError('no-return').\n";
break;
default:
echo "Error: why is it not a return TypeError?\n";
break;
}
if (!($didReturn instanceof ShouldTypeError)) {
echo "Failed to new a ShouldTypeError.\n";
}
} catch (Throwable $ex) {
echo "Success: throwable: ".$ex->getMessage()."\n";
}
}Resulted in this output (php 8.5.2):
--------------[ TESTCASE ]--------------
string(9) "no-return"
Success: without a return statement is always valid.
--------------[ TESTCASE ]--------------
string(15) "no-return-value"
Success: a return statement without a value is always valid.
--------------[ TESTCASE ]--------------
string(5) "$this"
Success: throwable: Using $this when not in object context
--------------[ TESTCASE ]--------------
object(ShouldTypeError)#4 (0) {
}
Error: why is it not a return TypeError?
--------------[ TESTCASE ]--------------
string(11) "Const hello"
Error: why is it not a return TypeError?
--------------[ TESTCASE ]--------------
string(3) "abc"
Error: why is it not a return TypeError?
--------------[ TESTCASE ]--------------
int(1)
Error: why is it not a return TypeError?
--------------[ TESTCASE ]--------------
float(1.23)
Error: why is it not a return TypeError?
--------------[ TESTCASE ]--------------
NULL
Error: why is it not a return TypeError?
--------------[ TESTCASE ]--------------
bool(true)
Success: without a return statement is always valid.
--------------[ TESTCASE ]--------------
bool(false)
Error: why is it not a return TypeError?
--------------[ TESTCASE ]--------------
array(3) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
[2]=>
string(1) "c"
}
Error: why is it not a return TypeError?
--------------[ TESTCASE ]--------------
array(4) {
["a"]=>
string(1) "a"
["b"]=>
string(1) "b"
["c"]=>
string(1) "c"
[0]=>
string(4) "Zero"
}
Error: why is it not a return TypeError?
--------------[ TESTCASE ]--------------
string(5) "never"
Success: throwable: Undefined constant "never"
--------------[ TESTCASE ]--------------
string(4) "void"
Success: throwable: Undefined constant "void"
--------------[ TESTCASE ]--------------
enum(SomeEnum::Spades)
Error: why is it not a return TypeError?
--------------[ TESTCASE ]--------------
object(Closure)#6 (3) {
["name"]=>
string(22) "{closure:/in/sXXe8:67}"
["file"]=>
string(9) "/in/sXXe8"
["line"]=>
int(67)
}
Error: why is it not a return TypeError?
--------------[ TESTCASE ]--------------
object(Closure)#1 (3) {
["name"]=>
string(22) "{closure:/in/sXXe8:17}"
["file"]=>
string(9) "/in/sXXe8"
["line"]=>
int(17)
}
Error: why is it not a return TypeError?
--------------[ TESTCASE ]--------------
object(Closure)#2 (1) {
["function"]=>
string(12) "SomeFunction"
}
Error: why is it not a return TypeError?
--------------[ TESTCASE ]--------------
object(Closure)#3 (1) {
["function"]=>
string(12) "SomeFunction"
}
Error: why is it not a return TypeError?
--------------[ TESTCASE ]--------------
object(DateTimeImmutable)#7 (3) {
["date"]=>
string(26) "2026-01-30 13:31:14.025598"
["timezone_type"]=>
int(3)
["timezone"]=>
string(16) "Europe/Amsterdam"
}
Error: why is it not a return TypeError?
PHP Version
PHP 8.5.2 via 3v4l
Operating System
3v4l