Skip to content
Merged
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
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ PHP NEWS
been deprecated. (Girgias)
. The $exclude_disabled parameter of the get_defined_functions() function has
been deprecated, as it no longer has any effect since PHP 8.0. (Girgias)
. Terminating case statements with a semicolon instead of a colon has
been deprecated. (theodorejb)

- DOM:
. Fixed bug GH-18877 (\Dom\HTMLDocument querySelectorAll selecting only the
Expand Down Expand Up @@ -60,6 +62,8 @@ PHP NEWS
. The socket_set_timeout() alias function has been deprecated. (timwolla)
. Passing null to to readdir(), rewinddir(), and closedir() to use the last
opened directory has been deprecated. (Girgias)
. Fixed bug GH-19153 (#[\Attribute] validation should error on
trait/interface/enum/abstract class). (DanielEScherzer)

31 Jul 2025, PHP 8.5.0alpha4

Expand Down
12 changes: 9 additions & 3 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ PHP 8.5 UPGRADE NOTES
immediately, without calling user-defined error handlers.
. Exceptions thrown by user-defined error handlers when handling class linking
errors are not promoted to fatal errors anymore and do not prevent linking.
. Applying #[\Attribute] to an abstract class, enum, interface, or trait triggers
an error during compilation. Previously, the attribute could be added, but when
ReflectionAttribute::newInstance() was called an error would be thrown.

- DOM:
. Cloning a DOMNamedNodeMap, DOMNodeList, Dom\NamedNodeMap, Dom\NodeList,
Expand Down Expand Up @@ -322,6 +325,9 @@ PHP 8.5 UPGRADE NOTES
. The $exclude_disabled parameter of the get_defined_functions() function has
been deprecated, as it no longer has any effect since PHP 8.0.
RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_exclude_disabled_parameter_of_get_defined_functions
. Terminating case statements with a semicolon instead of a colon has
been deprecated.
RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_semicolon_after_case_in_switch_statement

- FileInfo:
. The finfo_close() function has been deprecated.
Expand Down Expand Up @@ -727,9 +733,9 @@ PHP 8.5 UPGRADE NOTES
showing misleading values in PHPInfo.

- OpenSSL:
Added openssl.libctx to select the OpenSSL library context type. Either
custom libctx for each thread can be used or a single global (default)
libctx is used.
. Added openssl.libctx to select the OpenSSL library context type. Either
custom libctx for each thread can be used or a single global (default)
libctx is used.

========================================
12. Windows Support
Expand Down
12 changes: 12 additions & 0 deletions Zend/tests/attributes/Attribute/Attribute_on_abstract.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
#[Attribute] on an abstract class
--FILE--
<?php

#[Attribute]
abstract class Demo {}

echo "Done\n";
?>
--EXPECTF--
Fatal error: Cannot apply #[\Attribute] to abstract class Demo in %s on line %d
12 changes: 12 additions & 0 deletions Zend/tests/attributes/Attribute/Attribute_on_enum.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
#[Attribute] on an enum
--FILE--
<?php

#[Attribute]
enum Demo {}

echo "Done\n";
?>
--EXPECTF--
Fatal error: Cannot apply #[\Attribute] to enum Demo in %s on line %d
12 changes: 12 additions & 0 deletions Zend/tests/attributes/Attribute/Attribute_on_interface.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
#[Attribute] on an interface
--FILE--
<?php

#[Attribute]
interface Demo {}

echo "Done\n";
?>
--EXPECTF--
Fatal error: Cannot apply #[\Attribute] to interface Demo in %s on line %d
12 changes: 12 additions & 0 deletions Zend/tests/attributes/Attribute/Attribute_on_trait.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
#[Attribute] on a trait
--FILE--
<?php

#[Attribute]
trait Demo {}

echo "Done\n";
?>
--EXPECTF--
Fatal error: Cannot apply #[\Attribute] to trait Demo in %s on line %d
19 changes: 19 additions & 0 deletions Zend/zend_attributes.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,24 @@ static void validate_allow_dynamic_properties(
scope->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES;
}

static void validate_attribute(
zend_attribute *attr, uint32_t target, zend_class_entry *scope)
{
const char *msg = NULL;
if (scope->ce_flags & ZEND_ACC_TRAIT) {
msg = "Cannot apply #[\\Attribute] to trait %s";
} else if (scope->ce_flags & ZEND_ACC_INTERFACE) {
msg = "Cannot apply #[\\Attribute] to interface %s";
} else if (scope->ce_flags & ZEND_ACC_ENUM) {
msg = "Cannot apply #[\\Attribute] to enum %s";
} else if (scope->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) {
msg = "Cannot apply #[\\Attribute] to abstract class %s";
}
if (msg != NULL) {
zend_error_noreturn(E_ERROR, msg, ZSTR_VAL(scope->name));
}
}

ZEND_METHOD(Attribute, __construct)
{
zend_long flags = ZEND_ATTRIBUTE_TARGET_ALL;
Expand Down Expand Up @@ -522,6 +540,7 @@ void zend_register_attribute_ce(void)

zend_ce_attribute = register_class_Attribute();
attr = zend_mark_internal_attribute(zend_ce_attribute);
attr->validator = validate_attribute;

zend_ce_return_type_will_change_attribute = register_class_ReturnTypeWillChange();
zend_mark_internal_attribute(zend_ce_return_type_will_change_attribute);
Expand Down
5 changes: 5 additions & 0 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -6313,6 +6313,11 @@ static void zend_compile_switch(zend_ast *ast) /* {{{ */
continue;
}

if (case_ast->attr == ZEND_ALT_CASE_SYNTAX) {
CG(zend_lineno) = case_ast->lineno;
zend_error(E_DEPRECATED, "Case statements followed by a semicolon (;) are deprecated, use a colon (:) instead");
}

zend_compile_expr(&cond_node, cond_ast);

if (expr_node.op_type == IS_CONST
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,7 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
((ZEND_TYPE_FULL_MASK((arg_info)->type) & _ZEND_IS_TENTATIVE_BIT) != 0)

#define ZEND_DIM_IS (1 << 0) /* isset fetch needed for null coalesce. Set in zend_compile.c for ZEND_AST_DIM nested within ZEND_AST_COALESCE. */
#define ZEND_ALT_CASE_SYNTAX (1 << 1) /* deprecated switch case terminated by semicolon */

/* Attributes for ${} encaps var in strings (ZEND_AST_DIM or ZEND_AST_VAR node) */
/* ZEND_AST_VAR nodes can have any of the ZEND_ENCAPS_VAR_* flags */
Expand Down
13 changes: 6 additions & 7 deletions Zend/zend_language_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -713,15 +713,14 @@ switch_case_list:

case_list:
%empty { $$ = zend_ast_create_list(0, ZEND_AST_SWITCH_LIST); }
| case_list T_CASE expr case_separator inner_statement_list
| case_list T_CASE expr ':' inner_statement_list
{ $$ = zend_ast_list_add($1, zend_ast_create(ZEND_AST_SWITCH_CASE, $3, $5)); }
| case_list T_DEFAULT case_separator inner_statement_list
| case_list T_CASE expr ';' inner_statement_list
{ $$ = zend_ast_list_add($1, zend_ast_create_ex(ZEND_AST_SWITCH_CASE, ZEND_ALT_CASE_SYNTAX, $3, $5)); }
| case_list T_DEFAULT ':' inner_statement_list
{ $$ = zend_ast_list_add($1, zend_ast_create(ZEND_AST_SWITCH_CASE, NULL, $4)); }
;

case_separator:
':'
| ';'
| case_list T_DEFAULT ';' inner_statement_list
{ $$ = zend_ast_list_add($1, zend_ast_create_ex(ZEND_AST_SWITCH_CASE, ZEND_ALT_CASE_SYNTAX, NULL, $4)); }
;


Expand Down
4 changes: 2 additions & 2 deletions ext/opcache/tests/issue0057.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ class ZException extends Exception {
function dummy($query) {
try {
switch ($query) {
case 1;
case 1:
break;
case 2;
case 2:
break;
default:
throw new Exception('exception');
Expand Down
36 changes: 18 additions & 18 deletions ext/openssl/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -2749,7 +2749,7 @@ PHP_FUNCTION(openssl_pkcs7_read)
goto clean_exit;
}

p7 = PEM_read_bio_PKCS7(bio_in, NULL, NULL, NULL);
p7 = php_openssl_pem_read_bio_pkcs7(bio_in);
if (p7 == NULL) {
php_openssl_store_errors();
goto clean_exit;
Expand Down Expand Up @@ -3071,19 +3071,19 @@ PHP_FUNCTION(openssl_cms_verify)

switch (encoding) {
case ENCODING_PEM:
cms = PEM_read_bio_CMS(sigbio, NULL, 0, NULL);
datain = in;
break;
case ENCODING_DER:
cms = d2i_CMS_bio(sigbio, NULL);
datain = in;
break;
case ENCODING_SMIME:
cms = SMIME_read_CMS(sigbio, &datain);
break;
default:
php_error_docref(NULL, E_WARNING, "Unknown encoding");
goto clean_exit;
cms = php_openssl_pem_read_bio_cms(sigbio);
datain = in;
break;
case ENCODING_DER:
cms = php_openssl_d2i_bio_cms(sigbio);
datain = in;
break;
case ENCODING_SMIME:
cms = php_openssl_smime_read_cms(sigbio, &datain);
break;
default:
php_error_docref(NULL, E_WARNING, "Unknown encoding");
goto clean_exit;
}
if (cms == NULL) {
php_openssl_store_errors();
Expand Down Expand Up @@ -3398,7 +3398,7 @@ PHP_FUNCTION(openssl_cms_read)
goto clean_exit;
}

cms = PEM_read_bio_CMS(bio_in, NULL, NULL, NULL);
cms = php_openssl_pem_read_bio_cms(bio_in);
if (cms == NULL) {
php_openssl_store_errors();
goto clean_exit;
Expand Down Expand Up @@ -3703,13 +3703,13 @@ PHP_FUNCTION(openssl_cms_decrypt)

switch (encoding) {
case ENCODING_DER:
cms = d2i_CMS_bio(in, NULL);
cms = php_openssl_d2i_bio_cms(in);
break;
case ENCODING_PEM:
cms = PEM_read_bio_CMS(in, NULL, 0, NULL);
cms = php_openssl_pem_read_bio_cms(in);
break;
case ENCODING_SMIME:
cms = SMIME_read_CMS(in, &datain);
cms = php_openssl_smime_read_cms(in, &datain);
break;
default:
zend_argument_value_error(5, "must be an OPENSSL_ENCODING_* constant");
Expand Down
18 changes: 7 additions & 11 deletions ext/openssl/openssl_backend_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -541,18 +541,14 @@ X509 *php_openssl_x509_from_str(
php_openssl_store_errors();
return NULL;
}
cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
cert = php_openssl_pem_read_bio_x509(in);
} else {
in = BIO_new_mem_buf(ZSTR_VAL(cert_str), (int) ZSTR_LEN(cert_str));
if (in == NULL) {
php_openssl_store_errors();
return NULL;
}
#ifdef TYPEDEF_D2I_OF
cert = (X509 *) PEM_ASN1_read_bio((d2i_of_void *)d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
#else
cert = (X509 *) PEM_ASN1_read_bio((char *(*)())d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
#endif
cert = php_openssl_pem_read_asn1_bio_x509(in);
}

if (!BIO_free(in)) {
Expand Down Expand Up @@ -1127,7 +1123,7 @@ X509_REQ *php_openssl_csr_from_str(zend_string *csr_str, uint32_t arg_num)
return NULL;
}

csr = PEM_read_bio_X509_REQ(in, NULL,NULL,NULL);
csr = php_openssl_pem_read_bio_x509_req(in);
if (csr == NULL) {
php_openssl_store_errors();
}
Expand Down Expand Up @@ -1158,7 +1154,7 @@ EVP_PKEY *php_openssl_extract_public_key(EVP_PKEY *priv_key)
return NULL;
}

EVP_PKEY *pub_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
EVP_PKEY *pub_key = php_openssl_pem_read_bio_public_key(bio);
BIO_free(bio);
return pub_key;
}
Expand Down Expand Up @@ -1290,7 +1286,7 @@ EVP_PKEY *php_openssl_pkey_from_zval(
zend_string_release_ex(val_str, false);
TMP_CLEAN;
}
key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL);
key = php_openssl_pem_read_bio_public_key(in);
BIO_free(in);
}
} else {
Expand All @@ -1308,12 +1304,12 @@ EVP_PKEY *php_openssl_pkey_from_zval(
TMP_CLEAN;
}
if (passphrase == NULL) {
key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
key = php_openssl_pem_read_bio_private_key(in, NULL, NULL);
} else {
struct php_openssl_pem_password password;
password.key = passphrase;
password.len = passphrase_len;
key = PEM_read_bio_PrivateKey(in, NULL, php_openssl_pem_password_cb, &password);
key = php_openssl_pem_read_bio_private_key(in, php_openssl_pem_password_cb, &password);
}
BIO_free(in);
}
Expand Down
45 changes: 45 additions & 0 deletions ext/openssl/openssl_backend_v1.c
Original file line number Diff line number Diff line change
Expand Up @@ -692,4 +692,49 @@ CONF *php_openssl_nconf_new(void)
return NCONF_new(NULL);
}

X509 *php_openssl_pem_read_asn1_bio_x509(BIO *in)
{
return PEM_ASN1_read_bio((d2i_of_void *)d2i_X509, PEM_STRING_X509, in, NULL, NULL, NULL);
}

X509 *php_openssl_pem_read_bio_x509(BIO *in)
{
return PEM_read_bio_X509(in, NULL, NULL, NULL);
}

X509_REQ *php_openssl_pem_read_bio_x509_req(BIO *in)
{
return PEM_read_bio_X509_REQ(in, NULL, NULL, NULL);
}

EVP_PKEY *php_openssl_pem_read_bio_public_key(BIO *in)
{
return PEM_read_bio_PUBKEY(in, NULL, NULL, NULL);
}

EVP_PKEY *php_openssl_pem_read_bio_private_key(BIO *in, pem_password_cb *cb, void *u)
{
return PEM_read_bio_PrivateKey(in, NULL, cb, u);
}

PKCS7 *php_openssl_pem_read_bio_pkcs7(BIO *in)
{
return PEM_read_bio_PKCS7(in, NULL, NULL, NULL);
}

CMS_ContentInfo *php_openssl_pem_read_bio_cms(BIO *in)
{
return PEM_read_bio_CMS(in, NULL, NULL, NULL);
}

CMS_ContentInfo *php_openssl_d2i_bio_cms(BIO *in)
{
return d2i_CMS_bio(in, NULL);
}

CMS_ContentInfo *php_openssl_smime_read_cms(BIO *bio, BIO **bcont)
{
return SMIME_read_CMS(bio, bcont);
}

#endif
Loading