Coverage Report

Created: 2026-06-02 06:36

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
6.77k
#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
0
{
56
0
  zend_throw_error(NULL, "Cannot directly construct Directory, use dir() instead");
57
0
  return NULL;
58
0
}
59
60
static void php_set_default_dir(zend_resource *res)
61
0
{
62
0
  if (DIRG(default_dir)) {
63
0
    zend_list_delete(DIRG(default_dir));
64
0
  }
65
66
0
  if (res) {
67
0
    GC_ADDREF(res);
68
0
  }
69
70
0
  DIRG(default_dir) = res;
71
0
}
72
73
PHP_RINIT_FUNCTION(dir)
74
6.77k
{
75
6.77k
  DIRG(default_dir) = NULL;
76
6.77k
  return SUCCESS;
77
6.77k
}
78
79
PHP_MINIT_FUNCTION(dir)
80
2
{
81
2
  dirsep_str[0] = DEFAULT_SLASH;
82
2
  dirsep_str[1] = '\0';
83
84
2
  pathsep_str[0] = ZEND_PATHS_SEPARATOR;
85
2
  pathsep_str[1] = '\0';
86
87
2
  register_dir_symbols(module_number);
88
89
2
  dir_class_entry_ptr = register_class_Directory();
90
2
  dir_class_entry_ptr->default_object_handlers = &dir_class_object_handlers;
91
92
2
  memcpy(&dir_class_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
93
2
  dir_class_object_handlers.get_constructor = dir_class_get_constructor;
94
2
  dir_class_object_handlers.clone_obj = NULL;
95
2
  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
2
  return SUCCESS;
102
2
}
103
/* }}} */
104
105
/* {{{ internal functions */
106
static void _php_do_opendir(INTERNAL_FUNCTION_PARAMETERS, int createobject)
107
0
{
108
0
  char *dirname;
109
0
  size_t dir_len;
110
0
  zval *zcontext = NULL;
111
0
  php_stream_context *context = NULL;
112
0
  php_stream *dirp;
113
114
0
  ZEND_PARSE_PARAMETERS_START(1, 2)
115
0
    Z_PARAM_PATH(dirname, dir_len)
116
0
    Z_PARAM_OPTIONAL
117
0
    Z_PARAM_RESOURCE_OR_NULL(zcontext)
118
0
  ZEND_PARSE_PARAMETERS_END();
119
120
0
  context = php_stream_context_from_zval(zcontext, 0);
121
122
0
  dirp = php_stream_opendir(dirname, REPORT_ERRORS, context);
123
124
0
  if (dirp == NULL) {
125
0
    RETURN_FALSE;
126
0
  }
127
128
0
  dirp->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
129
130
0
  php_set_default_dir(dirp->res);
131
132
0
  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
0
  } else {
138
0
    php_stream_to_zval(dirp, return_value);
139
0
  }
140
0
}
141
/* }}} */
142
143
/* {{{ Open a directory and return a dir_handle */
144
PHP_FUNCTION(opendir)
145
0
{
146
0
  _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
147
0
}
148
/* }}} */
149
150
/* {{{ Directory class with properties, handle and class and methods read, rewind and close */
151
PHP_FUNCTION(dir)
152
0
{
153
0
  _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
154
0
}
155
/* }}} */
156
157
158
static php_stream* php_dir_get_directory_stream_from_user_arg(php_stream *dir_stream)
159
0
{
160
0
  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
0
  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
0
  return dir_stream;
177
0
}
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
0
{
205
0
  php_stream *dirp = NULL;
206
207
0
  ZEND_PARSE_PARAMETERS_START(0, 1)
208
0
    Z_PARAM_OPTIONAL
209
0
    PHP_Z_PARAM_STREAM_OR_NULL(dirp)
210
0
  ZEND_PARSE_PARAMETERS_END();
211
212
0
  dirp = php_dir_get_directory_stream_from_user_arg(dirp);
213
0
  if (UNEXPECTED(dirp == NULL)) {
214
0
    RETURN_THROWS();
215
0
  }
216
0
  zend_resource *res = dirp->res;
217
0
  zend_list_close(res);
218
219
0
  if (res == DIRG(default_dir)) {
220
0
    php_set_default_dir(NULL);
221
0
  }
222
0
}
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
0
{
381
0
  char path[MAXPATHLEN];
382
0
  char *ret=NULL;
383
384
0
  ZEND_PARSE_PARAMETERS_NONE();
385
386
0
#ifdef HAVE_GETCWD
387
0
  ret = VCWD_GETCWD(path, MAXPATHLEN);
388
#elif defined(HAVE_GETWD)
389
  ret = VCWD_GETWD(path);
390
#endif
391
392
0
  if (ret) {
393
0
    RETURN_STRING(path);
394
0
  } else {
395
0
    RETURN_FALSE;
396
0
  }
397
0
}
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
0
{
425
0
  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
0
  char *pattern = NULL;
431
0
  size_t pattern_len;
432
0
  zend_long flags = 0;
433
0
  php_glob_t globbuf;
434
0
  size_t n;
435
0
  int ret;
436
0
  bool basedir_limit = 0;
437
0
  zval tmp;
438
439
0
  ZEND_PARSE_PARAMETERS_START(1, 2)
440
0
    Z_PARAM_PATH(pattern, pattern_len)
441
0
    Z_PARAM_OPTIONAL
442
0
    Z_PARAM_LONG(flags)
443
0
  ZEND_PARSE_PARAMETERS_END();
444
445
0
  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
0
  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
0
  memset(&globbuf, 0, sizeof(globbuf));
456
457
0
  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
0
  if (0 != (ret = php_glob(pattern, passed_glob_flags, NULL, &globbuf))) {
494
0
#ifdef PHP_GLOB_NOMATCH
495
0
    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
0
      goto no_results;
505
0
    }
506
0
#endif
507
0
    RETURN_FALSE;
508
0
  }
509
510
  /* now catch the FreeBSD style of "no matches" */
511
0
  if (!globbuf.gl_pathc || !globbuf.gl_pathv) {
512
0
#ifdef PHP_GLOB_NOMATCH
513
0
no_results:
514
0
#endif
515
0
    RETURN_EMPTY_ARRAY();
516
0
  }
517
518
0
  array_init(return_value);
519
0
  for (n = 0; n < (size_t)globbuf.gl_pathc; n++) {
520
0
    if (PG(open_basedir) && *PG(open_basedir)) {
521
0
      if (php_check_open_basedir_ex(globbuf.gl_pathv[n], 0)) {
522
0
        basedir_limit = 1;
523
0
        continue;
524
0
      }
525
0
    }
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
0
  php_globfree(&globbuf);
550
551
0
  if (basedir_limit && !zend_hash_num_elements(Z_ARRVAL_P(return_value))) {
552
0
    zend_array_destroy(Z_ARR_P(return_value));
553
0
    RETURN_FALSE;
554
0
  }
555
0
}
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
/* }}} */