RFC: Marking return values as important (#[\NoDiscard]) (#17599)

RFC: https://wiki.php.net/rfc/marking_return_value_as_important

Co-authored-by: Volker Dusch <volker@tideways-gmbh.com>
This commit is contained in:
Tim Düsterhus 2025-04-02 09:35:29 +02:00 committed by GitHub
parent f11c22ae30
commit 5544be7018
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
52 changed files with 1261 additions and 68 deletions

4
NEWS
View file

@ -38,8 +38,10 @@ PHP NEWS
. Fixed bug GH-18033 (NULL-ptr dereference when using register_tick_function
in destructor). (nielsdos)
. Fixed bug GH-18026 (Improve "expecting token" error for ampersand). (ilutov)
. Added the #[\NoDiscard] attribute to indicate that a function's return
value is important and should be consumed. (timwolla, Volker Dusch)
. Added the (void) cast to indicate that not using a value is intentional.
(timwolla)
(timwolla, Volker Dusch)
. Added get_error_handler(), get_exception_handler() functions. (Arnaud)
. Fixed bug GH-15753 and GH-16198 (Bind traits before parent class). (ilutov)

View file

@ -114,8 +114,13 @@ PHP 8.5 UPGRADE NOTES
. Fatal Errors (such as an exceeded maximum execution time) now include a
backtrace.
RFC: https://wiki.php.net/rfc/error_backtraces_v2
. Added the (void) to indicate that not using a value is intentional. The
(void) cast does nothing by itself.
. Added the #[\NoDiscard] attribute to indicate that a function's return
value is important and should be consumed.
RFC: https://wiki.php.net/rfc/marking_return_value_as_important
. Added the (void) cast to indicate that not using a value is intentional.
The (void) cast has no effect on the program's execution by itself, but
it can be used to suppress warnings emitted by #[\NoDiscard] and possibly
also diagnostics emitted by external IDEs or static analysis tools.
RFC: https://wiki.php.net/rfc/marking_return_value_as_important
- Curl:

View file

@ -78,8 +78,10 @@ static void zend_delete_call_instructions(zend_op_array *op_array, zend_op *opli
static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_op *opline, zend_function *func)
{
const uint32_t no_discard = RETURN_VALUE_USED(opline) ? 0 : ZEND_ACC_NODISCARD;
if (func->type == ZEND_USER_FUNCTION
&& !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_DEPRECATED))
&& !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_DEPRECATED|no_discard))
/* TODO: function copied from trait may be inconsistent ??? */
&& !(func->op_array.fn_flags & (ZEND_ACC_TRAIT_CLONE))
&& fcall->extended_value >= func->op_array.required_num_args
@ -202,18 +204,12 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
literal_dtor(&ZEND_OP2_LITERAL(fcall));
fcall->op2.constant = fcall->op2.constant + 1;
if (opline->opcode != ZEND_CALLABLE_CONVERT) {
opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
}
} else if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
fcall->opcode = ZEND_INIT_FCALL;
fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
literal_dtor(&op_array->literals[fcall->op2.constant]);
literal_dtor(&op_array->literals[fcall->op2.constant + 2]);
fcall->op2.constant = fcall->op2.constant + 1;
if (opline->opcode != ZEND_CALLABLE_CONVERT) {
opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
}
} else if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL
|| fcall->opcode == ZEND_INIT_METHOD_CALL
|| fcall->opcode == ZEND_INIT_PARENT_PROPERTY_HOOK_CALL
@ -223,6 +219,16 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
ZEND_UNREACHABLE();
}
/* If the INIT opcode changed the DO opcode can also change to
* a more optimized one.
*
* At this point we also know whether or not the result of
* the DO opcode is used, allowing to optimize calls to
* ZEND_ACC_NODISCARD functions. */
if (opline->opcode != ZEND_CALLABLE_CONVERT) {
opline->opcode = zend_get_call_op(fcall, call_stack[call].func, !RESULT_UNUSED(opline));
}
if ((ZEND_OPTIMIZER_PASS_16 & ctx->optimization_level)
&& call_stack[call].try_inline
&& opline->opcode != ZEND_CALLABLE_CONVERT) {

View file

@ -0,0 +1,86 @@
--TEST--
#[\NoDiscard]: Basic test.
--FILE--
<?php
#[\NoDiscard]
function test(): int {
return 0;
}
#[\NoDiscard("this is important")]
function test2(): int {
return 0;
}
#[\NoDiscard]
function test3(...$args): int {
return 0;
}
class Clazz {
#[\NoDiscard]
function test(): int {
return 0;
}
#[\NoDiscard("this is important")]
function test2(): int {
return 0;
}
#[\NoDiscard]
static function test3(): int {
return 0;
}
}
$closure = #[\NoDiscard] function(): int {
return 0;
};
$closure2 = #[\NoDiscard] function(): int {
return 0;
};
test();
test2();
test3(1, 2, named: 3);
call_user_func("test");
$fcc = test(...);
$fcc();
$cls = new Clazz();
$cls->test();
$cls->test2();
Clazz::test3();
call_user_func([$cls, "test"]);
$closure();
$closure2();
?>
--EXPECTF--
Warning: The return value of function test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
Warning: The return value of function test2() should either be used or intentionally ignored by casting it as (void), this is important in %s on line %d
Warning: The return value of function test3() should either be used or intentionally ignored by casting it as (void) in %s on line %d
Warning: The return value of function test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
Warning: The return value of function test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
Warning: The return value of method Clazz::test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
Warning: The return value of method Clazz::test2() should either be used or intentionally ignored by casting it as (void), this is important in %s on line %d
Warning: The return value of method Clazz::test3() should either be used or intentionally ignored by casting it as (void) in %s on line %d
Warning: The return value of method Clazz::test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
Warning: The return value of function {closure:%s:%d}() should either be used or intentionally ignored by casting it as (void) in %s on line %d
Warning: The return value of function {closure:%s:%d}() should either be used or intentionally ignored by casting it as (void) in %s on line %d

View file

@ -0,0 +1,44 @@
--TEST--
#[\NoDiscard]: __call(), __callStatic(), and __invoke().
--FILE--
<?php
class Clazz {
#[\NoDiscard]
public function __call(string $name, array $args): int {
echo "__call({$name})", PHP_EOL;
return strlen($name);
}
#[\NoDiscard]
public static function __callStatic(string $name, array $args): int {
echo "__callStatic({$name})", PHP_EOL;
return strlen($name);
}
#[\NoDiscard]
public function __invoke(string $param): int {
echo "__invoke({$param})", PHP_EOL;
return strlen($param);
}
}
$cls = new Clazz();
$cls->test();
Clazz::test();
$cls('foo');
?>
--EXPECTF--
Warning: The return value of method Clazz::test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
__call(test)
Warning: The return value of method Clazz::test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
__callStatic(test)
Warning: The return value of method Clazz::__invoke() should either be used or intentionally ignored by casting it as (void) in %s on line %d
__invoke(foo)

View file

@ -0,0 +1,22 @@
--TEST--
#[\NoDiscard]: Taken from trait.
--FILE--
<?php
trait T {
#[\NoDiscard]
function test(): int {
return 0;
}
}
class Clazz {
use T;
}
$cls = new Clazz();
$cls->test();
?>
--EXPECTF--
Warning: The return value of method Clazz::test() should either be used or intentionally ignored by casting it as (void) in %s on line %d

View file

@ -0,0 +1,17 @@
--TEST--
#[\NoDiscard]: Native function and method.
--FILE--
<?php
$f = tmpfile();
flock($f, LOCK_SH | LOCK_NB);
fclose($f);
$date = new DateTimeImmutable('now');
$date->setTimestamp(0);
?>
--EXPECTF--
Warning: The return value of function flock() should either be used or intentionally ignored by casting it as (void), as locking the stream might have failed in %s on line %d
Warning: The return value of method DateTimeImmutable::setTimestamp() should either be used or intentionally ignored by casting it as (void), as DateTimeImmutable::setTimestamp() does not modify the object itself in %s on line %d

View file

@ -0,0 +1,20 @@
--TEST--
#[\NoDiscard]: execute_ex overwritten
--EXTENSIONS--
zend_test
--INI--
zend_test.replace_zend_execute_ex=1
opcache.jit=disable
--FILE--
<?php
#[\NoDiscard]
function test(): int {
return 0;
}
test();
?>
--EXPECTF--
Warning: The return value of function test() should either be used or intentionally ignored by casting it as (void) in %s on line %d

View file

@ -0,0 +1,21 @@
--TEST--
#[\NoDiscard]: execute_internal overwritten
--EXTENSIONS--
zend_test
--INI--
zend_test.observer.execute_internal=1
--FILE--
<?php
$f = tmpfile();
flock($f, LOCK_SH | LOCK_NB);
fclose($f);
?>
--EXPECTF--
<!-- internal enter tmpfile() -->
<!-- internal enter NoDiscard::__construct() -->
Warning: The return value of function flock() should either be used or intentionally ignored by casting it as (void), as locking the stream might have failed in %s on line %d
<!-- internal enter flock() -->
<!-- internal enter fclose() -->

View file

@ -0,0 +1,18 @@
--TEST--
#[\NoDiscard]: Combining with #[\Deprecated].
--FILE--
<?php
#[\NoDiscard]
#[\Deprecated]
function test(): int {
return 0;
}
test();
?>
--EXPECTF--
Deprecated: Function test() is deprecated in %s on line %d
Warning: The return value of function test() should either be used or intentionally ignored by casting it as (void) in %s on line %d

View file

@ -0,0 +1,14 @@
--TEST--
#[\NoDiscard]: Combining with #[\Deprecated] (Internal).
--EXTENSIONS--
zend_test
--FILE--
<?php
zend_test_deprecated_nodiscard();
?>
--EXPECTF--
Deprecated: Function zend_test_deprecated_nodiscard() is deprecated, custom message in %s on line %d
Warning: The return value of function zend_test_deprecated_nodiscard() should either be used or intentionally ignored by casting it as (void), custom message 2 in %s on line %d

View file

@ -0,0 +1,76 @@
--TEST--
#[\NoDiscard]: Functions with known-used result use DO_[IU]CALL opcodes
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.optimization_level=-1
opcache.opt_debug_level=0x20000
--EXTENSIONS--
opcache
--FILE--
<?php
$f = tmpfile();
flock($f, LOCK_SH | LOCK_NB);
(void)flock($f, LOCK_SH | LOCK_NB);
$success = flock($f, LOCK_SH | LOCK_NB);
fclose($f);
#[\NoDiscard]
function test() {
return new stdClass();
}
test();
(void)test();
$obj = test();
?>
--EXPECTF--
$_main:
; (lines=29, args=0, vars=3, tmps=1)
; (after optimizer)
; %s
0000 INIT_FCALL 0 %d string("tmpfile")
0001 V3 = DO_ICALL
0002 ASSIGN CV0($f) V3
0003 INIT_FCALL 2 %d string("flock")
0004 SEND_VAR CV0($f) 1
0005 SEND_VAL int(5) 2
0006 DO_FCALL_BY_NAME
0007 INIT_FCALL 2 %d string("flock")
0008 SEND_VAR CV0($f) 1
0009 SEND_VAL int(5) 2
0010 V3 = DO_ICALL
0011 FREE V3
0012 INIT_FCALL 2 %d string("flock")
0013 SEND_VAR CV0($f) 1
0014 SEND_VAL int(5) 2
0015 V3 = DO_ICALL
0016 ASSIGN CV1($success) V3
0017 INIT_FCALL 1 %d string("fclose")
0018 SEND_VAR CV0($f) 1
0019 DO_ICALL
0020 INIT_FCALL 0 %d string("test")
0021 DO_FCALL_BY_NAME
0022 INIT_FCALL 0 %d string("test")
0023 V3 = DO_UCALL
0024 FREE V3
0025 INIT_FCALL 0 %d string("test")
0026 V3 = DO_UCALL
0027 ASSIGN CV2($obj) V3
0028 RETURN int(1)
test:
; (lines=3, args=0, vars=0, tmps=1)
; (after optimizer)
; %s
0000 V0 = NEW 0 string("stdClass")
0001 DO_FCALL
0002 RETURN V0
LIVE RANGES:
0: 0001 - 0002 (new)
Warning: The return value of function flock() should either be used or intentionally ignored by casting it as (void), as locking the stream might have failed in %s on line %d
Warning: The return value of function test() should either be used or intentionally ignored by casting it as (void) in %s on line %d

View file

@ -0,0 +1,21 @@
--TEST--
#[\NoDiscard]: Code is E_USER_WARNING.
--FILE--
<?php
set_error_handler(function (int $errno, string $errstr, ?string $errfile = null, ?int $errline = null) {
var_dump($errno, E_USER_WARNING, $errno === E_USER_WARNING);
});
#[\NoDiscard]
function test(): int {
return 0;
}
test();
?>
--EXPECT--
int(512)
int(512)
bool(true)

View file

@ -0,0 +1,14 @@
--TEST--
#[\NoDiscard]: NoDiscard::$message is readonly.
--FILE--
<?php
$d = new \NoDiscard("foo");
$d->message = 'bar';
?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot modify readonly property NoDiscard::$message in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d

View file

@ -0,0 +1,15 @@
--TEST--
#[\NoDiscard]: __construct() respects that properties are readonly.
--FILE--
<?php
$d = new \NoDiscard("foo");
$d->__construct("bar");
?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot modify readonly property NoDiscard::$message in %s:%d
Stack trace:
#0 %s(%d): NoDiscard->__construct('bar')
#1 {main}
thrown in %s on line %d

View file

@ -0,0 +1,66 @@
--TEST--
#[\NoDiscard]: Assigning to variable suppresses.
--FILE--
<?php
#[\NoDiscard]
function test(): int {
return 0;
}
#[\NoDiscard("this is important")]
function test2(): int {
return 0;
}
#[\NoDiscard]
function test3(...$args): int {
return 0;
}
class Clazz {
#[\NoDiscard]
function test(): int {
return 0;
}
#[\NoDiscard("this is important")]
function test2(): int {
return 0;
}
#[\NoDiscard]
static function test3(): int {
return 0;
}
}
$closure = #[\NoDiscard] function(): int {
return 0;
};
$closure2 = #[\NoDiscard] function(): int {
return 0;
};
$_ = test();
$_ = test2();
$_ = test3(1, 2, named: 3);
$_ = call_user_func("test");
$fcc = test(...);
$_ = $fcc();
$cls = new Clazz();
$_ = $cls->test();
$_ = $cls->test2();
$_ = call_user_func([$cls, "test"]);
$_ = Clazz::test3();
$_ = $closure();
$_ = $closure2();
?>
DONE
--EXPECT--
DONE

View file

@ -0,0 +1,66 @@
--TEST--
#[\NoDiscard]: Casting to (void) suppresses.
--FILE--
<?php
#[\NoDiscard]
function test(): int {
return 0;
}
#[\NoDiscard("this is important")]
function test2(): int {
return 0;
}
#[\NoDiscard]
function test3(...$args): int {
return 0;
}
class Clazz {
#[\NoDiscard]
function test(): int {
return 0;
}
#[\NoDiscard("this is important")]
function test2(): int {
return 0;
}
#[\NoDiscard]
static function test3(): int {
return 0;
}
}
$closure = #[\NoDiscard] function(): int {
return 0;
};
$closure2 = #[\NoDiscard] function(): int {
return 0;
};
(void)test();
(void)test2();
(void)test3(1, 2, named: 3);
(void)call_user_func("test");
$fcc = test(...);
(void)$fcc();
$cls = new Clazz();
(void)$cls->test();
(void)$cls->test2();
(void)call_user_func([$cls, "test"]);
(void)Clazz::test3();
(void)$closure();
(void)$closure2();
?>
DONE
--EXPECT--
DONE

View file

@ -0,0 +1,35 @@
--TEST--
#[\NoDiscard]: Throwing error handler.
--FILE--
<?php
set_error_handler(function (int $errno, string $errstr, ?string $errfile = null, ?int $errline = null) {
throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
});
#[\NoDiscard]
function test(): int {
return 0;
}
try {
test();
} catch (ErrorException $e) {
echo "Caught: ", $e->getMessage(), PHP_EOL;
}
#[\NoDiscard]
function test2(): stdClass {
return new stdClass();
}
try {
test2();
} catch (ErrorException $e) {
echo "Caught: ", $e->getMessage(), PHP_EOL;
}
?>
--EXPECT--
Caught: The return value of function test() should either be used or intentionally ignored by casting it as (void)
Caught: The return value of function test2() should either be used or intentionally ignored by casting it as (void)

View file

@ -0,0 +1,15 @@
--TEST--
#[\NoDiscard]: Type validation of $message parameter with int.
--FILE--
<?php
#[\NoDiscard(1234)]
function test(): int {
return 0;
}
test();
?>
--EXPECTF--
Warning: The return value of function test() should either be used or intentionally ignored by casting it as (void), 1234 in %s on line %d

View file

@ -0,0 +1,20 @@
--TEST--
#[\NoDiscard]: Type validation of $message parameter with int and strict types.
--FILE--
<?php
declare(strict_types = 1);
#[\NoDiscard(1234)]
function test(): int {
return 0;
}
test();
?>
--EXPECTF--
Fatal error: Uncaught TypeError: NoDiscard::__construct(): Argument #1 ($message) must be of type ?string, int given in %s:%d
Stack trace:
#0 %s(%d): NoDiscard->__construct(1234)
#1 {main}
thrown in %s on line %d

View file

@ -0,0 +1,18 @@
--TEST--
#[\NoDiscard]: Type validation of $message parameter with array.
--FILE--
<?php
#[\NoDiscard([])]
function test(): int {
return 0;
}
test();
?>
--EXPECTF--
Fatal error: Uncaught TypeError: NoDiscard::__construct(): Argument #1 ($message) must be of type ?string, array given in %s:%d
Stack trace:
#0 %s(%d): NoDiscard->__construct(Array)
#1 {main}
thrown in %s on line %d

View file

@ -0,0 +1,18 @@
--TEST--
#[\NoDiscard]: Type validation of $message parameter with native enum case.
--FILE--
<?php
#[\NoDiscard(\Random\IntervalBoundary::ClosedOpen)]
function test(): int {
return 0;
}
test();
?>
--EXPECTF--
Fatal error: Uncaught TypeError: NoDiscard::__construct(): Argument #1 ($message) must be of type ?string, Random\IntervalBoundary given in %s:%d
Stack trace:
#0 %s(%d): NoDiscard->__construct(Random\IntervalBoundary::ClosedOpen)
#1 {main}
thrown in %s on line %d

View file

@ -0,0 +1,16 @@
--TEST--
#[\NoDiscard]: Not allowed on '__clone'.
--FILE--
<?php
class Clazz {
#[\NoDiscard]
public function __clone() {
}
}
$cls = new Clazz();
?>
--EXPECTF--
Fatal error: Method Clazz::__clone cannot be #[\NoDiscard] in %s on line %d

View file

@ -0,0 +1,16 @@
--TEST--
#[\NoDiscard]: Not allowed on '__construct'.
--FILE--
<?php
class Clazz {
#[\NoDiscard]
public function __construct() {
}
}
$cls = new Clazz();
?>
--EXPECTF--
Fatal error: Method Clazz::__construct cannot be #[\NoDiscard] in %s on line %d

View file

@ -0,0 +1,15 @@
--TEST--
#[\NoDiscard]: Not allowed on never function.
--FILE--
<?php
#[\NoDiscard]
function test(): never {
throw new \Exception('Error!');
}
test();
?>
--EXPECTF--
Fatal error: A never returning function does not return a value, but #[\NoDiscard] requires a return value in %s on line %d

View file

@ -0,0 +1,20 @@
--TEST--
#[\NoDiscard]: Not allowed on 'get' property hook.
--FILE--
<?php
class Clazz {
public string $test {
#[\NoDiscard]
get {
return 'asd';
}
}
}
$cls = new Clazz();
$cls->test;
?>
--EXPECTF--
Fatal error: #[\NoDiscard] is not supported for property hooks in %s on line %d

View file

@ -0,0 +1,20 @@
--TEST--
#[\NoDiscard]: Not allowed on 'set' property hook.
--FILE--
<?php
class Clazz {
public string $test {
#[\NoDiscard]
set(string $value) {
$this->test = $value;
}
}
}
$cls = new Foo();
$cls->test = 'foo';
?>
--EXPECTF--
Fatal error: #[\NoDiscard] is not supported for property hooks in %s on line %d

View file

@ -0,0 +1,15 @@
--TEST--
#[\NoDiscard]: Not allowed on void function.
--FILE--
<?php
#[\NoDiscard]
function test(): void {
return;
}
test();
?>
--EXPECTF--
Fatal error: A void function does not return a value, but #[\NoDiscard] requires a return value in %s on line %d

View file

@ -2698,6 +2698,12 @@ static void zend_check_magic_method_arg_type(uint32_t arg_num, const zend_class_
static void zend_check_magic_method_return_type(const zend_class_entry *ce, const zend_function *fptr, int error_type, int return_type)
{
if (return_type == MAY_BE_VOID) {
if (fptr->common.fn_flags & ZEND_ACC_NODISCARD) {
zend_error_noreturn(error_type, "Method %s::%s cannot be #[\\NoDiscard]", ZSTR_VAL(ce->name), ZSTR_VAL(fptr->common.function_name));
}
}
if (!(fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
/* For backwards compatibility reasons, do not enforce the return type if it is not set. */
return;
@ -2757,6 +2763,10 @@ static void zend_check_magic_method_no_return_type(
zend_error_noreturn(error_type, "Method %s::%s() cannot declare a return type",
ZSTR_VAL(ce->name), ZSTR_VAL(fptr->common.function_name));
}
if (fptr->common.fn_flags & ZEND_ACC_NODISCARD) {
zend_error_noreturn(error_type, "Method %s::%s cannot be #[\\NoDiscard]", ZSTR_VAL(ce->name), ZSTR_VAL(fptr->common.function_name));
}
}
ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce, const zend_function *fptr, zend_string *lcname, int error_type) /* {{{ */

View file

@ -31,6 +31,7 @@ ZEND_API zend_class_entry *zend_ce_sensitive_parameter;
ZEND_API zend_class_entry *zend_ce_sensitive_parameter_value;
ZEND_API zend_class_entry *zend_ce_override;
ZEND_API zend_class_entry *zend_ce_deprecated;
ZEND_API zend_class_entry *zend_ce_nodiscard;
static zend_object_handlers attributes_object_handlers_sensitive_parameter_value;
@ -193,6 +194,29 @@ ZEND_METHOD(Deprecated, __construct)
}
}
ZEND_METHOD(NoDiscard, __construct)
{
zend_string *message = NULL;
zval value;
ZEND_PARSE_PARAMETERS_START(0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_STR_OR_NULL(message)
ZEND_PARSE_PARAMETERS_END();
if (message) {
ZVAL_STR(&value, message);
} else {
ZVAL_NULL(&value);
}
zend_update_property_ex(zend_ce_nodiscard, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value);
/* The assignment might fail due to 'readonly'. */
if (UNEXPECTED(EG(exception))) {
RETURN_THROWS();
}
}
static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
{
if (attributes) {
@ -520,6 +544,9 @@ void zend_register_attribute_ce(void)
zend_ce_deprecated = register_class_Deprecated();
attr = zend_mark_internal_attribute(zend_ce_deprecated);
zend_ce_nodiscard = register_class_NoDiscard();
attr = zend_mark_internal_attribute(zend_ce_nodiscard);
}
void zend_attributes_shutdown(void)

View file

@ -47,6 +47,7 @@ extern ZEND_API zend_class_entry *zend_ce_sensitive_parameter;
extern ZEND_API zend_class_entry *zend_ce_sensitive_parameter_value;
extern ZEND_API zend_class_entry *zend_ce_override;
extern ZEND_API zend_class_entry *zend_ce_deprecated;
extern ZEND_API zend_class_entry *zend_ce_nodiscard;
typedef struct {
zend_string *name;

View file

@ -84,3 +84,14 @@ final class Deprecated
public function __construct(?string $message = null, ?string $since = null) {}
}
/**
* @strict-properties
*/
#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION)]
final class NoDiscard
{
public readonly ?string $message;
public function __construct(?string $message = null) {}
}

View file

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 2358a0d820edd06a1702c84104bfd545af08311c */
* Stub hash: 6b54bc195be211caabb395b621380681953c1f5a */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL")
@ -29,6 +29,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Deprecated___construct, 0, 0, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, since, IS_STRING, 1, "null")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_NoDiscard___construct, 0, 0, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null")
ZEND_END_ARG_INFO()
ZEND_METHOD(Attribute, __construct);
ZEND_METHOD(ReturnTypeWillChange, __construct);
ZEND_METHOD(AllowDynamicProperties, __construct);
@ -38,6 +42,7 @@ ZEND_METHOD(SensitiveParameterValue, getValue);
ZEND_METHOD(SensitiveParameterValue, __debugInfo);
ZEND_METHOD(Override, __construct);
ZEND_METHOD(Deprecated, __construct);
ZEND_METHOD(NoDiscard, __construct);
static const zend_function_entry class_Attribute_methods[] = {
ZEND_ME(Attribute, __construct, arginfo_class_Attribute___construct, ZEND_ACC_PUBLIC)
@ -76,6 +81,11 @@ static const zend_function_entry class_Deprecated_methods[] = {
ZEND_FE_END
};
static const zend_function_entry class_NoDiscard_methods[] = {
ZEND_ME(NoDiscard, __construct, arginfo_class_NoDiscard___construct, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
static zend_class_entry *register_class_Attribute(void)
{
zend_class_entry ce, *class_entry;
@ -253,3 +263,24 @@ static zend_class_entry *register_class_Deprecated(void)
return class_entry;
}
static zend_class_entry *register_class_NoDiscard(void)
{
zend_class_entry ce, *class_entry;
INIT_CLASS_ENTRY(ce, "NoDiscard", class_NoDiscard_methods);
class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES);
zval property_message_default_value;
ZVAL_UNDEF(&property_message_default_value);
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_MESSAGE), &property_message_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
zend_string *attribute_name_Attribute_class_NoDiscard_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, 1);
zend_attribute *attribute_Attribute_class_NoDiscard_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_NoDiscard_0, 1);
zend_string_release(attribute_name_Attribute_class_NoDiscard_0);
zval attribute_Attribute_class_NoDiscard_0_arg0;
ZVAL_LONG(&attribute_Attribute_class_NoDiscard_0_arg0, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_FUNCTION);
ZVAL_COPY_VALUE(&attribute_Attribute_class_NoDiscard_0->args[0].value, &attribute_Attribute_class_NoDiscard_0_arg0);
return class_entry;
}

View file

@ -3927,13 +3927,15 @@ static uint32_t zend_compile_args(
}
/* }}} */
ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc) /* {{{ */
ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc, bool result_used) /* {{{ */
{
if (fbc) {
uint32_t no_discard = result_used ? 0 : ZEND_ACC_NODISCARD;
if (fbc && init_op->opcode != ZEND_NEW) {
ZEND_ASSERT(!(fbc->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE));
if (fbc->type == ZEND_INTERNAL_FUNCTION && !(CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS)) {
if (init_op->opcode == ZEND_INIT_FCALL && !zend_execute_internal) {
if (!(fbc->common.fn_flags & ZEND_ACC_DEPRECATED)) {
if (!(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
return ZEND_DO_ICALL;
} else {
return ZEND_DO_FCALL_BY_NAME;
@ -3941,7 +3943,7 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc) /*
}
} else if (!(CG(compiler_options) & ZEND_COMPILE_IGNORE_USER_FUNCTIONS)){
if (zend_execute_ex == execute_ex) {
if (!(fbc->common.fn_flags & ZEND_ACC_DEPRECATED)) {
if (!(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
return ZEND_DO_UCALL;
} else {
return ZEND_DO_FCALL_BY_NAME;
@ -3991,7 +3993,16 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_fun
opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc);
}
opline = zend_emit_op(result, zend_get_call_op(opline, fbc), NULL, NULL);
uint8_t call_op = zend_get_call_op(
opline,
fbc,
/* result_used: At this point we do not yet reliably
* know if the result is used. Deoptimize #[\NoDiscard]
* calls to be sure. The optimizer will fix this up.
*/
false
);
opline = zend_emit_op(result, call_op, NULL, NULL);
if (may_have_extra_named_args) {
opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
}
@ -8353,6 +8364,16 @@ static zend_op_array *zend_compile_func_decl_ex(
if (deprecated_attribute) {
op_array->fn_flags |= ZEND_ACC_DEPRECATED;
}
zend_attribute *nodiscard_attribute = zend_get_attribute_str(
op_array->attributes,
"nodiscard",
sizeof("nodiscard")-1
);
if (nodiscard_attribute) {
op_array->fn_flags |= ZEND_ACC_NODISCARD;
}
}
/* Do not leak the class scope into free standing functions, even if they are dynamically
@ -8403,6 +8424,27 @@ static zend_op_array *zend_compile_func_decl_ex(
}
}
if (op_array->fn_flags & ZEND_ACC_NODISCARD) {
if (is_hook) {
zend_error_noreturn(E_COMPILE_ERROR, "#[\\NoDiscard] is not supported for property hooks");
}
if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
zend_arg_info *return_info = CG(active_op_array)->arg_info - 1;
if (ZEND_TYPE_CONTAINS_CODE(return_info->type, IS_VOID)) {
zend_error_noreturn(E_COMPILE_ERROR,
"A void %s does not return a value, but #[\\NoDiscard] requires a return value",
CG(active_class_entry) != NULL ? "method" : "function");
}
if (ZEND_TYPE_CONTAINS_CODE(return_info->type, IS_NEVER)) {
zend_error_noreturn(E_COMPILE_ERROR,
"A never returning %s does not return a value, but #[\\NoDiscard] requires a return value",
CG(active_class_entry) != NULL ? "method" : "function");
}
}
}
zend_compile_stmt(stmt_ast);
if (is_method) {

View file

@ -333,7 +333,7 @@ typedef struct _zend_oparray_context {
/* Class cannot be serialized or unserialized | | | */
#define ZEND_ACC_NOT_SERIALIZABLE (1 << 29) /* X | | | */
/* | | | */
/* Function Flags (unused: 29-30) | | | */
/* Function Flags (unused: 30) | | | */
/* ============== | | | */
/* | | | */
/* deprecation flag | | | */
@ -395,6 +395,9 @@ typedef struct _zend_oparray_context {
/* has #[\Override] attribute | | | */
#define ZEND_ACC_OVERRIDE (1 << 28) /* | X | | */
/* | | | */
/* has #[\NoDiscard] attribute | | | */
#define ZEND_ACC_NODISCARD (1 << 29) /* | X | | */
/* | | | */
/* op_array uses strict mode types | | | */
#define ZEND_ACC_STRICT_TYPES (1U << 31) /* | X | | */
@ -981,7 +984,7 @@ ZEND_API bool zend_is_compiling(void);
ZEND_API char *zend_make_compiled_string_description(const char *name);
ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_handlers);
uint32_t zend_get_class_fetch_type(const zend_string *name);
ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc);
ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc, bool result_used);
ZEND_API bool zend_is_smart_branch(const zend_op *opline);
typedef bool (*zend_auto_global_callback)(zend_string *name);

View file

@ -1907,6 +1907,87 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_functi
zend_string_release(message_suffix);
}
ZEND_COLD static zend_result ZEND_FASTCALL get_nodiscard_suffix_from_attribute(HashTable *attributes, zend_class_entry* scope, zend_string **message_suffix)
{
*message_suffix = ZSTR_EMPTY_ALLOC();
if (!attributes) {
return SUCCESS;
}
zend_attribute *nodiscard = zend_get_attribute_str(attributes, "nodiscard", sizeof("nodiscard")-1);
if (!nodiscard) {
return SUCCESS;
}
if (nodiscard->argc == 0) {
return SUCCESS;
}
zend_result result = FAILURE;
zend_string *message = ZSTR_EMPTY_ALLOC();
zval obj;
ZVAL_UNDEF(&obj);
zval *z;
/* Construct the NoDiscard object to correctly handle parameter processing. */
if (FAILURE == zend_get_attribute_object(&obj, zend_ce_nodiscard, nodiscard, scope, NULL)) {
goto out;
}
/* Extract the $message property. */
z = zend_read_property_ex(zend_ce_nodiscard, Z_OBJ_P(&obj), ZSTR_KNOWN(ZEND_STR_MESSAGE), false, NULL);
ZEND_ASSERT(z != &EG(uninitialized_zval));
if (Z_TYPE_P(z) == IS_STRING) {
message = Z_STR_P(z);
}
/* Construct the suffix. */
*message_suffix = zend_strpprintf_unchecked(
0,
"%s%S",
ZSTR_LEN(message) > 0 ? ", " : "",
message
);
result = SUCCESS;
out:
zval_ptr_dtor(&obj);
return result;
}
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_nodiscard_function(const zend_function *fbc)
{
zend_string *message_suffix = ZSTR_EMPTY_ALLOC();
if (get_nodiscard_suffix_from_attribute(fbc->common.attributes, fbc->common.scope, &message_suffix) == FAILURE) {
return;
}
int code = fbc->type == ZEND_INTERNAL_FUNCTION ? E_WARNING : E_USER_WARNING;
if (fbc->common.scope) {
zend_error_unchecked(code, "The return value of method %s::%s() should either be used or intentionally ignored by casting it as (void)%S",
ZSTR_VAL(fbc->common.scope->name),
ZSTR_VAL(fbc->common.function_name),
message_suffix
);
} else {
zend_error_unchecked(code, "The return value of function %s() should either be used or intentionally ignored by casting it as (void)%S",
ZSTR_VAL(fbc->common.function_name),
message_suffix
);
}
zend_string_release(message_suffix);
}
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_constant(const zend_class_constant *c, const zend_string *constant_name)
{
zend_string *message_suffix = ZSTR_EMPTY_ALLOC();

View file

@ -62,6 +62,7 @@ extern ZEND_API const zend_internal_function zend_pass_function;
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_function *fbc);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_nodiscard_function(const zend_function *fbc);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_constant(const zend_class_constant *c, const zend_string *constant_name);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_false_to_array_deprecated(void);
ZEND_COLD void ZEND_FASTCALL zend_param_must_be_ref(const zend_function *func, uint32_t arg_num);

View file

@ -1679,7 +1679,7 @@ ZEND_API zend_function *zend_get_call_trampoline_func(const zend_class_entry *ce
func->fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE
| ZEND_ACC_PUBLIC
| ZEND_ACC_VARIADIC
| (fbc->common.fn_flags & (ZEND_ACC_RETURN_REFERENCE|ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED));
| (fbc->common.fn_flags & (ZEND_ACC_RETURN_REFERENCE|ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED|ZEND_ACC_NODISCARD));
/* Attributes outlive the trampoline because they are created by the compiler. */
func->attributes = fbc->common.attributes;
if (is_static) {

View file

@ -4156,8 +4156,15 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL,OBSERVER))
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
zend_deprecated_function(fbc);
const uint32_t no_discard = RETURN_VALUE_USED(opline) ? 0 : ZEND_ACC_NODISCARD;
if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
zend_deprecated_function(fbc);
}
if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
zend_nodiscard_function(fbc);
}
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
if (!RETURN_VALUE_USED(opline)) {
@ -4260,8 +4267,15 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL,OBSERVER))
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
zend_deprecated_function(fbc);
const uint32_t no_discard = RETURN_VALUE_USED(opline) ? 0 : ZEND_ACC_NODISCARD;
if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
zend_deprecated_function(fbc);
}
if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
zend_nodiscard_function(fbc);
}
if (UNEXPECTED(EG(exception) != NULL)) {
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));

66
Zend/zend_vm_execute.h generated
View file

@ -1552,8 +1552,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
zend_deprecated_function(fbc);
const uint32_t no_discard = 0 ? 0 : ZEND_ACC_NODISCARD;
if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
zend_deprecated_function(fbc);
}
if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
zend_nodiscard_function(fbc);
}
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
if (!0) {
@ -1654,8 +1661,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
zend_deprecated_function(fbc);
const uint32_t no_discard = 1 ? 0 : ZEND_ACC_NODISCARD;
if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
zend_deprecated_function(fbc);
}
if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
zend_nodiscard_function(fbc);
}
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
if (!1) {
@ -1756,8 +1770,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
zend_deprecated_function(fbc);
const uint32_t no_discard = RETURN_VALUE_USED(opline) ? 0 : ZEND_ACC_NODISCARD;
if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
zend_deprecated_function(fbc);
}
if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
zend_nodiscard_function(fbc);
}
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
if (!RETURN_VALUE_USED(opline)) {
@ -1860,8 +1881,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
zend_deprecated_function(fbc);
const uint32_t no_discard = 0 ? 0 : ZEND_ACC_NODISCARD;
if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
zend_deprecated_function(fbc);
}
if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
zend_nodiscard_function(fbc);
}
if (UNEXPECTED(EG(exception) != NULL)) {
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));
@ -1978,8 +2006,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
zend_deprecated_function(fbc);
const uint32_t no_discard = 1 ? 0 : ZEND_ACC_NODISCARD;
if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
zend_deprecated_function(fbc);
}
if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
zend_nodiscard_function(fbc);
}
if (UNEXPECTED(EG(exception) != NULL)) {
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));
@ -2096,8 +2131,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_OBS
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
zend_deprecated_function(fbc);
const uint32_t no_discard = RETURN_VALUE_USED(opline) ? 0 : ZEND_ACC_NODISCARD;
if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
zend_deprecated_function(fbc);
}
if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
zend_nodiscard_function(fbc);
}
if (UNEXPECTED(EG(exception) != NULL)) {
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));

View file

@ -1519,9 +1519,13 @@ class FuncInfo {
}
foreach ($this->attributes as $attr) {
if ($attr->class === "Deprecated") {
$flags[] = "ZEND_ACC_DEPRECATED";
break;
switch ($attr->class) {
case "Deprecated":
$flags[] = "ZEND_ACC_DEPRECATED";
break;
case "NoDiscard":
$flags[] = "ZEND_ACC_NODISCARD";
break;
}
}

View file

@ -524,29 +524,38 @@ class DateTimeImmutable implements DateTimeInterface
public function diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval {}
/** @tentative-return-type */
#[\NoDiscard(message: "as DateTimeImmutable::modify() does not modify the object itself")]
public function modify(string $modifier): DateTimeImmutable {}
/** @tentative-return-type */
#[\NoDiscard(message: "as DateTimeImmutable::add() does not modify the object itself")]
public function add(DateInterval $interval): DateTimeImmutable {}
/** @tentative-return-type */
#[\NoDiscard(message: "as DateTimeImmutable::sub() does not modify the object itself")]
public function sub(DateInterval $interval): DateTimeImmutable {}
/** @tentative-return-type */
#[\NoDiscard(message: "as DateTimeImmutable::setTimezone() does not modify the object itself")]
public function setTimezone(DateTimeZone $timezone): DateTimeImmutable {}
/** @tentative-return-type */
#[\NoDiscard(message: "as DateTimeImmutable::setTime() does not modify the object itself")]
public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTimeImmutable {}
/** @tentative-return-type */
#[\NoDiscard(message: "as DateTimeImmutable::setDate() does not modify the object itself")]
public function setDate(int $year, int $month, int $day): DateTimeImmutable {}
/** @tentative-return-type */
#[\NoDiscard(message: "as DateTimeImmutable::setISODate() does not modify the object itself")]
public function setISODate(int $year, int $week, int $dayOfWeek = 1): DateTimeImmutable {}
/** @tentative-return-type */
#[\NoDiscard(message: "as DateTimeImmutable::setTimestamp() does not modify the object itself")]
public function setTimestamp(int $timestamp): DateTimeImmutable {}
#[\NoDiscard(message: "as DateTimeImmutable::setMicrosecond() does not modify the object itself")]
public function setMicrosecond(int $microsecond): static {}
/** @tentative-return-type */

View file

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: d7a318f6fd85e23c6352323e03c323035a511738 */
* Stub hash: 093743b4fe7a698d1262cc1a81b60a85064fdccb */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_strtotime, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0)
@ -725,15 +725,15 @@ static const zend_function_entry class_DateTimeImmutable_methods[] = {
ZEND_RAW_FENTRY("getTimestamp", zif_date_timestamp_get, arginfo_class_DateTimeImmutable_getTimestamp, ZEND_ACC_PUBLIC, NULL, NULL)
ZEND_RAW_FENTRY("getMicrosecond", zim_DateTime_getMicrosecond, arginfo_class_DateTimeImmutable_getMicrosecond, ZEND_ACC_PUBLIC, NULL, NULL)
ZEND_RAW_FENTRY("diff", zif_date_diff, arginfo_class_DateTimeImmutable_diff, ZEND_ACC_PUBLIC, NULL, NULL)
ZEND_ME(DateTimeImmutable, modify, arginfo_class_DateTimeImmutable_modify, ZEND_ACC_PUBLIC)
ZEND_ME(DateTimeImmutable, add, arginfo_class_DateTimeImmutable_add, ZEND_ACC_PUBLIC)
ZEND_ME(DateTimeImmutable, sub, arginfo_class_DateTimeImmutable_sub, ZEND_ACC_PUBLIC)
ZEND_ME(DateTimeImmutable, setTimezone, arginfo_class_DateTimeImmutable_setTimezone, ZEND_ACC_PUBLIC)
ZEND_ME(DateTimeImmutable, setTime, arginfo_class_DateTimeImmutable_setTime, ZEND_ACC_PUBLIC)
ZEND_ME(DateTimeImmutable, setDate, arginfo_class_DateTimeImmutable_setDate, ZEND_ACC_PUBLIC)
ZEND_ME(DateTimeImmutable, setISODate, arginfo_class_DateTimeImmutable_setISODate, ZEND_ACC_PUBLIC)
ZEND_ME(DateTimeImmutable, setTimestamp, arginfo_class_DateTimeImmutable_setTimestamp, ZEND_ACC_PUBLIC)
ZEND_ME(DateTimeImmutable, setMicrosecond, arginfo_class_DateTimeImmutable_setMicrosecond, ZEND_ACC_PUBLIC)
ZEND_ME(DateTimeImmutable, modify, arginfo_class_DateTimeImmutable_modify, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
ZEND_ME(DateTimeImmutable, add, arginfo_class_DateTimeImmutable_add, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
ZEND_ME(DateTimeImmutable, sub, arginfo_class_DateTimeImmutable_sub, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
ZEND_ME(DateTimeImmutable, setTimezone, arginfo_class_DateTimeImmutable_setTimezone, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
ZEND_ME(DateTimeImmutable, setTime, arginfo_class_DateTimeImmutable_setTime, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
ZEND_ME(DateTimeImmutable, setDate, arginfo_class_DateTimeImmutable_setDate, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
ZEND_ME(DateTimeImmutable, setISODate, arginfo_class_DateTimeImmutable_setISODate, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
ZEND_ME(DateTimeImmutable, setTimestamp, arginfo_class_DateTimeImmutable_setTimestamp, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
ZEND_ME(DateTimeImmutable, setMicrosecond, arginfo_class_DateTimeImmutable_setMicrosecond, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
ZEND_ME(DateTimeImmutable, createFromMutable, arginfo_class_DateTimeImmutable_createFromMutable, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(DateTimeImmutable, createFromInterface, arginfo_class_DateTimeImmutable_createFromInterface, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_FE_END
@ -989,6 +989,88 @@ static zend_class_entry *register_class_DateTimeImmutable(zend_class_entry *clas
class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0);
zend_class_implements(class_entry, 1, class_entry_DateTimeInterface);
zend_string *attribute_name_NoDiscard_func_modify_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
zend_attribute *attribute_NoDiscard_func_modify_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "modify", sizeof("modify") - 1), attribute_name_NoDiscard_func_modify_0, 1);
zend_string_release(attribute_name_NoDiscard_func_modify_0);
zval attribute_NoDiscard_func_modify_0_arg0;
zend_string *attribute_NoDiscard_func_modify_0_arg0_str = zend_string_init("as DateTimeImmutable::modify() does not modify the object itself", strlen("as DateTimeImmutable::modify() does not modify the object itself"), 1);
ZVAL_STR(&attribute_NoDiscard_func_modify_0_arg0, attribute_NoDiscard_func_modify_0_arg0_str);
ZVAL_COPY_VALUE(&attribute_NoDiscard_func_modify_0->args[0].value, &attribute_NoDiscard_func_modify_0_arg0);
attribute_NoDiscard_func_modify_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
zend_string *attribute_name_NoDiscard_func_add_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
zend_attribute *attribute_NoDiscard_func_add_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "add", sizeof("add") - 1), attribute_name_NoDiscard_func_add_0, 1);
zend_string_release(attribute_name_NoDiscard_func_add_0);
zval attribute_NoDiscard_func_add_0_arg0;
zend_string *attribute_NoDiscard_func_add_0_arg0_str = zend_string_init("as DateTimeImmutable::add() does not modify the object itself", strlen("as DateTimeImmutable::add() does not modify the object itself"), 1);
ZVAL_STR(&attribute_NoDiscard_func_add_0_arg0, attribute_NoDiscard_func_add_0_arg0_str);
ZVAL_COPY_VALUE(&attribute_NoDiscard_func_add_0->args[0].value, &attribute_NoDiscard_func_add_0_arg0);
attribute_NoDiscard_func_add_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
zend_string *attribute_name_NoDiscard_func_sub_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
zend_attribute *attribute_NoDiscard_func_sub_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "sub", sizeof("sub") - 1), attribute_name_NoDiscard_func_sub_0, 1);
zend_string_release(attribute_name_NoDiscard_func_sub_0);
zval attribute_NoDiscard_func_sub_0_arg0;
zend_string *attribute_NoDiscard_func_sub_0_arg0_str = zend_string_init("as DateTimeImmutable::sub() does not modify the object itself", strlen("as DateTimeImmutable::sub() does not modify the object itself"), 1);
ZVAL_STR(&attribute_NoDiscard_func_sub_0_arg0, attribute_NoDiscard_func_sub_0_arg0_str);
ZVAL_COPY_VALUE(&attribute_NoDiscard_func_sub_0->args[0].value, &attribute_NoDiscard_func_sub_0_arg0);
attribute_NoDiscard_func_sub_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
zend_string *attribute_name_NoDiscard_func_settimezone_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
zend_attribute *attribute_NoDiscard_func_settimezone_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settimezone", sizeof("settimezone") - 1), attribute_name_NoDiscard_func_settimezone_0, 1);
zend_string_release(attribute_name_NoDiscard_func_settimezone_0);
zval attribute_NoDiscard_func_settimezone_0_arg0;
zend_string *attribute_NoDiscard_func_settimezone_0_arg0_str = zend_string_init("as DateTimeImmutable::setTimezone() does not modify the object itself", strlen("as DateTimeImmutable::setTimezone() does not modify the object itself"), 1);
ZVAL_STR(&attribute_NoDiscard_func_settimezone_0_arg0, attribute_NoDiscard_func_settimezone_0_arg0_str);
ZVAL_COPY_VALUE(&attribute_NoDiscard_func_settimezone_0->args[0].value, &attribute_NoDiscard_func_settimezone_0_arg0);
attribute_NoDiscard_func_settimezone_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
zend_string *attribute_name_NoDiscard_func_settime_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
zend_attribute *attribute_NoDiscard_func_settime_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settime", sizeof("settime") - 1), attribute_name_NoDiscard_func_settime_0, 1);
zend_string_release(attribute_name_NoDiscard_func_settime_0);
zval attribute_NoDiscard_func_settime_0_arg0;
zend_string *attribute_NoDiscard_func_settime_0_arg0_str = zend_string_init("as DateTimeImmutable::setTime() does not modify the object itself", strlen("as DateTimeImmutable::setTime() does not modify the object itself"), 1);
ZVAL_STR(&attribute_NoDiscard_func_settime_0_arg0, attribute_NoDiscard_func_settime_0_arg0_str);
ZVAL_COPY_VALUE(&attribute_NoDiscard_func_settime_0->args[0].value, &attribute_NoDiscard_func_settime_0_arg0);
attribute_NoDiscard_func_settime_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
zend_string *attribute_name_NoDiscard_func_setdate_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
zend_attribute *attribute_NoDiscard_func_setdate_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setdate", sizeof("setdate") - 1), attribute_name_NoDiscard_func_setdate_0, 1);
zend_string_release(attribute_name_NoDiscard_func_setdate_0);
zval attribute_NoDiscard_func_setdate_0_arg0;
zend_string *attribute_NoDiscard_func_setdate_0_arg0_str = zend_string_init("as DateTimeImmutable::setDate() does not modify the object itself", strlen("as DateTimeImmutable::setDate() does not modify the object itself"), 1);
ZVAL_STR(&attribute_NoDiscard_func_setdate_0_arg0, attribute_NoDiscard_func_setdate_0_arg0_str);
ZVAL_COPY_VALUE(&attribute_NoDiscard_func_setdate_0->args[0].value, &attribute_NoDiscard_func_setdate_0_arg0);
attribute_NoDiscard_func_setdate_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
zend_string *attribute_name_NoDiscard_func_setisodate_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
zend_attribute *attribute_NoDiscard_func_setisodate_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setisodate", sizeof("setisodate") - 1), attribute_name_NoDiscard_func_setisodate_0, 1);
zend_string_release(attribute_name_NoDiscard_func_setisodate_0);
zval attribute_NoDiscard_func_setisodate_0_arg0;
zend_string *attribute_NoDiscard_func_setisodate_0_arg0_str = zend_string_init("as DateTimeImmutable::setISODate() does not modify the object itself", strlen("as DateTimeImmutable::setISODate() does not modify the object itself"), 1);
ZVAL_STR(&attribute_NoDiscard_func_setisodate_0_arg0, attribute_NoDiscard_func_setisodate_0_arg0_str);
ZVAL_COPY_VALUE(&attribute_NoDiscard_func_setisodate_0->args[0].value, &attribute_NoDiscard_func_setisodate_0_arg0);
attribute_NoDiscard_func_setisodate_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
zend_string *attribute_name_NoDiscard_func_settimestamp_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
zend_attribute *attribute_NoDiscard_func_settimestamp_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settimestamp", sizeof("settimestamp") - 1), attribute_name_NoDiscard_func_settimestamp_0, 1);
zend_string_release(attribute_name_NoDiscard_func_settimestamp_0);
zval attribute_NoDiscard_func_settimestamp_0_arg0;
zend_string *attribute_NoDiscard_func_settimestamp_0_arg0_str = zend_string_init("as DateTimeImmutable::setTimestamp() does not modify the object itself", strlen("as DateTimeImmutable::setTimestamp() does not modify the object itself"), 1);
ZVAL_STR(&attribute_NoDiscard_func_settimestamp_0_arg0, attribute_NoDiscard_func_settimestamp_0_arg0_str);
ZVAL_COPY_VALUE(&attribute_NoDiscard_func_settimestamp_0->args[0].value, &attribute_NoDiscard_func_settimestamp_0_arg0);
attribute_NoDiscard_func_settimestamp_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
zend_string *attribute_name_NoDiscard_func_setmicrosecond_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
zend_attribute *attribute_NoDiscard_func_setmicrosecond_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setmicrosecond", sizeof("setmicrosecond") - 1), attribute_name_NoDiscard_func_setmicrosecond_0, 1);
zend_string_release(attribute_name_NoDiscard_func_setmicrosecond_0);
zval attribute_NoDiscard_func_setmicrosecond_0_arg0;
zend_string *attribute_NoDiscard_func_setmicrosecond_0_arg0_str = zend_string_init("as DateTimeImmutable::setMicrosecond() does not modify the object itself", strlen("as DateTimeImmutable::setMicrosecond() does not modify the object itself"), 1);
ZVAL_STR(&attribute_NoDiscard_func_setmicrosecond_0_arg0, attribute_NoDiscard_func_setmicrosecond_0_arg0_str);
ZVAL_COPY_VALUE(&attribute_NoDiscard_func_setmicrosecond_0->args[0].value, &attribute_NoDiscard_func_setmicrosecond_0_arg0);
attribute_NoDiscard_func_setmicrosecond_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
return class_entry;
}

View file

@ -233,6 +233,8 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_loop_counter_helper(ZEND_OPCODE_H
void ZEND_FASTCALL zend_jit_copy_extra_args_helper(EXECUTE_DATA_D);
void ZEND_FASTCALL zend_jit_copy_extra_args_helper_no_skip_recv(EXECUTE_DATA_D);
bool ZEND_FASTCALL zend_jit_deprecated_helper(OPLINE_D);
bool ZEND_FASTCALL zend_jit_nodiscard_helper(OPLINE_D);
bool ZEND_FASTCALL zend_jit_deprecated_nodiscard_helper(OPLINE_D);
void ZEND_FASTCALL zend_jit_undefined_long_key(EXECUTE_DATA_D);
void ZEND_FASTCALL zend_jit_undefined_long_key_ex(zend_long key EXECUTE_DATA_DC);
void ZEND_FASTCALL zend_jit_undefined_string_key(EXECUTE_DATA_D);

View file

@ -10153,7 +10153,7 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen
ir_GUARD_NOT(
ir_AND_U32(
ir_LOAD_U32(ir_ADD_OFFSET(func_ref, offsetof(zend_op_array, fn_flags))),
ir_CONST_U32(ZEND_ACC_DEPRECATED)),
ir_CONST_U32(ZEND_ACC_DEPRECATED|ZEND_ACC_NODISCARD)),
ir_CONST_ADDR(exit_addr));
}
}
@ -10179,12 +10179,27 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen
if (opline->opcode == ZEND_DO_FCALL || opline->opcode == ZEND_DO_FCALL_BY_NAME) {
if (!func) {
if (!trace) {
ir_ref if_deprecated, ret;
ir_ref if_deprecated_nodiscard, ret;
if_deprecated = ir_IF(ir_AND_U32(
uint32_t no_discard = RETURN_VALUE_USED(opline) ? 0 : ZEND_ACC_NODISCARD;
if_deprecated_nodiscard = ir_IF(ir_AND_U32(
ir_LOAD_U32(ir_ADD_OFFSET(func_ref, offsetof(zend_op_array, fn_flags))),
ir_CONST_U32(ZEND_ACC_DEPRECATED)));
ir_IF_TRUE_cold(if_deprecated);
ir_CONST_U32(ZEND_ACC_DEPRECATED|no_discard)));
ir_IF_TRUE_cold(if_deprecated_nodiscard);
ir_ref helper = ir_CONST_FC_FUNC(no_discard ? zend_jit_deprecated_nodiscard_helper : zend_jit_deprecated_helper);
if (GCC_GLOBAL_REGS) {
ret = ir_CALL(IR_BOOL, helper);
} else {
ret = ir_CALL_1(IR_BOOL, helper, rx);
}
ir_GUARD(ret, jit_STUB_ADDR(jit, jit_stub_exception_handler));
ir_MERGE_WITH_EMPTY_FALSE(if_deprecated_nodiscard);
}
} else {
if (func->common.fn_flags & ZEND_ACC_DEPRECATED) {
ir_ref ret;
if (GCC_GLOBAL_REGS) {
ret = ir_CALL(IR_BOOL, ir_CONST_FC_FUNC(zend_jit_deprecated_helper));
@ -10192,17 +10207,18 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen
ret = ir_CALL_1(IR_BOOL, ir_CONST_FC_FUNC(zend_jit_deprecated_helper), rx);
}
ir_GUARD(ret, jit_STUB_ADDR(jit, jit_stub_exception_handler));
ir_MERGE_WITH_EMPTY_FALSE(if_deprecated);
}
} else if (func->common.fn_flags & ZEND_ACC_DEPRECATED) {
ir_ref ret;
if (GCC_GLOBAL_REGS) {
ret = ir_CALL(IR_BOOL, ir_CONST_FC_FUNC(zend_jit_deprecated_helper));
} else {
ret = ir_CALL_1(IR_BOOL, ir_CONST_FC_FUNC(zend_jit_deprecated_helper), rx);
if ((func->common.fn_flags & ZEND_ACC_NODISCARD) && !RETURN_VALUE_USED(opline)) {
ir_ref ret;
if (GCC_GLOBAL_REGS) {
ret = ir_CALL(IR_BOOL, ir_CONST_FC_FUNC(zend_jit_nodiscard_helper));
} else {
ret = ir_CALL_1(IR_BOOL, ir_CONST_FC_FUNC(zend_jit_nodiscard_helper), rx);
}
ir_GUARD(ret, jit_STUB_ADDR(jit, jit_stub_exception_handler));
}
ir_GUARD(ret, jit_STUB_ADDR(jit, jit_stub_exception_handler));
}
}

View file

@ -204,6 +204,54 @@ bool ZEND_FASTCALL zend_jit_deprecated_helper(OPLINE_D)
return 1;
}
bool ZEND_FASTCALL zend_jit_nodiscard_helper(OPLINE_D)
{
zend_execute_data *call = (zend_execute_data *) opline;
zend_function *fbc = call->func;
zend_nodiscard_function(fbc);
if (EG(exception)) {
#ifndef HAVE_GCC_GLOBAL_REGS
zend_execute_data *execute_data = EG(current_execute_data);
#endif
const zend_op *opline = EG(opline_before_exception);
if (opline && RETURN_VALUE_USED(opline)) {
ZVAL_UNDEF(EX_VAR(opline->result.var));
}
zend_vm_stack_free_args(call);
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) {
OBJ_RELEASE(Z_OBJ(call->This));
}
zend_vm_stack_free_call_frame(call);
return 0;
}
return 1;
}
bool ZEND_FASTCALL zend_jit_deprecated_nodiscard_helper(OPLINE_D)
{
zend_execute_data *call = (zend_execute_data *) opline;
zend_function *fbc = call->func;
if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
if (zend_jit_deprecated_helper(OPLINE_C) == 0) {
return 0;
}
}
if (fbc->common.fn_flags & ZEND_ACC_NODISCARD) {
if (zend_jit_nodiscard_helper(OPLINE_C) == 0) {
return 0;
}
}
return 1;
}
void ZEND_FASTCALL zend_jit_undefined_long_key(EXECUTE_DATA_D)
{
const zend_op *opline = EX(opline);

View file

@ -2732,6 +2732,7 @@ function proc_nice(int $priority): bool {}
* @param resource $stream
* @param int $would_block
*/
#[\NoDiscard(message: "as locking the stream might have failed")]
function flock($stream, int $operation, &$would_block = null): bool {}
/**

View file

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 85677dc3476d25b7820fd3a26fe39f2e9378b6e7 */
* Stub hash: 824ccb41163307bd0fad452b705a8222b6f42d09 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0)
@ -3205,7 +3205,7 @@ static const zend_function_entry ext_functions[] = {
#if defined(HAVE_NICE)
ZEND_FE(proc_nice, arginfo_proc_nice)
#endif
ZEND_FE(flock, arginfo_flock)
ZEND_RAW_FENTRY("flock", zif_flock, arginfo_flock, ZEND_ACC_NODISCARD, NULL, NULL)
ZEND_FE(get_meta_tags, arginfo_get_meta_tags)
ZEND_FE(pclose, arginfo_pclose)
ZEND_FE(popen, arginfo_popen)
@ -4039,6 +4039,15 @@ static void register_basic_functions_symbols(int module_number)
ZVAL_COPY_VALUE(&attribute_Deprecated_func_utf8_decode_0->args[1].value, &attribute_Deprecated_func_utf8_decode_0_arg1);
attribute_Deprecated_func_utf8_decode_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
zend_string *attribute_name_NoDiscard_func_flock_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
zend_attribute *attribute_NoDiscard_func_flock_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "flock", sizeof("flock") - 1), attribute_name_NoDiscard_func_flock_0, 1);
zend_string_release(attribute_name_NoDiscard_func_flock_0);
zval attribute_NoDiscard_func_flock_0_arg0;
zend_string *attribute_NoDiscard_func_flock_0_arg0_str = zend_string_init("as locking the stream might have failed", strlen("as locking the stream might have failed"), 1);
ZVAL_STR(&attribute_NoDiscard_func_flock_0_arg0, attribute_NoDiscard_func_flock_0_arg0_str);
ZVAL_COPY_VALUE(&attribute_NoDiscard_func_flock_0->args[0].value, &attribute_NoDiscard_func_flock_0_arg0);
attribute_NoDiscard_func_flock_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "password_hash", sizeof("password_hash") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "password_verify", sizeof("password_verify") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);

View file

@ -19,7 +19,7 @@ class test_wrapper extends test_wrapper_base {
}
function test($name, $fd, $mode) {
echo "------ $name: -------\n";
flock($fd, $mode);
(void)flock($fd, $mode);
$data = stream_get_meta_data($fd);
var_dump($data['wrapper_data']->mode === $mode);
}

View file

@ -124,6 +124,13 @@ static ZEND_FUNCTION(zend_test_deprecated_attr)
ZEND_PARSE_PARAMETERS_NONE();
}
static ZEND_FUNCTION(zend_test_deprecated_nodiscard)
{
ZEND_PARSE_PARAMETERS_NONE();
RETURN_LONG(1);
}
/* Create a string without terminating null byte. Must be terminated with
* zend_terminate_string() before destruction, otherwise a warning is issued
* in debug builds. */

View file

@ -218,6 +218,11 @@ namespace {
#[\Deprecated(message: "custom message")]
function zend_test_deprecated_attr(): void {}
#[\Deprecated(message: "custom message")]
#[\NoDiscard(message: "custom message 2")]
function zend_test_deprecated_nodiscard(): int {}
/** @alias zend_test_void_return */
function zend_test_aliased(): void {}

View file

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 80b2dbc373baccd5ee4df047070d95e4c44effcd */
* Stub hash: eebe535d0295f707201ff751e38a5ad3837dbbd2 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0)
ZEND_END_ARG_INFO()
@ -22,6 +22,9 @@ ZEND_END_ARG_INFO()
#define arginfo_zend_test_deprecated_attr arginfo_zend_test_void_return
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_deprecated_nodiscard, 0, 0, IS_LONG, 0)
ZEND_END_ARG_INFO()
#define arginfo_zend_test_aliased arginfo_zend_test_void_return
#define arginfo_zend_test_deprecated_aliased arginfo_zend_test_void_return
@ -128,8 +131,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_is_string_marked_as_va
ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_get_map_ptr_last, 0, 0, IS_LONG, 0)
ZEND_END_ARG_INFO()
#define arginfo_zend_get_map_ptr_last arginfo_zend_test_deprecated_nodiscard
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_crash, 0, 0, IS_VOID, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null")
@ -187,7 +189,7 @@ ZEND_END_ARG_INFO()
#define arginfo_ZendTestNS2_ZendSubNS_namespaced_deprecated_aliased_func arginfo_zend_test_void_return
#define arginfo_class__ZendTestClass_is_object arginfo_zend_get_map_ptr_last
#define arginfo_class__ZendTestClass_is_object arginfo_zend_test_deprecated_nodiscard
#define arginfo_class__ZendTestClass___toString arginfo_zend_get_current_func_name
@ -262,6 +264,7 @@ static ZEND_FUNCTION(zend_test_void_return);
static ZEND_FUNCTION(zend_test_compile_string);
static ZEND_FUNCTION(zend_test_deprecated);
static ZEND_FUNCTION(zend_test_deprecated_attr);
static ZEND_FUNCTION(zend_test_deprecated_nodiscard);
static ZEND_FUNCTION(zend_create_unterminated_string);
static ZEND_FUNCTION(zend_terminate_string);
static ZEND_FUNCTION(zend_leak_variable);
@ -358,6 +361,11 @@ static const zend_function_entry ext_functions[] = {
#else
ZEND_RAW_FENTRY("zend_test_deprecated_attr", zif_zend_test_deprecated_attr, arginfo_zend_test_deprecated_attr, ZEND_ACC_DEPRECATED)
#endif
#if (PHP_VERSION_ID >= 80400)
ZEND_RAW_FENTRY("zend_test_deprecated_nodiscard", zif_zend_test_deprecated_nodiscard, arginfo_zend_test_deprecated_nodiscard, ZEND_ACC_DEPRECATED|ZEND_ACC_NODISCARD, NULL, NULL)
#else
ZEND_RAW_FENTRY("zend_test_deprecated_nodiscard", zif_zend_test_deprecated_nodiscard, arginfo_zend_test_deprecated_nodiscard, ZEND_ACC_DEPRECATED|ZEND_ACC_NODISCARD)
#endif
#if (PHP_VERSION_ID >= 80400)
ZEND_RAW_FENTRY("zend_test_aliased", zif_zend_test_void_return, arginfo_zend_test_aliased, 0, NULL, NULL)
#else
@ -564,6 +572,24 @@ static void register_test_symbols(int module_number)
ZVAL_COPY_VALUE(&attribute_Deprecated_func_zend_test_deprecated_attr_0->args[0].value, &attribute_Deprecated_func_zend_test_deprecated_attr_0_arg0);
attribute_Deprecated_func_zend_test_deprecated_attr_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
zend_string *attribute_name_Deprecated_func_zend_test_deprecated_nodiscard_0 = zend_string_init_interned("Deprecated", sizeof("Deprecated") - 1, 1);
zend_attribute *attribute_Deprecated_func_zend_test_deprecated_nodiscard_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "zend_test_deprecated_nodiscard", sizeof("zend_test_deprecated_nodiscard") - 1), attribute_name_Deprecated_func_zend_test_deprecated_nodiscard_0, 1);
zend_string_release(attribute_name_Deprecated_func_zend_test_deprecated_nodiscard_0);
zval attribute_Deprecated_func_zend_test_deprecated_nodiscard_0_arg0;
zend_string *attribute_Deprecated_func_zend_test_deprecated_nodiscard_0_arg0_str = zend_string_init("custom message", strlen("custom message"), 1);
ZVAL_STR(&attribute_Deprecated_func_zend_test_deprecated_nodiscard_0_arg0, attribute_Deprecated_func_zend_test_deprecated_nodiscard_0_arg0_str);
ZVAL_COPY_VALUE(&attribute_Deprecated_func_zend_test_deprecated_nodiscard_0->args[0].value, &attribute_Deprecated_func_zend_test_deprecated_nodiscard_0_arg0);
attribute_Deprecated_func_zend_test_deprecated_nodiscard_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
zend_string *attribute_name_NoDiscard_func_zend_test_deprecated_nodiscard_1 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
zend_attribute *attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "zend_test_deprecated_nodiscard", sizeof("zend_test_deprecated_nodiscard") - 1), attribute_name_NoDiscard_func_zend_test_deprecated_nodiscard_1, 1);
zend_string_release(attribute_name_NoDiscard_func_zend_test_deprecated_nodiscard_1);
zval attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1_arg0;
zend_string *attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1_arg0_str = zend_string_init("custom message 2", strlen("custom message 2"), 1);
ZVAL_STR(&attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1_arg0, attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1_arg0_str);
ZVAL_COPY_VALUE(&attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1->args[0].value, &attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1_arg0);
attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
zend_string *attribute_name_ZendTestParameterAttribute_func_zend_test_parameter_with_attribute_arg0_0 = zend_string_init_interned("ZendTestParameterAttribute", sizeof("ZendTestParameterAttribute") - 1, 1);
zend_attribute *attribute_ZendTestParameterAttribute_func_zend_test_parameter_with_attribute_arg0_0 = zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "zend_test_parameter_with_attribute", sizeof("zend_test_parameter_with_attribute") - 1), 0, attribute_name_ZendTestParameterAttribute_func_zend_test_parameter_with_attribute_arg0_0, 1);
zend_string_release(attribute_name_ZendTestParameterAttribute_func_zend_test_parameter_with_attribute_arg0_0);