Skip to content
Open
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
63 changes: 63 additions & 0 deletions Zend/tests/attributes/deprecated/functions/argument_binding.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
--TEST--
#[\Deprecated]: positional and named arguments, null, and the errors of the constructor
--FILE--
<?php

#[\Deprecated("message only")]
function positional_message() {}
#[\Deprecated(since: "1.0")]
function named_since() {}
#[\Deprecated("m", "1.0")]
function both_positional() {}
#[\Deprecated(since: "1.0", message: "m")]
function named_reversed() {}
#[\Deprecated(null, since: "1.0")]
function null_message() {}
#[\Deprecated("m", null)]
function null_since() {}
#[\Deprecated("", "")]
function empty_strings() {}
#[\Deprecated("a", message: "b")]
function positional_then_same_named() {}
#[\Deprecated(null, message: "b")]
function null_then_same_named() {}
#[\Deprecated(unknown: "x")]
function unknown_named() {}
#[\Deprecated(Message: "x")]
function wrong_case() {}
#[\Deprecated("a", "b", "c")]
function too_many() {}

foreach ([
'positional_message', 'named_since', 'both_positional', 'named_reversed', 'null_message',
'null_since', 'empty_strings', 'positional_then_same_named', 'null_then_same_named',
'unknown_named', 'wrong_case', 'too_many',
] as $f) {
try {
$f();
} catch (Throwable $e) {
echo get_class($e), ": ", $e->getMessage(), "\n";
}
}

?>
--EXPECTF--

Deprecated: Function positional_message() is deprecated, message only in %s on line %d

Deprecated: Function named_since() is deprecated since 1.0 in %s on line %d

Deprecated: Function both_positional() is deprecated since 1.0, m in %s on line %d

Deprecated: Function named_reversed() is deprecated since 1.0, m in %s on line %d

Deprecated: Function null_message() is deprecated since 1.0 in %s on line %d

Deprecated: Function null_since() is deprecated, m in %s on line %d

Deprecated: Function empty_strings() is deprecated in %s on line %d
Error: Named parameter $message overwrites previous argument
Error: Named parameter $message overwrites previous argument
Error: Unknown named parameter $unknown
Error: Unknown named parameter $Message
ArgumentCountError: Deprecated::__construct() expects at most 2 arguments, 3 given
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
--TEST--
#[\Deprecated]: the message of an internal function is the same on every call
--FILE--
<?php

$rp = new ReflectionProperty(new class { public $p = 1; }, 'p');

$rp->setAccessible(true);
$rp->setAccessible(true);
(Closure::fromCallable([$rp, 'setAccessible']))(true);
(new ReflectionMethod($rp, 'setAccessible'))->invoke($rp, true);

set_error_handler(function (int $type, string $message) {
echo "handler: $message\n";
return true;
});
$rp->setAccessible(true);
restore_error_handler();
$rp->setAccessible(true);

?>
--EXPECTF--
Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d

Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d

Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d

Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d
handler: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1

Deprecated: Method ReflectionProperty::setAccessible() is deprecated since 8.5, as it has no effect since PHP 8.1 in %s on line %d
14 changes: 12 additions & 2 deletions Zend/tests/attributes/nodiscard/007.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,23 @@ zend_test.observer.execute_internal=1
--FILE--
<?php

define('MSG', 'from a constant');

#[\NoDiscard(message: MSG)]
function f() { return 1; }

zend_test_nodiscard();
f();

?>
--EXPECTF--
<!-- internal enter NoDiscard::__construct() -->
<!-- internal leave NoDiscard::__construct() -->
<!-- internal enter define() -->
<!-- internal leave define() -->

Warning: The return value of function zend_test_nodiscard() should either be used or intentionally ignored by casting it as (void), custom message in %s on line %d
<!-- internal enter zend_test_nodiscard() -->
<!-- internal leave zend_test_nodiscard() -->
<!-- internal enter NoDiscard::__construct() -->
<!-- internal leave NoDiscard::__construct() -->

Warning: The return value of function f() should either be used or intentionally ignored by casting it as (void), from a constant in %s on line %d
41 changes: 41 additions & 0 deletions Zend/tests/attributes/nodiscard/argument_binding.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
--TEST--
#[\NoDiscard]: positional and named message, null, and the errors of the constructor
--FILE--
<?php

#[\NoDiscard("positional")]
function positional_message() { return 1; }
#[\NoDiscard(message: "named")]
function named_message() { return 1; }
#[\NoDiscard(null)]
function null_message() { return 1; }
#[\NoDiscard("")]
function empty_message() { return 1; }
#[\NoDiscard(since: "1.0")]
function since_named() { return 1; }
#[\NoDiscard("a", "b")]
function too_many() { return 1; }
#[\NoDiscard(unknown: "x")]
function unknown_named() { return 1; }

foreach (['positional_message', 'named_message', 'null_message', 'empty_message', 'since_named', 'too_many', 'unknown_named'] as $f) {
try {
$f();
} catch (Throwable $e) {
echo get_class($e), ": ", $e->getMessage(), "\n";
}
}

?>
--EXPECTF--

Warning: The return value of function positional_message() should either be used or intentionally ignored by casting it as (void), positional in %s on line %d

Warning: The return value of function named_message() should either be used or intentionally ignored by casting it as (void), named in %s on line %d

Warning: The return value of function null_message() should either be used or intentionally ignored by casting it as (void) in %s on line %d

Warning: The return value of function empty_message() should either be used or intentionally ignored by casting it as (void) in %s on line %d
Error: Unknown named parameter $since
ArgumentCountError: NoDiscard::__construct() expects at most 1 argument, 2 given
Error: Unknown named parameter $unknown
53 changes: 53 additions & 0 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -1831,6 +1831,48 @@ ZEND_API zend_never_inline ZEND_COLD void zend_wrong_string_offset_error(void)
zend_throw_error(NULL, "%s", msg);
}

/* Reads the arguments of a #[\Deprecated] (message, since) or #[\NoDiscard] (message)
* attribute when they are strings or null, positional or named, each at most once; anything
* else is left to the constructor and its errors */
static bool get_attribute_string_args(const zend_attribute *attr, zend_string **message, zend_string **since)
{
zend_string *strs[2] = {NULL, NULL};
uint32_t num_params = since ? 2 : 1;

if (attr->argc > num_params) {
return false;
}
for (uint32_t i = 0; i < attr->argc; i++) {
const zend_attribute_arg *arg = &attr->args[i];
uint32_t param;

if (arg->name == NULL) {
param = i;
} else if (zend_string_equals(arg->name, ZSTR_KNOWN(ZEND_STR_MESSAGE))) {
param = 0;
} else if (num_params == 2 && zend_string_equals(arg->name, ZSTR_KNOWN(ZEND_STR_SINCE))) {
param = 1;
} else {
return false;
}
if (strs[param] != NULL) {
return false;
}
if (Z_TYPE(arg->value) == IS_STRING) {
strs[param] = Z_STR(arg->value);
} else if (Z_TYPE(arg->value) == IS_NULL) {
strs[param] = ZSTR_EMPTY_ALLOC();
} else {
return false;
}
}
*message = strs[0] ? strs[0] : ZSTR_EMPTY_ALLOC();
if (since) {
*since = strs[1] ? strs[1] : ZSTR_EMPTY_ALLOC();
}
return true;
}

ZEND_COLD static zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute(HashTable *attributes, zend_class_entry* scope, zend_string **message_suffix)
{
*message_suffix = ZSTR_EMPTY_ALLOC();
Expand Down Expand Up @@ -1858,6 +1900,11 @@ ZEND_COLD static zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute
ZVAL_UNDEF(&obj);
zval *z;

/* With an exception pending the constructor is not run and nothing is emitted; keep that */
if (!EG(exception) && get_attribute_string_args(deprecated, &message, &since)) {
goto build;
}

/* Construct the Deprecated object to correctly handle parameter processing. */
if (FAILURE == zend_get_attribute_object(&obj, zend_ce_deprecated, deprecated, scope, NULL)) {
goto out;
Expand All @@ -1877,6 +1924,7 @@ ZEND_COLD static zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute
since = Z_STR_P(z);
}

build:
/* Construct the suffix. */
*message_suffix = zend_strpprintf_unchecked(
0,
Expand Down Expand Up @@ -1948,6 +1996,10 @@ ZEND_COLD static zend_result ZEND_FASTCALL get_nodiscard_suffix_from_attribute(H
ZVAL_UNDEF(&obj);
zval *z;

if (!EG(exception) && get_attribute_string_args(nodiscard, &message, NULL)) {
goto build;
}

/* Construct the NoDiscard object to correctly handle parameter processing. */
if (FAILURE == zend_get_attribute_object(&obj, zend_ce_nodiscard, nodiscard, scope, NULL)) {
goto out;
Expand All @@ -1960,6 +2012,7 @@ ZEND_COLD static zend_result ZEND_FASTCALL get_nodiscard_suffix_from_attribute(H
message = Z_STR_P(z);
}

build:
/* Construct the suffix. */
*message_suffix = zend_strpprintf_unchecked(
0,
Expand Down
3 changes: 0 additions & 3 deletions ext/zend_test/tests/observer_sqlite_create_function.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ echo 'Done' . PHP_EOL;
<!-- init PDO::__construct() -->
<PDO::__construct>
</PDO::__construct>
<!-- init Deprecated::__construct() -->
<Deprecated::__construct>
</Deprecated::__construct>

Deprecated: Method PDO::sqliteCreateFunction() is deprecated since 8.5, use Pdo\Sqlite::createFunction() instead in %s on line %d
<!-- init PDO::sqliteCreateFunction() -->
Expand Down
Loading