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
5 changes: 2 additions & 3 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,8 @@ PHP NEWS
- PGSQL:
. Enabled 64 bits support for pg_lo_truncate()/pg_lo_tell()
if the server supports it. (KentarouTakeda)
. pg_fetch_object() now surfaces non-instantiable class errors
before fetching, resolves the constructor via the get_constructor
handler, and reports the empty-constructor ValueError on the
. pg_fetch_object() now surfaces non-instantiable class errors before
fetching, and reports the empty-constructor ValueError on the
$constructor_args argument. (David Carlier)

- Phar:
Expand Down
10 changes: 10 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ PHP 8.6 UPGRADE NOTES
. MessageFormatter::parse() and parseMessage() now return PHP_INT_MIN as
int, rather than float, on 64-bit platforms when parsing integer values.

- MySQLi:
. mysqli_fetch_object() no longer accepts classes with non-public
constructors.

- PCNTL:
. pcntl_alarm() now raises a ValueError if the seconds argument is
lower than zero or greater than platform's UINT_MAX.
Expand All @@ -54,6 +58,10 @@ PHP 8.6 UPGRADE NOTES
execution error occurs (e.g. malformed UTF-8 input with the /u modifier).
This is consistent with other preg_* functions.

- PDO:
. PDOStatement::fetchObject() no longer accepts classes with non-public
constructors.

- Phar:
. Phar::mungServer() now raises a ValueError when an invalid
argument value is passed instead of being silently ignored.
Expand All @@ -69,6 +77,8 @@ PHP 8.6 UPGRADE NOTES
$constructor_args argument instead of $class. Errors raised when
the requested class is not instantiable (abstract, interface, enum)
now surface before the row is fetched.
. pg_fetch_object() no longer accepts classes with non-public
constructors.

- Posix:
. posix_access() now raises a ValueError when an invalid $flags
Expand Down
4 changes: 4 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES
. Added ZEND_CONTAINER_OF().
. The OPENBASEDIR_CHECKPATH() compatibility macro has been removed, instead
use php_check_open_basedir() directly.
. The get_constructor object handler has been removed.
Instead to mark an internal class as not instantiable the new
#[\NonInstantiableClass("Reason")] attribute should be attached to the
class definition in the stubs.

========================
2. Build system changes
Expand Down
2 changes: 0 additions & 2 deletions Zend/Optimizer/escape_analysis.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i
if (ce
&& !ce->parent
&& !ce->create_object
&& ce->default_object_handlers->get_constructor == zend_std_get_constructor
&& ce->default_object_handlers->dtor_obj == zend_objects_destroy_object
&& !ce->constructor
&& !ce->destructor
Expand Down Expand Up @@ -234,7 +233,6 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va
script, op_array, opline);
if (ce
&& !ce->create_object
&& ce->default_object_handlers->get_constructor == zend_std_get_constructor
&& ce->default_object_handlers->dtor_obj == zend_objects_destroy_object
&& !ce->constructor
&& !ce->destructor
Expand Down
3 changes: 1 addition & 2 deletions Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -3394,8 +3394,7 @@ static zend_always_inline zend_result _zend_update_type_info(
/* New objects without constructors cannot escape. */
if (ce
&& !ce->constructor
&& !ce->create_object
&& ce->default_object_handlers->get_constructor == zend_std_get_constructor) {
&& !ce->create_object) {
tmp &= ~MAY_BE_RCN;
}
UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
Expand Down
11 changes: 11 additions & 0 deletions Zend/tests/attributes/noninstantiableclass/on_abstract_class.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
#[\NonInstantiableClass] cannot be applied to abstract class
--FILE--
<?php

#[\NonInstantiableClass("reason")]
abstract class Test {}

?>
--EXPECTF--
Fatal error: Cannot apply #[\NonInstantiableClass] to abstract class Test in %s on line %d
13 changes: 13 additions & 0 deletions Zend/tests/attributes/noninstantiableclass/on_class_const.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
#[\NonInstantiableClass] cannot be applied to class constant
--FILE--
<?php

class Test {
#[\NonInstantiableClass("reason")]
const FOO = 5;
}

?>
--EXPECTF--
Fatal error: Attribute "NonInstantiableClass" cannot target class constant (allowed targets: class) in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
#[\NonInstantiableClass] cannot be applied to class that has a constructor
--FILE--
<?php

#[\NonInstantiableClass("reason")]
class Test {
public function __construct() {}
}

var_dump(new Test());
?>
--EXPECTF--
Fatal error: Cannot apply #[\NonInstantiableClass] to a non-internal class Test in %s on line %d
11 changes: 11 additions & 0 deletions Zend/tests/attributes/noninstantiableclass/on_const.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
#[\NonInstantiableClass] cannot be applied to constant
--FILE--
<?php

#[\NonInstantiableClass("reason")]
const FOO = 5;

?>
--EXPECTF--
Fatal error: Attribute "NonInstantiableClass" cannot target constant (allowed targets: class) in %s on line %d
11 changes: 11 additions & 0 deletions Zend/tests/attributes/noninstantiableclass/on_enum.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
#[\NonInstantiableClass] cannot be applied to enum
--FILE--
<?php

#[\NonInstantiableClass("reason")]
enum Test {}

?>
--EXPECTF--
Fatal error: Cannot apply #[\NonInstantiableClass] to enum Test in %s on line %d
11 changes: 11 additions & 0 deletions Zend/tests/attributes/noninstantiableclass/on_function.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
#[\NonInstantiableClass] cannot be applied to function
--FILE--
<?php

#[\NonInstantiableClass("reason")]
function foo() {}

?>
--EXPECTF--
Fatal error: Attribute "NonInstantiableClass" cannot target function (allowed targets: class) in %s on line %d
11 changes: 11 additions & 0 deletions Zend/tests/attributes/noninstantiableclass/on_interface.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
#[\NonInstantiableClass] cannot be applied to interface
--FILE--
<?php

#[\NonInstantiableClass("reason")]
interface Test {}

?>
--EXPECTF--
Fatal error: Cannot apply #[\NonInstantiableClass] to interface Test in %s on line %d
13 changes: 13 additions & 0 deletions Zend/tests/attributes/noninstantiableclass/on_method.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
#[\NonInstantiableClass] cannot be applied to method
--FILE--
<?php

class Test {
#[\NonInstantiableClass("reason")]
public function foo() {}
}

?>
--EXPECTF--
Fatal error: Attribute "NonInstantiableClass" cannot target method (allowed targets: class) in %s on line %d
13 changes: 13 additions & 0 deletions Zend/tests/attributes/noninstantiableclass/on_property.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
#[\NonInstantiableClass] cannot be applied to property
--FILE--
<?php

class Test {
#[\NonInstantiableClass("reason")]
public $foo;
}

?>
--EXPECTF--
Fatal error: Attribute "NonInstantiableClass" cannot target property (allowed targets: class) in %s on line %d
11 changes: 11 additions & 0 deletions Zend/tests/attributes/noninstantiableclass/on_trait.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
#[\NonInstantiableClass] cannot be applied to trait
--FILE--
<?php

#[\NonInstantiableClass("reason")]
trait Test {}

?>
--EXPECTF--
Fatal error: Cannot apply #[\NonInstantiableClass] to trait Test in %s on line %d
8 changes: 2 additions & 6 deletions Zend/tests/closures/closure_instantiate.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,10 @@ Mark Baker mark@lange.demon.co.uk at the PHPNW2017 Conference for PHP Testfest 2
try {
// Closures should be instantiatable using new
$x = new Closure();
} catch (Exception $e) {
// Instantiating a closure is an error, not an exception, so we shouldn't see this
echo 'EXCEPTION: ', $e->getMessage();
} catch (Throwable $e) {
// This is the message that we should see for a caught error
echo 'ERROR: ', $e->getMessage();
echo $e::class, ': ', $e->getMessage();
}

?>
--EXPECT--
ERROR: Instantiation of class Closure is not allowed
Error: Instantiation of class Closure is not allowed
6 changes: 3 additions & 3 deletions Zend/tests/enum/no-new-through-reflection.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ enum Foo {}

try {
(new \ReflectionClass(Foo::class))->newInstanceWithoutConstructor();
} catch (Error $e) {
echo $e->getMessage() . "\n";
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage() . "\n";
}

?>
--EXPECT--
Cannot instantiate enum Foo
ReflectionException: Cannot instantiate enum Foo
2 changes: 1 addition & 1 deletion Zend/tests/traits/bug60173.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ $rc = new ReflectionClass('foo');
$rc->newInstance();
?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot instantiate trait foo in %s:%d
Fatal error: Uncaught ReflectionException: Cannot instantiate trait foo in %s:%d
Stack trace:
#0 %s(%d): ReflectionClass->newInstance()
#1 {main}
Expand Down
60 changes: 28 additions & 32 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "zend_hash.h"
#include "zend_modules.h"
#include "zend_extensions.h"
#include "zend_attributes.h"
#include "zend_constants.h"
#include "zend_interfaces.h"
#include "zend_exceptions.h"
Expand Down Expand Up @@ -1789,22 +1790,17 @@ ZEND_API void object_properties_load(zend_object *object, const HashTable *prope
* class and all props being public. If only a subset is given or the class
* has protected members then you need to merge the properties separately by
* calling zend_merge_properties(). */
static zend_always_inline zend_result _object_and_properties_init(zval *arg, zend_class_entry *class_type, HashTable *properties) /* {{{ */
{
if (UNEXPECTED(class_type->ce_flags & ZEND_ACC_UNINSTANTIABLE)) {
if (class_type->ce_flags & ZEND_ACC_INTERFACE) {
zend_throw_error(NULL, "Cannot instantiate interface %s", ZSTR_VAL(class_type->name));
} else if (class_type->ce_flags & ZEND_ACC_TRAIT) {
zend_throw_error(NULL, "Cannot instantiate trait %s", ZSTR_VAL(class_type->name));
} else if (class_type->ce_flags & ZEND_ACC_ENUM) {
zend_throw_error(NULL, "Cannot instantiate enum %s", ZSTR_VAL(class_type->name));
} else {
ZEND_ASSERT(class_type->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS));
zend_throw_error(NULL, "Cannot instantiate abstract class %s", ZSTR_VAL(class_type->name));
static zend_always_inline zend_result _object_and_properties_init(zval *arg, zend_class_entry *class_type, HashTable *properties, bool known_instantiable) /* {{{ */
{
if (!known_instantiable) {
if (UNEXPECTED(class_type->ce_flags & ZEND_ACC_UNINSTANTIABLE)) {
zend_cannot_instantiate_class(class_type, NULL);
ZVAL_NULL(arg);
Z_OBJ_P(arg) = NULL;
return FAILURE;
}
ZVAL_NULL(arg);
Z_OBJ_P(arg) = NULL;
return FAILURE;
} else {
ZEND_ASSERT((class_type->ce_flags & ZEND_ACC_UNINSTANTIABLE) == 0);
}

if (UNEXPECTED(!(class_type->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) {
Expand Down Expand Up @@ -1833,39 +1829,38 @@ static zend_always_inline zend_result _object_and_properties_init(zval *arg, zen

ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_entry *class_type, HashTable *properties) /* {{{ */
{
return _object_and_properties_init(arg, class_type, properties);
return _object_and_properties_init(arg, class_type, properties, false);
}
/* }}} */

ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *class_type) /* {{{ */
{
return _object_and_properties_init(arg, class_type, NULL);
return _object_and_properties_init(arg, class_type, NULL, false);
}
/* }}} */

ZEND_API zend_result object_init_instantiable_class(zval *arg, zend_class_entry *class_type) /* {{{ */
{
return _object_and_properties_init(arg, class_type, NULL, true);
}

ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *class_type, uint32_t param_count, zval *params, HashTable *named_params) /* {{{ */
{
zend_result status = _object_and_properties_init(arg, class_type, NULL);
if (UNEXPECTED(!zend_check_class_is_instantiable_or_throw(class_type, zend_get_executed_scope()))) {
ZVAL_UNDEF(arg);
return FAILURE;
}

zend_result status = _object_and_properties_init(arg, class_type, NULL, true);
if (UNEXPECTED(status == FAILURE)) {
ZVAL_UNDEF(arg);
return FAILURE;
}
zend_object *obj = Z_OBJ_P(arg);
zend_function *constructor = obj->handlers->get_constructor(obj);
if (constructor == NULL) {
/* The constructor can be NULL for 2 different reasons:
* - It is not defined
* - We are not allowed to call the constructor (e.g. private, or internal opaque class)
* and an exception has been thrown
* in the former case, we are (mostly) done and the object is initialized,
* in the latter we need to destroy the object as initialization failed
*/
if (UNEXPECTED(EG(exception))) {
zval_ptr_dtor(arg);
ZVAL_UNDEF(arg);
return FAILURE;
}

zend_function *constructor = class_type->constructor;
/* No constructor, so no need to call it */
if (constructor == NULL) {
/* Surprisingly, this is the only case where internal classes will allow to pass extra arguments
* However, if there are named arguments (and it is not empty),
* an Error must be thrown to be consistent with new ClassName() */
Expand All @@ -1884,6 +1879,7 @@ ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *c
return SUCCESS;
}
}

/* A constructor should not return a value, however if an exception is thrown
* zend_call_known_function() will set the retval to IS_UNDEF */
zval retval;
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ static zend_always_inline void array_init(zval *arg)

ZEND_API void object_init(zval *arg);
ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *ce);
ZEND_API zend_result object_init_instantiable_class(zval *arg, zend_class_entry *ce);
ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *class_type, uint32_t param_count, zval *params, HashTable *named_params);
ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_entry *ce, HashTable *properties);
ZEND_API void object_properties_init(zend_object *object, zend_class_entry *class_type);
Expand Down
14 changes: 8 additions & 6 deletions Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,10 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
return FAILURE;
}

if (!zend_is_class_instantiable(ce)) {
return FAILURE;
}

if (object_init_ex(result, ce) != SUCCESS) {
return FAILURE;
}
Expand Down Expand Up @@ -1096,10 +1100,9 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
}
}

zend_function *ctor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result));
if (ctor) {
if (ce->constructor) {
zend_call_known_function(
ctor, Z_OBJ_P(result), Z_OBJCE_P(result), NULL, 0, NULL, args);
ce->constructor, Z_OBJ_P(result), Z_OBJCE_P(result), NULL, 0, NULL, args);
}

zend_array_destroy(args);
Expand All @@ -1117,10 +1120,9 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
}
}

zend_function *ctor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result));
if (ctor) {
if (ce->constructor) {
zend_call_known_instance_method(
ctor, Z_OBJ_P(result), NULL, args_ast->children, args);
ce->constructor, Z_OBJ_P(result), NULL, args_ast->children, args);
}

for (uint32_t i = 0; i < args_ast->children; i++) {
Expand Down
Loading
Loading