Skip to content

Commit 8e48e3d

Browse files
committed
ext/standard/dir.c: Refactor implementation of Directory and dir functions
1 parent ebcd023 commit 8e48e3d

8 files changed

+157
-100
lines changed

ext/standard/dir.c

+145-79
Original file line numberDiff line numberDiff line change
@@ -59,39 +59,6 @@ static zend_function *dir_class_get_constructor(zend_object *object)
5959
return NULL;
6060
}
6161

62-
#define FETCH_DIRP() \
63-
myself = getThis(); \
64-
if (!myself) { \
65-
ZEND_PARSE_PARAMETERS_START(0, 1) \
66-
Z_PARAM_OPTIONAL \
67-
Z_PARAM_RESOURCE_OR_NULL(id) \
68-
ZEND_PARSE_PARAMETERS_END(); \
69-
if (id) { \
70-
if ((dirp = (php_stream *)zend_fetch_resource(Z_RES_P(id), "Directory", php_file_le_stream())) == NULL) { \
71-
RETURN_THROWS(); \
72-
} \
73-
} else { \
74-
if (!DIRG(default_dir)) { \
75-
zend_type_error("No resource supplied"); \
76-
RETURN_THROWS(); \
77-
} \
78-
if ((dirp = (php_stream *)zend_fetch_resource(DIRG(default_dir), "Directory", php_file_le_stream())) == NULL) { \
79-
RETURN_THROWS(); \
80-
} \
81-
} \
82-
} else { \
83-
ZEND_PARSE_PARAMETERS_NONE(); \
84-
zval *handle_zv = Z_DIRECTORY_HANDLE_P(myself); \
85-
if (Z_TYPE_P(handle_zv) != IS_RESOURCE) { \
86-
zend_throw_error(NULL, "Unable to find my handle property"); \
87-
RETURN_THROWS(); \
88-
} \
89-
if ((dirp = (php_stream *)zend_fetch_resource_ex(handle_zv, "Directory", php_file_le_stream())) == NULL) { \
90-
RETURN_THROWS(); \
91-
} \
92-
}
93-
94-
9562
static void php_set_default_dir(zend_resource *res)
9663
{
9764
if (DIRG(default_dir)) {
@@ -189,21 +156,71 @@ PHP_FUNCTION(dir)
189156
}
190157
/* }}} */
191158

159+
160+
static php_stream* php_dir_get_directory_stream_from_user_arg(zval *arg)
161+
{
162+
zend_resource *res;
163+
if (arg == NULL) {
164+
if (UNEXPECTED(DIRG(default_dir) == NULL)) {
165+
zend_type_error("No resource supplied");
166+
return NULL;
167+
}
168+
res = DIRG(default_dir);
169+
} else {
170+
ZEND_ASSERT(Z_TYPE_P(arg) == IS_RESOURCE);
171+
res = Z_RES_P(arg);
172+
}
173+
174+
if (UNEXPECTED(res->type != php_file_le_stream())) {
175+
zend_argument_type_error(1, "must be a valid Directory resource");
176+
return NULL;
177+
}
178+
php_stream *dir_stream = (php_stream*) res->ptr;
179+
if (UNEXPECTED((dir_stream->flags & PHP_STREAM_FLAG_IS_DIR)) == 0) {
180+
zend_argument_type_error(1, "must be a valid Directory resource");
181+
return NULL;
182+
}
183+
return dir_stream;
184+
}
185+
186+
static php_stream* php_dir_get_directory_stream_from_this(zval *this_z)
187+
{
188+
zval *handle_zv = Z_DIRECTORY_HANDLE_P(this_z);
189+
if (UNEXPECTED(Z_TYPE_P(handle_zv) != IS_RESOURCE)) {
190+
zend_throw_error(NULL, "internal directory stream has been altered");
191+
return NULL;
192+
}
193+
zend_resource *res = Z_RES_P(handle_zv);
194+
/* Assume the close() method was called
195+
* (instead of the hacky case where a different resource would have been set via the ArrayObject "hack") */
196+
if (UNEXPECTED(res->type != php_file_le_stream())) {
197+
/* TypeError is used for BC, TODO: Use base Error in PHP 9 */
198+
zend_type_error("Directory::%s(): cannot use Directory resource after it has been closed", get_active_function_name());
199+
return NULL;
200+
}
201+
php_stream *dir_stream = (php_stream*) res->ptr;
202+
if (UNEXPECTED((dir_stream->flags & PHP_STREAM_FLAG_IS_DIR)) == 0) {
203+
zend_throw_error(NULL, "internal directory stream has been altered");
204+
return NULL;
205+
}
206+
return dir_stream;
207+
}
208+
192209
/* {{{ Close directory connection identified by the dir_handle */
193210
PHP_FUNCTION(closedir)
194211
{
195-
zval *id = NULL, *myself;
196-
php_stream *dirp;
197-
zend_resource *res;
212+
zval *id = NULL;
198213

199-
FETCH_DIRP();
214+
ZEND_PARSE_PARAMETERS_START(0, 1)
215+
Z_PARAM_OPTIONAL
216+
Z_PARAM_RESOURCE_OR_NULL(id)
217+
ZEND_PARSE_PARAMETERS_END();
200218

201-
if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
202-
zend_argument_type_error(1, "must be a valid Directory resource");
219+
php_stream *dirp = php_dir_get_directory_stream_from_user_arg(id);
220+
if (UNEXPECTED(dirp == NULL)) {
203221
RETURN_THROWS();
204222
}
205-
206-
res = dirp->res;
223+
zend_resource *res = dirp->res;
207224
zend_list_close(dirp->res);
208225

209226
if (res == DIRG(default_dir)) {
@@ -212,6 +229,93 @@ PHP_FUNCTION(closedir)
212229
}
213230
/* }}} */
214231

232+
PHP_METHOD(Directory, close)
233+
{
234+
ZEND_PARSE_PARAMETERS_NONE();
235+
236+
php_stream *dirp = php_dir_get_directory_stream_from_this(ZEND_THIS);
237+
if (UNEXPECTED(dirp == NULL)) {
238+
RETURN_THROWS();
239+
}
240+
241+
zend_resource *res = dirp->res;
242+
zend_list_close(res);
243+
244+
if (res == DIRG(default_dir)) {
245+
php_set_default_dir(NULL);
246+
}
247+
}
248+
249+
/* {{{ Rewind dir_handle back to the start */
250+
PHP_FUNCTION(rewinddir)
251+
{
252+
zval *id = NULL;
253+
254+
ZEND_PARSE_PARAMETERS_START(0, 1)
255+
Z_PARAM_OPTIONAL
256+
Z_PARAM_RESOURCE_OR_NULL(id)
257+
ZEND_PARSE_PARAMETERS_END();
258+
259+
php_stream *dirp = php_dir_get_directory_stream_from_user_arg(id);
260+
if (UNEXPECTED(dirp == NULL)) {
261+
RETURN_THROWS();
262+
}
263+
264+
php_stream_rewinddir(dirp);
265+
}
266+
/* }}} */
267+
268+
PHP_METHOD(Directory, rewind)
269+
{
270+
ZEND_PARSE_PARAMETERS_NONE();
271+
272+
php_stream *dirp = php_dir_get_directory_stream_from_this(ZEND_THIS);
273+
if (UNEXPECTED(dirp == NULL)) {
274+
RETURN_THROWS();
275+
}
276+
277+
php_stream_rewinddir(dirp);
278+
}
279+
280+
/* {{{ Read directory entry from dir_handle */
281+
PHP_FUNCTION(readdir)
282+
{
283+
zval *id = NULL;
284+
285+
ZEND_PARSE_PARAMETERS_START(0, 1)
286+
Z_PARAM_OPTIONAL
287+
Z_PARAM_RESOURCE_OR_NULL(id)
288+
ZEND_PARSE_PARAMETERS_END();
289+
290+
php_stream *dirp = php_dir_get_directory_stream_from_user_arg(id);
291+
if (UNEXPECTED(dirp == NULL)) {
292+
RETURN_THROWS();
293+
}
294+
295+
php_stream_dirent entry;
296+
if (php_stream_readdir(dirp, &entry)) {
297+
RETURN_STRINGL(entry.d_name, strlen(entry.d_name));
298+
}
299+
RETURN_FALSE;
300+
}
301+
/* }}} */
302+
303+
PHP_METHOD(Directory, read)
304+
{
305+
ZEND_PARSE_PARAMETERS_NONE();
306+
307+
php_stream *dirp = php_dir_get_directory_stream_from_this(ZEND_THIS);
308+
if (UNEXPECTED(dirp == NULL)) {
309+
RETURN_THROWS();
310+
}
311+
312+
php_stream_dirent entry;
313+
if (php_stream_readdir(dirp, &entry)) {
314+
RETURN_STRINGL(entry.d_name, strlen(entry.d_name));
315+
}
316+
RETURN_FALSE;
317+
}
318+
215319
#if defined(HAVE_CHROOT) && !defined(ZTS) && defined(ENABLE_CHROOT_FUNC)
216320
/* {{{ Change root directory */
217321
PHP_FUNCTION(chroot)
@@ -300,44 +404,6 @@ PHP_FUNCTION(getcwd)
300404
}
301405
/* }}} */
302406

303-
/* {{{ Rewind dir_handle back to the start */
304-
PHP_FUNCTION(rewinddir)
305-
{
306-
zval *id = NULL, *myself;
307-
php_stream *dirp;
308-
309-
FETCH_DIRP();
310-
311-
if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
312-
zend_argument_type_error(1, "must be a valid Directory resource");
313-
RETURN_THROWS();
314-
}
315-
316-
php_stream_rewinddir(dirp);
317-
}
318-
/* }}} */
319-
320-
/* {{{ Read directory entry from dir_handle */
321-
PHP_FUNCTION(readdir)
322-
{
323-
zval *id = NULL, *myself;
324-
php_stream *dirp;
325-
php_stream_dirent entry;
326-
327-
FETCH_DIRP();
328-
329-
if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
330-
zend_argument_type_error(1, "must be a valid Directory resource");
331-
RETURN_THROWS();
332-
}
333-
334-
if (php_stream_readdir(dirp, &entry)) {
335-
RETURN_STRINGL(entry.d_name, strlen(entry.d_name));
336-
}
337-
RETURN_FALSE;
338-
}
339-
/* }}} */
340-
341407
#ifdef HAVE_GLOB
342408
/* {{{ Find pathnames matching a pattern */
343409
PHP_FUNCTION(glob)

ext/standard/dir.stub.php

-9
Original file line numberDiff line numberDiff line change
@@ -98,18 +98,9 @@ final class Directory
9898
/** @var resource */
9999
public readonly mixed $handle;
100100

101-
/**
102-
* @implementation-alias closedir
103-
*/
104101
public function close(): void {}
105102

106-
/**
107-
* @implementation-alias rewinddir
108-
*/
109103
public function rewind(): void {}
110104

111-
/**
112-
* @implementation-alias readdir
113-
*/
114105
public function read(): string|false {}
115106
}

ext/standard/dir_arginfo.h

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/standard/tests/dir/closedir_variation2.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,5 @@ NULL
4141
Directory Handle: resource(%d) of type (Unknown)
4242

4343
-- Close directory handle second time: --
44-
closedir(): supplied resource is not a valid Directory resource
44+
closedir(): Argument #1 ($dir_handle) must be a valid Directory resource
4545
Directory Handle: resource(%d) of type (Unknown)

ext/standard/tests/dir/dir_basic.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,5 @@ object(Directory)#%d (2) {
7979
}
8080

8181
Test read after closing the dir:
82-
Directory::read(): supplied resource is not a valid Directory resource
82+
Directory::read(): cannot use Directory resource after it has been closed
8383
Done

ext/standard/tests/dir/rewinddir_variation2.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@ resource(%d) of type (stream)
3636
string(%d) "%s"
3737

3838
-- Call to rewinddir() --
39-
rewinddir(): supplied resource is not a valid Directory resource
39+
rewinddir(): Argument #1 ($dir_handle) must be a valid Directory resource

ext/standard/tests/directory/DirectoryClass_readonly_handle_by_pass_via_ArrayObject.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@ try {
3030
?>
3131
--EXPECT--
3232
resource(3) of type (stream)
33-
TypeError: Directory::read(): Argument #1 must be a valid Directory resource
33+
Error: internal directory stream has been altered
3434
Error: Typed property Directory::$handle must not be accessed before initialization

ext/standard/tests/directory/DirectoryClass_reflection_create_instance_no_construct.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ object(Directory)#2 (0) {
2727
["handle"]=>
2828
uninitialized(mixed)
2929
}
30-
Error: Unable to find my handle property
30+
Error: internal directory stream has been altered

0 commit comments

Comments
 (0)