Coverage Report

Created: 2026-04-01 06:49

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