Coverage Report

Created: 2026-09-14 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/standard/dir.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Author: Thies C. Arntzen <thies@thieso.net>                          |
12
   +----------------------------------------------------------------------+
13
 */
14
15
/* {{{ includes/startup/misc */
16
17
#include "php.h"
18
#include "fopen_wrappers.h"
19
#include "file.h"
20
#include "php_dir.h"
21
#include "php_dir_int.h"
22
#include "php_scandir.h"
23
#include "basic_functions.h"
24
#include "dir_arginfo.h"
25
26
#ifdef HAVE_UNISTD_H
27
#include <unistd.h>
28
#endif
29
30
#include <errno.h>
31
32
#ifdef PHP_WIN32
33
#include "win32/readdir.h"
34
#endif
35
36
typedef struct {
37
  zend_resource *default_dir;
38
} php_dir_globals;
39
40
#ifdef ZTS
41
#define DIRG(v) ZEND_TSRMG(dir_globals_id, php_dir_globals *, v)
42
int dir_globals_id;
43
#else
44
296k
#define DIRG(v) (dir_globals.v)
45
php_dir_globals dir_globals;
46
#endif
47
48
static zend_class_entry *dir_class_entry_ptr;
49
static zend_object_handlers dir_class_object_handlers;
50
51
#define Z_DIRECTORY_PATH_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 0)
52
0
#define Z_DIRECTORY_HANDLE_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 1)
53
54
static zend_function *dir_class_get_constructor(zend_object *object)
55
4
{
56
4
  zend_throw_error(NULL, "Cannot directly construct Directory, use dir() instead");
57
4
  return NULL;
58
4
}
59
60
static void php_set_default_dir(zend_resource *res)
61
374
{
62
374
  if (DIRG(default_dir)) {
63
182
    zend_list_delete(DIRG(default_dir));
64
182
  }
65
66
374
  if (res) {
67
192
    GC_ADDREF(res);
68
192
  }
69
70
374
  DIRG(default_dir) = res;
71
374
}
72
73
PHP_RINIT_FUNCTION(dir)
74
295k
{
75
295k
  DIRG(default_dir) = NULL;
76
295k
  return SUCCESS;
77
295k
}
78
79
PHP_MINIT_FUNCTION(dir)
80
16
{
81
16
  dirsep_str[0] = DEFAULT_SLASH;
82
16
  dirsep_str[1] = '\0';
83
84
16
  pathsep_str[0] = ZEND_PATHS_SEPARATOR;
85
16
  pathsep_str[1] = '\0';
86
87
16
  register_dir_symbols(module_number);
88
89
16
  dir_class_entry_ptr = register_class_Directory();
90
16
  dir_class_entry_ptr->default_object_handlers = &dir_class_object_handlers;
91
92
16
  memcpy(&dir_class_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
93
16
  dir_class_object_handlers.get_constructor = dir_class_get_constructor;
94
16
  dir_class_object_handlers.clone_obj = NULL;
95
16
  dir_class_object_handlers.compare = zend_objects_not_comparable;
96
97
#ifdef ZTS
98
  ts_allocate_id(&dir_globals_id, sizeof(php_dir_globals), NULL, NULL);
99
#endif
100
101
16
  return SUCCESS;
102
16
}
103
/* }}} */
104
105
/* {{{ internal functions */
106
static void _php_do_opendir(INTERNAL_FUNCTION_PARAMETERS, int createobject)
107
219
{
108
219
  char *dirname;
109
219
  size_t dir_len;
110
219
  zval *zcontext = NULL;
111
219
  php_stream_context *context = NULL;
112
219
  php_stream *dirp;
113
114
657
  ZEND_PARSE_PARAMETERS_START(1, 2)
115
876
    Z_PARAM_PATH(dirname, dir_len)
116
216
    Z_PARAM_OPTIONAL
117
432
    Z_PARAM_RESOURCE_OR_NULL(zcontext)
118
219
  ZEND_PARSE_PARAMETERS_END();
119
120
216
  context = php_stream_context_from_zval(zcontext, 0);
121
122
216
  dirp = php_stream_opendir(dirname, REPORT_ERRORS, context);
123
124
216
  if (dirp == NULL) {
125
24
    RETURN_FALSE;
126
24
  }
127
128
192
  dirp->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
129
130
192
  php_set_default_dir(dirp->res);
131
132
192
  if (createobject) {
133
0
    object_init_ex(return_value, dir_class_entry_ptr);
134
0
    ZVAL_STRINGL(Z_DIRECTORY_PATH_P(return_value), dirname, dir_len);
135
0
    ZVAL_RES(Z_DIRECTORY_HANDLE_P(return_value), dirp->res);
136
0
    php_stream_auto_cleanup(dirp); /* so we don't get warnings under debug */
137
192
  } else {
138
192
    php_stream_to_zval(dirp, return_value);
139
192
  }
140
192
}
141
/* }}} */
142
143
/* {{{ Open a directory and return a dir_handle */
144
PHP_FUNCTION(opendir)
145
212
{
146
212
  _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
147
212
}
148
/* }}} */
149
150
/* {{{ Directory class with properties, handle and class and methods read, rewind and close */
151
PHP_FUNCTION(dir)
152
7
{
153
7
  _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
154
7
}
155
/* }}} */
156
157
158
static php_stream* php_dir_get_directory_stream_from_user_arg(php_stream *dir_stream)
159
182
{
160
182
  if (dir_stream == NULL) {
161
0
    php_error_docref(NULL, E_DEPRECATED,
162
0
      "Passing null is deprecated, instead the last opened directory stream should be provided");
163
0
    if (UNEXPECTED(DIRG(default_dir) == NULL)) {
164
0
      zend_type_error("No resource supplied");
165
0
      return NULL;
166
0
    }
167
0
    zend_resource *res = DIRG(default_dir);
168
0
    ZEND_ASSERT(res->type == php_file_le_stream());
169
0
    dir_stream = (php_stream*) res->ptr;
170
0
  }
171
172
182
  if (UNEXPECTED((dir_stream->flags & PHP_STREAM_FLAG_IS_DIR)) == 0) {
173
0
    zend_argument_type_error(1, "must be a valid Directory resource");
174
0
    return NULL;
175
0
  }
176
182
  return dir_stream;
177
182
}
178
179
static php_stream* php_dir_get_directory_stream_from_this(zval *this_z)
180
0
{
181
0
  zval *handle_zv = Z_DIRECTORY_HANDLE_P(this_z);
182
0
  if (UNEXPECTED(Z_TYPE_P(handle_zv) != IS_RESOURCE)) {
183
0
    zend_throw_error(NULL, "Internal directory stream has been altered");
184
0
    return NULL;
185
0
  }
186
0
  zend_resource *res = Z_RES_P(handle_zv);
187
  /* Assume the close() method was called
188
   * (instead of the hacky case where a different resource would have been set via the ArrayObject "hack") */
189
0
  if (UNEXPECTED(res->type != php_file_le_stream())) {
190
    /* TypeError is used for BC, TODO: Use base Error in PHP 9 */
191
0
    zend_type_error("Directory::%s(): cannot use Directory resource after it has been closed", get_active_function_name());
192
0
    return NULL;
193
0
  }
194
0
  php_stream *dir_stream = (php_stream*) res->ptr;
195
0
  if (UNEXPECTED((dir_stream->flags & PHP_STREAM_FLAG_IS_DIR)) == 0) {
196
0
    zend_throw_error(NULL, "Internal directory stream has been altered");
197
0
    return NULL;
198
0
  }
199
0
  return dir_stream;
200
0
}
201
202
/* {{{ Close directory connection identified by the dir_handle */
203
PHP_FUNCTION(closedir)
204
182
{
205
182
  php_stream *dirp = NULL;
206
207
546
  ZEND_PARSE_PARAMETERS_START(0, 1)
208
546
    Z_PARAM_OPTIONAL
209
728
    PHP_Z_PARAM_STREAM_OR_NULL(dirp)
210
182
  ZEND_PARSE_PARAMETERS_END();
211
212
182
  dirp = php_dir_get_directory_stream_from_user_arg(dirp);
213
182
  if (UNEXPECTED(dirp == NULL)) {
214
0
    RETURN_THROWS();
215
0
  }
216
182
  zend_resource *res = dirp->res;
217
182
  zend_list_close(res);
218
219
182
  if (res == DIRG(default_dir)) {
220
182
    php_set_default_dir(NULL);
221
182
  }
222
182
}
223
/* }}} */
224
225
PHP_METHOD(Directory, close)
226
0
{
227
0
  ZEND_PARSE_PARAMETERS_NONE();
228
229
0
  php_stream *dirp = php_dir_get_directory_stream_from_this(ZEND_THIS);
230
0
  if (UNEXPECTED(dirp == NULL)) {
231
0
    RETURN_THROWS();
232
0
  }
233
234
0
  zend_resource *res = dirp->res;
235
0
  zend_list_close(res);
236
237
0
  if (res == DIRG(default_dir)) {
238
0
    php_set_default_dir(NULL);
239
0
  }
240
0
}
241
242
/* {{{ Rewind dir_handle back to the start */
243
PHP_FUNCTION(rewinddir)
244
0
{
245
0
  php_stream *dirp = NULL;
246
247
0
  ZEND_PARSE_PARAMETERS_START(0, 1)
248
0
    Z_PARAM_OPTIONAL
249
0
    PHP_Z_PARAM_STREAM_OR_NULL(dirp)
250
0
  ZEND_PARSE_PARAMETERS_END();
251
252
0
  dirp = php_dir_get_directory_stream_from_user_arg(dirp);
253
0
  if (UNEXPECTED(dirp == NULL)) {
254
0
    RETURN_THROWS();
255
0
  }
256
257
0
  php_stream_rewinddir(dirp);
258
0
}
259
/* }}} */
260
261
PHP_METHOD(Directory, rewind)
262
0
{
263
0
  ZEND_PARSE_PARAMETERS_NONE();
264
265
0
  php_stream *dirp = php_dir_get_directory_stream_from_this(ZEND_THIS);
266
0
  if (UNEXPECTED(dirp == NULL)) {
267
0
    RETURN_THROWS();
268
0
  }
269
270
0
  php_stream_rewinddir(dirp);
271
0
}
272
273
/* {{{ Read directory entry from dir_handle */
274
PHP_FUNCTION(readdir)
275
0
{
276
0
  php_stream *dirp = NULL;
277
278
0
  ZEND_PARSE_PARAMETERS_START(0, 1)
279
0
    Z_PARAM_OPTIONAL
280
0
    PHP_Z_PARAM_STREAM_OR_NULL(dirp)
281
0
  ZEND_PARSE_PARAMETERS_END();
282
283
0
  dirp = php_dir_get_directory_stream_from_user_arg(dirp);
284
0
  if (UNEXPECTED(dirp == NULL)) {
285
0
    RETURN_THROWS();
286
0
  }
287
288
0
  php_stream_dirent entry;
289
0
  if (php_stream_readdir(dirp, &entry)) {
290
0
    RETURN_STRING(entry.d_name);
291
0
  }
292
0
  RETURN_FALSE;
293
0
}
294
/* }}} */
295
296
PHP_METHOD(Directory, read)
297
0
{
298
0
  ZEND_PARSE_PARAMETERS_NONE();
299
300
0
  php_stream *dirp = php_dir_get_directory_stream_from_this(ZEND_THIS);
301
0
  if (UNEXPECTED(dirp == NULL)) {
302
0
    RETURN_THROWS();
303
0
  }
304
305
0
  php_stream_dirent entry;
306
0
  if (php_stream_readdir(dirp, &entry)) {
307
0
    RETURN_STRING(entry.d_name);
308
0
  }
309
0
  RETURN_FALSE;
310
0
}
311
312
#if defined(HAVE_CHROOT) && !defined(ZTS) && defined(ENABLE_CHROOT_FUNC)
313
/* {{{ Change root directory */
314
PHP_FUNCTION(chroot)
315
{
316
  char *str;
317
  int ret;
318
  size_t str_len;
319
320
  ZEND_PARSE_PARAMETERS_START(1, 1)
321
    Z_PARAM_PATH(str, str_len)
322
  ZEND_PARSE_PARAMETERS_END();
323
324
  ret = chroot(str);
325
  if (ret != 0) {
326
    php_error_docref(NULL, E_WARNING, "%s (errno %d)", strerror(errno), errno);
327
    RETURN_FALSE;
328
  }
329
330
  php_clear_stat_cache(1, NULL, 0);
331
332
  ret = chdir("/");
333
334
  if (ret != 0) {
335
    php_error_docref(NULL, E_WARNING, "%s (errno %d)", strerror(errno), errno);
336
    RETURN_FALSE;
337
  }
338
339
  RETURN_TRUE;
340
}
341
/* }}} */
342
#endif
343
344
/* {{{ Change the current directory */
345
PHP_FUNCTION(chdir)
346
0
{
347
0
  char *str;
348
0
  int ret;
349
0
  size_t str_len;
350
351
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
352
0
    Z_PARAM_PATH(str, str_len)
353
0
  ZEND_PARSE_PARAMETERS_END();
354
355
0
  if (php_check_open_basedir(str)) {
356
0
    RETURN_FALSE;
357
0
  }
358
0
  ret = VCWD_CHDIR(str);
359
360
0
  if (ret != 0) {
361
0
    php_error_docref(NULL, E_WARNING, "%s (errno %d)", strerror(errno), errno);
362
0
    RETURN_FALSE;
363
0
  }
364
365
0
  if (BG(CurrentStatFile) && !IS_ABSOLUTE_PATH(ZSTR_VAL(BG(CurrentStatFile)), ZSTR_LEN(BG(CurrentStatFile)))) {
366
0
    zend_string_release(BG(CurrentStatFile));
367
0
    BG(CurrentStatFile) = NULL;
368
0
  }
369
0
  if (BG(CurrentLStatFile) && !IS_ABSOLUTE_PATH(ZSTR_VAL(BG(CurrentLStatFile)), ZSTR_LEN(BG(CurrentLStatFile)))) {
370
0
    zend_string_release(BG(CurrentLStatFile));
371
0
    BG(CurrentLStatFile) = NULL;
372
0
  }
373
374
0
  RETURN_TRUE;
375
0
}
376
/* }}} */
377
378
/* {{{ Gets the current directory */
379
PHP_FUNCTION(getcwd)
380
5
{
381
5
  char path[MAXPATHLEN];
382
5
  char *ret=NULL;
383
384
5
  ZEND_PARSE_PARAMETERS_NONE();
385
386
5
#ifdef HAVE_GETCWD
387
5
  ret = VCWD_GETCWD(path, MAXPATHLEN);
388
#elif defined(HAVE_GETWD)
389
  ret = VCWD_GETWD(path);
390
#endif
391
392
5
  if (ret) {
393
5
    RETURN_STRING(path);
394
5
  } else {
395
0
    RETURN_FALSE;
396
0
  }
397
5
}
398
/* }}} */
399
400
/* {{{ Find pathnames matching a pattern */
401
#if defined(ZTS) && defined(PHP_GLOB_ALTDIRFUNC)
402
static void *php_glob_opendir_wrapper(const char *path)
403
{
404
  return VCWD_OPENDIR(path);
405
}
406
407
static void php_glob_closedir_wrapper(void *dir)
408
{
409
  (void) closedir(dir);
410
}
411
412
static int php_glob_lstat_wrapper(const char *buf, zend_stat_t *sb)
413
{
414
  return VCWD_LSTAT(buf, sb);
415
}
416
417
static int php_glob_stat_wrapper(const char *buf, zend_stat_t *sb)
418
{
419
  return VCWD_STAT(buf, sb);
420
}
421
#endif
422
423
PHP_FUNCTION(glob)
424
9
{
425
9
  size_t cwd_skip = 0;
426
#if defined(ZTS) && !defined(PHP_GLOB_ALTDIRFUNC)
427
  char cwd[MAXPATHLEN];
428
  char work_pattern[MAXPATHLEN];
429
#endif
430
9
  char *pattern = NULL;
431
9
  size_t pattern_len;
432
9
  zend_long flags = 0;
433
9
  php_glob_t globbuf;
434
9
  size_t n;
435
9
  int ret;
436
9
  bool basedir_limit = 0;
437
9
  zval tmp;
438
439
27
  ZEND_PARSE_PARAMETERS_START(1, 2)
440
36
    Z_PARAM_PATH(pattern, pattern_len)
441
9
    Z_PARAM_OPTIONAL
442
32
    Z_PARAM_LONG(flags)
443
9
  ZEND_PARSE_PARAMETERS_END();
444
445
9
  if (pattern_len >= MAXPATHLEN) {
446
0
    php_error_docref(NULL, E_WARNING, "Pattern exceeds the maximum allowed length of %d characters", MAXPATHLEN);
447
0
    RETURN_FALSE;
448
0
  }
449
450
9
  if ((PHP_GLOB_AVAILABLE_FLAGS & flags) != flags) {
451
0
    php_error_docref(NULL, E_WARNING, "At least one of the passed flags is invalid or not supported on this platform");
452
0
    RETURN_FALSE;
453
0
  }
454
455
9
  memset(&globbuf, 0, sizeof(globbuf));
456
457
9
  int passed_glob_flags = flags & PHP_GLOB_FLAGMASK;
458
459
#ifdef ZTS
460
  if (!IS_ABSOLUTE_PATH(pattern, pattern_len)) {
461
    /* System glob uses the current work directory which is not thread safe.
462
     * The first fix is to override the functions used to open/read/... paths
463
     * with the VCWD ones used in PHP.
464
     * If that functionality is unavailable for whatever reason, fall back
465
     * to prepending the current working directory to the passed path.
466
     * However, that comes with limitations regarding meta characters
467
     * that is not solvable in general (GH-13204). */
468
# ifdef PHP_GLOB_ALTDIRFUNC
469
    globbuf.gl_opendir = php_glob_opendir_wrapper;
470
    globbuf.gl_readdir = (struct dirent *(*)(void *)) readdir;
471
    globbuf.gl_closedir = php_glob_closedir_wrapper;
472
    globbuf.gl_lstat = php_glob_lstat_wrapper;
473
    globbuf.gl_stat = php_glob_stat_wrapper;
474
    passed_glob_flags |= PHP_GLOB_ALTDIRFUNC;
475
# else
476
    char *result = VCWD_GETCWD(cwd, MAXPATHLEN);
477
    if (!result) {
478
      cwd[0] = '\0';
479
    }
480
#  ifdef PHP_WIN32
481
    if (IS_SLASH(*pattern)) {
482
      cwd[2] = '\0';
483
    }
484
#  endif
485
    cwd_skip = strlen(cwd)+1;
486
487
    snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, pattern);
488
    pattern = work_pattern;
489
# endif
490
  }
491
#endif
492
493
9
  if (0 != (ret = php_glob(pattern, passed_glob_flags, NULL, &globbuf))) {
494
3
#ifdef PHP_GLOB_NOMATCH
495
3
    if (PHP_GLOB_NOMATCH == ret) {
496
      /* Some glob implementation simply return no data if no matches
497
         were found, others return the PHP_GLOB_NOMATCH error code.
498
         We don't want to treat PHP_GLOB_NOMATCH as an error condition
499
         so that PHP glob() behaves the same on both types of
500
         implementations and so that 'foreach (glob() as ...'
501
         can be used for simple glob() calls without further error
502
         checking.
503
      */
504
3
      goto no_results;
505
3
    }
506
0
#endif
507
0
    RETURN_FALSE;
508
0
  }
509
510
  /* now catch the FreeBSD style of "no matches" */
511
6
  if (!globbuf.gl_pathc || !globbuf.gl_pathv) {
512
0
#ifdef PHP_GLOB_NOMATCH
513
3
no_results:
514
3
#endif
515
3
    RETURN_EMPTY_ARRAY();
516
3
  }
517
518
6
  array_init(return_value);
519
12
  for (n = 0; n < (size_t)globbuf.gl_pathc; n++) {
520
6
    if (PG(open_basedir) && *PG(open_basedir)) {
521
6
      if (php_check_open_basedir_ex(globbuf.gl_pathv[n], 0)) {
522
6
        basedir_limit = 1;
523
6
        continue;
524
6
      }
525
6
    }
526
    /* we need to do this every time since PHP_GLOB_ONLYDIR does not guarantee that
527
     * all directories will be filtered. GNU libc documentation states the
528
     * following:
529
     * If the information about the type of the file is easily available
530
     * non-directories will be rejected but no extra work will be done to
531
     * determine the information for each file. I.e., the caller must still be
532
     * able to filter directories out.
533
     */
534
0
    if (flags & PHP_GLOB_ONLYDIR) {
535
0
      zend_stat_t s = {0};
536
537
0
      if (0 != VCWD_STAT(globbuf.gl_pathv[n], &s)) {
538
0
        continue;
539
0
      }
540
541
0
      if (S_IFDIR != (s.st_mode & S_IFMT)) {
542
0
        continue;
543
0
      }
544
0
    }
545
0
    ZVAL_STRING(&tmp, globbuf.gl_pathv[n]+cwd_skip);
546
0
    zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
547
0
  }
548
549
6
  php_globfree(&globbuf);
550
551
6
  if (basedir_limit && !zend_hash_num_elements(Z_ARRVAL_P(return_value))) {
552
6
    zend_array_destroy(Z_ARR_P(return_value));
553
6
    RETURN_FALSE;
554
6
  }
555
6
}
556
/* }}} */
557
558
/* {{{ List files & directories inside the specified path */
559
PHP_FUNCTION(scandir)
560
0
{
561
0
  char *dirn;
562
0
  size_t dirn_len;
563
0
  zend_long flags = PHP_SCANDIR_SORT_ASCENDING;
564
0
  zend_string **namelist;
565
0
  int n, i;
566
0
  zval *zcontext = NULL;
567
0
  php_stream_context *context = NULL;
568
569
0
  ZEND_PARSE_PARAMETERS_START(1, 3)
570
0
    Z_PARAM_PATH(dirn, dirn_len)
571
0
    Z_PARAM_OPTIONAL
572
0
    Z_PARAM_LONG(flags)
573
0
    Z_PARAM_RESOURCE_OR_NULL(zcontext)
574
0
  ZEND_PARSE_PARAMETERS_END();
575
576
0
  if (dirn_len < 1) {
577
0
    zend_argument_must_not_be_empty_error(1);
578
0
    RETURN_THROWS();
579
0
  }
580
581
0
  if (zcontext) {
582
0
    context = php_stream_context_from_zval(zcontext, 0);
583
0
  }
584
585
0
  if (flags == PHP_SCANDIR_SORT_ASCENDING) {
586
0
    n = php_stream_scandir(dirn, &namelist, context, (void *) php_stream_dirent_alphasort);
587
0
  } else if (flags == PHP_SCANDIR_SORT_NONE) {
588
0
    n = php_stream_scandir(dirn, &namelist, context, NULL);
589
0
  } else if (flags == PHP_SCANDIR_SORT_DESCENDING) {
590
0
    n = php_stream_scandir(dirn, &namelist, context, (void *) php_stream_dirent_alphasortr);
591
0
  } else {
592
0
    zend_argument_value_error(2, "must be one of the SCANDIR_SORT_ASCENDING, SCANDIR_SORT_DESCENDING, or SCANDIR_SORT_NONE constants");
593
0
    RETURN_THROWS();
594
0
    }
595
596
0
  if (n < 0) {
597
0
    php_error_docref(NULL, E_WARNING, "(errno %d): %s", errno, strerror(errno));
598
0
    RETURN_FALSE;
599
0
  }
600
601
0
  array_init_size(return_value, n);
602
0
  zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
603
604
0
  ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
605
0
    for (i = 0; i < n; i++) {
606
0
      ZEND_HASH_FILL_SET_STR(namelist[i]);
607
0
      ZEND_HASH_FILL_NEXT();
608
0
    }
609
0
  } ZEND_HASH_FILL_END();
610
611
0
  if (n) {
612
    efree(namelist);
613
0
  }
614
0
}
615
/* }}} */