From 995e3d3eb6b7632f02a26604c478a992f778dfa4 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 24 Sep 2024 17:58:22 +0100 Subject: [PATCH 1/3] ext/standard: Add tests for Directory class --- .../DirectoryClass_cannot_clone.phpt | 28 +++++++++++++++++ .../DirectoryClass_cannot_construct.phpt | 20 +++++++++++++ .../DirectoryClass_cannot_serialize.phpt | 16 ++++++++++ .../directory/DirectoryClass_error_001.phpt | 27 ----------------- .../DirectoryClass_readonly_handle.phpt | 26 ++++++++++++++++ .../DirectoryClass_readonly_path.phpt | 26 ++++++++++++++++ ...flection_create_instance_no_construct.phpt | 30 +++++++++++++++++++ ... DirectoryClass_reflection_structure.phpt} | 18 ----------- 8 files changed, 146 insertions(+), 45 deletions(-) create mode 100644 ext/standard/tests/directory/DirectoryClass_cannot_clone.phpt create mode 100644 ext/standard/tests/directory/DirectoryClass_cannot_construct.phpt create mode 100644 ext/standard/tests/directory/DirectoryClass_cannot_serialize.phpt delete mode 100644 ext/standard/tests/directory/DirectoryClass_error_001.phpt create mode 100644 ext/standard/tests/directory/DirectoryClass_readonly_handle.phpt create mode 100644 ext/standard/tests/directory/DirectoryClass_readonly_path.phpt create mode 100644 ext/standard/tests/directory/DirectoryClass_reflection_create_instance_no_construct.phpt rename ext/standard/tests/directory/{DirectoryClass_basic_001.phpt => DirectoryClass_reflection_structure.phpt} (73%) diff --git a/ext/standard/tests/directory/DirectoryClass_cannot_clone.phpt b/ext/standard/tests/directory/DirectoryClass_cannot_clone.phpt new file mode 100644 index 0000000000000..7e521f4bf3131 --- /dev/null +++ b/ext/standard/tests/directory/DirectoryClass_cannot_clone.phpt @@ -0,0 +1,28 @@ +--TEST-- +Cannot serialize instance of Directory class constructed via Reflection. +--FILE-- +read()){ + $cloned_files[] = $row; + } + var_dump(count($cloned_files)); + echo "Using original object:\n"; + $original_files = []; + while ($row = $d->read()){ + $original_files[] = $row; + } + var_dump(count($original_files)); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +int(17) +Using original object: +int(0) diff --git a/ext/standard/tests/directory/DirectoryClass_cannot_construct.phpt b/ext/standard/tests/directory/DirectoryClass_cannot_construct.phpt new file mode 100644 index 0000000000000..800e2904235d7 --- /dev/null +++ b/ext/standard/tests/directory/DirectoryClass_cannot_construct.phpt @@ -0,0 +1,20 @@ +--TEST-- +Cannot directly instantiate Directory class. +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +object(Directory)#1 (0) { + ["path"]=> + uninitialized(string) + ["handle"]=> + uninitialized(mixed) +} diff --git a/ext/standard/tests/directory/DirectoryClass_cannot_serialize.phpt b/ext/standard/tests/directory/DirectoryClass_cannot_serialize.phpt new file mode 100644 index 0000000000000..b2e7caa258bf4 --- /dev/null +++ b/ext/standard/tests/directory/DirectoryClass_cannot_serialize.phpt @@ -0,0 +1,16 @@ +--TEST-- +Cannot serialize instance of Directory class constructed via Reflection. +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +string(%d) "O:9:"Directory":2:{s:4:"path";s:%d:"%s";s:6:"handle";i:%d;}" diff --git a/ext/standard/tests/directory/DirectoryClass_error_001.phpt b/ext/standard/tests/directory/DirectoryClass_error_001.phpt deleted file mode 100644 index 251c6b64885f5..0000000000000 --- a/ext/standard/tests/directory/DirectoryClass_error_001.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -Changing Directory::$handle property ---FILE-- -handle = "Havoc!"; -} catch (Error $e) { - echo $e->getMessage(), "\n"; -} -var_dump($d->handle); - -$d = dir(getcwd()); -try { - unset($d->handle); -} catch (Error $e) { - echo $e->getMessage(), "\n"; -} -var_dump($d->handle); - -?> ---EXPECTF-- -Cannot modify readonly property Directory::$handle -resource(%d) of type (stream) -Cannot unset readonly property Directory::$handle -resource(%d) of type (stream) diff --git a/ext/standard/tests/directory/DirectoryClass_readonly_handle.phpt b/ext/standard/tests/directory/DirectoryClass_readonly_handle.phpt new file mode 100644 index 0000000000000..f3f70a6b22a16 --- /dev/null +++ b/ext/standard/tests/directory/DirectoryClass_readonly_handle.phpt @@ -0,0 +1,26 @@ +--TEST-- +Changing Directory::$handle property +--FILE-- +handle = "Havoc!"; +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +var_dump($d->handle); + +try { + unset($d->handle); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +var_dump($d->handle); + +?> +--EXPECTF-- +Error: Cannot modify readonly property Directory::$handle +resource(%d) of type (stream) +Error: Cannot unset readonly property Directory::$handle +resource(%d) of type (stream) diff --git a/ext/standard/tests/directory/DirectoryClass_readonly_path.phpt b/ext/standard/tests/directory/DirectoryClass_readonly_path.phpt new file mode 100644 index 0000000000000..1dcf8ac8e5225 --- /dev/null +++ b/ext/standard/tests/directory/DirectoryClass_readonly_path.phpt @@ -0,0 +1,26 @@ +--TEST-- +Changing Directory::$handle property +--FILE-- +path = "Havoc!"; +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +var_dump($d->path == __DIR__); + +try { + unset($d->path); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +var_dump($d->path == __DIR__); + +?> +--EXPECTF-- +Error: Cannot modify readonly property Directory::$path +bool(true) +Error: Cannot unset readonly property Directory::$path +bool(true) diff --git a/ext/standard/tests/directory/DirectoryClass_reflection_create_instance_no_construct.phpt b/ext/standard/tests/directory/DirectoryClass_reflection_create_instance_no_construct.phpt new file mode 100644 index 0000000000000..35b07591635fd --- /dev/null +++ b/ext/standard/tests/directory/DirectoryClass_reflection_create_instance_no_construct.phpt @@ -0,0 +1,30 @@ +--TEST-- +Cannot use instance of Directory class constructed via Reflection. +--FILE-- +isInstantiable()); +try { + $d = $rc->newInstanceWithoutConstructor(); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +var_dump($d); +try { + var_dump($d->read()); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +bool(true) +object(Directory)#2 (0) { + ["path"]=> + uninitialized(string) + ["handle"]=> + uninitialized(mixed) +} +Error: Unable to find my handle property diff --git a/ext/standard/tests/directory/DirectoryClass_basic_001.phpt b/ext/standard/tests/directory/DirectoryClass_reflection_structure.phpt similarity index 73% rename from ext/standard/tests/directory/DirectoryClass_basic_001.phpt rename to ext/standard/tests/directory/DirectoryClass_reflection_structure.phpt index c345ea30b84b4..549b6c870ebf1 100644 --- a/ext/standard/tests/directory/DirectoryClass_basic_001.phpt +++ b/ext/standard/tests/directory/DirectoryClass_reflection_structure.phpt @@ -11,16 +11,6 @@ echo "Structure of Directory class:\n"; $rc = new ReflectionClass("Directory"); echo $rc; -echo "Cannot instantiate a valid Directory directly:\n"; -$d = new Directory(getcwd()); -var_dump($d); - -try { - var_dump($d->read()); -} catch (\Error $e) { - echo $e->getMessage() . "\n"; -} - ?> --EXPECTF-- Structure of Directory class: @@ -63,11 +53,3 @@ Class [ class Directory ] { } } } -Cannot instantiate a valid Directory directly: -object(Directory)#%d (0) { - ["path"]=> - uninitialized(string) - ["handle"]=> - uninitialized(mixed) -} -Unable to find my handle property From a6d81e9e2346d58bcea8c9c6386703b9b66dde5e Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 24 Sep 2024 17:58:57 +0100 Subject: [PATCH 2/3] ext/standard/dir.c: Directory class should behave like other resource objects --- ext/standard/dir.c | 13 +++++++++++++ ext/standard/dir.stub.php | 6 +++++- ext/standard/dir_arginfo.h | 4 ++-- .../directory/DirectoryClass_cannot_clone.phpt | 4 +--- .../directory/DirectoryClass_cannot_construct.phpt | 7 +------ .../directory/DirectoryClass_cannot_serialize.phpt | 4 ++-- .../DirectoryClass_reflection_structure.phpt | 2 +- 7 files changed, 25 insertions(+), 15 deletions(-) diff --git a/ext/standard/dir.c b/ext/standard/dir.c index 1af6efe211a7e..a2a50eab7f51d 100644 --- a/ext/standard/dir.c +++ b/ext/standard/dir.c @@ -48,10 +48,17 @@ php_dir_globals dir_globals; #endif static zend_class_entry *dir_class_entry_ptr; +static zend_object_handlers dir_class_object_handlers; #define Z_DIRECTORY_PATH_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 0) #define Z_DIRECTORY_HANDLE_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 1) +static zend_function *dir_class_get_constructor(zend_object *object) +{ + zend_throw_error(NULL, "Cannot directly construct Directory, use dir() instead"); + return NULL; +} + #define FETCH_DIRP() \ myself = getThis(); \ if (!myself) { \ @@ -115,6 +122,12 @@ PHP_MINIT_FUNCTION(dir) register_dir_symbols(module_number); dir_class_entry_ptr = register_class_Directory(); + dir_class_entry_ptr->default_object_handlers = &dir_class_object_handlers; + + memcpy(&dir_class_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + dir_class_object_handlers.get_constructor = dir_class_get_constructor; + dir_class_object_handlers.clone_obj = NULL; + dir_class_object_handlers.compare = zend_objects_not_comparable; #ifdef ZTS ts_allocate_id(&dir_globals_id, sizeof(php_dir_globals), NULL, NULL); diff --git a/ext/standard/dir.stub.php b/ext/standard/dir.stub.php index 6177d9fb590e6..1ceafdee0d9a0 100644 --- a/ext/standard/dir.stub.php +++ b/ext/standard/dir.stub.php @@ -87,7 +87,11 @@ */ const SCANDIR_SORT_NONE = UNKNOWN; -class Directory +/** + * @strict-properties + * @not-serializable + */ +final class Directory { public readonly string $path; diff --git a/ext/standard/dir_arginfo.h b/ext/standard/dir_arginfo.h index 8440c154e60b9..51c3f087d5c0b 100644 --- a/ext/standard/dir_arginfo.h +++ b/ext/standard/dir_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 4b0f093770ff9a6cad9db033e0b62b412408b937 */ + * Stub hash: 3fdc106d96cf9e728886637eecdb43c2552b174f */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Directory_close, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() @@ -58,7 +58,7 @@ static zend_class_entry *register_class_Directory(void) zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "Directory", class_Directory_methods); - class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); zval property_path_default_value; ZVAL_UNDEF(&property_path_default_value); diff --git a/ext/standard/tests/directory/DirectoryClass_cannot_clone.phpt b/ext/standard/tests/directory/DirectoryClass_cannot_clone.phpt index 7e521f4bf3131..c149ec4b1c718 100644 --- a/ext/standard/tests/directory/DirectoryClass_cannot_clone.phpt +++ b/ext/standard/tests/directory/DirectoryClass_cannot_clone.phpt @@ -23,6 +23,4 @@ try { ?> --EXPECT-- -int(17) -Using original object: -int(0) +Error: Trying to clone an uncloneable object of class Directory diff --git a/ext/standard/tests/directory/DirectoryClass_cannot_construct.phpt b/ext/standard/tests/directory/DirectoryClass_cannot_construct.phpt index 800e2904235d7..c0ae685b8e722 100644 --- a/ext/standard/tests/directory/DirectoryClass_cannot_construct.phpt +++ b/ext/standard/tests/directory/DirectoryClass_cannot_construct.phpt @@ -12,9 +12,4 @@ try { ?> --EXPECT-- -object(Directory)#1 (0) { - ["path"]=> - uninitialized(string) - ["handle"]=> - uninitialized(mixed) -} +Error: Cannot directly construct Directory, use dir() instead diff --git a/ext/standard/tests/directory/DirectoryClass_cannot_serialize.phpt b/ext/standard/tests/directory/DirectoryClass_cannot_serialize.phpt index b2e7caa258bf4..5dc6bbda8eb65 100644 --- a/ext/standard/tests/directory/DirectoryClass_cannot_serialize.phpt +++ b/ext/standard/tests/directory/DirectoryClass_cannot_serialize.phpt @@ -12,5 +12,5 @@ try { } ?> ---EXPECTF-- -string(%d) "O:9:"Directory":2:{s:4:"path";s:%d:"%s";s:6:"handle";i:%d;}" +--EXPECT-- +Exception: Serialization of 'Directory' is not allowed diff --git a/ext/standard/tests/directory/DirectoryClass_reflection_structure.phpt b/ext/standard/tests/directory/DirectoryClass_reflection_structure.phpt index 549b6c870ebf1..76576d6922d46 100644 --- a/ext/standard/tests/directory/DirectoryClass_reflection_structure.phpt +++ b/ext/standard/tests/directory/DirectoryClass_reflection_structure.phpt @@ -14,7 +14,7 @@ echo $rc; ?> --EXPECTF-- Structure of Directory class: -Class [ class Directory ] { +Class [ final class Directory ] { - Constants [0] { } From 3156bf74539f476bd7ce4a367f8cf1dc2dbe33a3 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sun, 27 Oct 2024 10:36:17 +0000 Subject: [PATCH 3/3] ext/standard: Transform tentative return types into proper types for Directory class This class is now final --- ext/standard/dir.stub.php | 3 --- ext/standard/dir_arginfo.h | 6 +++--- .../directory/DirectoryClass_reflection_structure.phpt | 10 +++++----- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/ext/standard/dir.stub.php b/ext/standard/dir.stub.php index 1ceafdee0d9a0..457a965352516 100644 --- a/ext/standard/dir.stub.php +++ b/ext/standard/dir.stub.php @@ -99,19 +99,16 @@ final class Directory public readonly mixed $handle; /** - * @tentative-return-type * @implementation-alias closedir */ public function close(): void {} /** - * @tentative-return-type * @implementation-alias rewinddir */ public function rewind(): void {} /** - * @tentative-return-type * @implementation-alias readdir */ public function read(): string|false {} diff --git a/ext/standard/dir_arginfo.h b/ext/standard/dir_arginfo.h index 51c3f087d5c0b..9b293f3cd753f 100644 --- a/ext/standard/dir_arginfo.h +++ b/ext/standard/dir_arginfo.h @@ -1,12 +1,12 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3fdc106d96cf9e728886637eecdb43c2552b174f */ + * Stub hash: 069117bab1b9502faf516307aa7e80308f7b7f13 */ -ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Directory_close, 0, 0, IS_VOID, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Directory_close, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() #define arginfo_class_Directory_rewind arginfo_class_Directory_close -ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_Directory_read, 0, 0, MAY_BE_STRING|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Directory_read, 0, 0, MAY_BE_STRING|MAY_BE_FALSE) ZEND_END_ARG_INFO() ZEND_FUNCTION(closedir); diff --git a/ext/standard/tests/directory/DirectoryClass_reflection_structure.phpt b/ext/standard/tests/directory/DirectoryClass_reflection_structure.phpt index 76576d6922d46..a8052602b4243 100644 --- a/ext/standard/tests/directory/DirectoryClass_reflection_structure.phpt +++ b/ext/standard/tests/directory/DirectoryClass_reflection_structure.phpt @@ -12,9 +12,9 @@ $rc = new ReflectionClass("Directory"); echo $rc; ?> ---EXPECTF-- +--EXPECT-- Structure of Directory class: -Class [ final class Directory ] { +Class [ final class Directory ] { - Constants [0] { } @@ -35,21 +35,21 @@ Class [ final class Directory ] { - Parameters [0] { } - - Tentative return [ void ] + - Return [ void ] } Method [ public method rewind ] { - Parameters [0] { } - - Tentative return [ void ] + - Return [ void ] } Method [ public method read ] { - Parameters [0] { } - - Tentative return [ string|false ] + - Return [ string|false ] } } }