xref: /php-src/ext/spl/spl_directory.c (revision bf5e6c5f)
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: Marcus Boerger <helly@php.net>                               |
14    +----------------------------------------------------------------------+
15  */
16 
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
20 
21 #include "php.h"
22 #include "ext/standard/file.h"
23 #include "ext/standard/php_filestat.h"
24 #include "ext/standard/flock_compat.h"
25 #include "ext/standard/scanf.h"
26 #include "ext/standard/php_string.h" /* For php_basename() */
27 #include "zend_attributes.h"
28 #include "zend_exceptions.h"
29 #include "zend_interfaces.h"
30 
31 #include "spl_iterators.h"
32 #include "spl_directory.h"
33 #include "spl_directory_arginfo.h"
34 #include "spl_exceptions.h"
35 #include "spl_functions.h" /* For spl_set_private_debug_info_property() */
36 
37 #define SPL_HAS_FLAG(flags, test_flag) ((flags & test_flag) ? 1 : 0)
38 
39 /* declare the class handlers */
40 static zend_object_handlers spl_filesystem_object_handlers;
41 /* includes handler to validate object state when retrieving methods */
42 static zend_object_handlers spl_filesystem_object_check_handlers;
43 
44 /* decalre the class entry */
45 PHPAPI zend_class_entry *spl_ce_SplFileInfo;
46 PHPAPI zend_class_entry *spl_ce_DirectoryIterator;
47 PHPAPI zend_class_entry *spl_ce_FilesystemIterator;
48 PHPAPI zend_class_entry *spl_ce_RecursiveDirectoryIterator;
49 PHPAPI zend_class_entry *spl_ce_GlobIterator;
50 PHPAPI zend_class_entry *spl_ce_SplFileObject;
51 PHPAPI zend_class_entry *spl_ce_SplTempFileObject;
52 
53 /* Object helper */
spl_filesystem_from_obj(zend_object * obj)54 static inline spl_filesystem_object *spl_filesystem_from_obj(zend_object *obj) /* {{{ */ {
55 	return (spl_filesystem_object*)((char*)(obj) - XtOffsetOf(spl_filesystem_object, std));
56 }
57 /* }}} */
58 
59 /* define an overloaded iterator structure */
60 typedef struct {
61 	zend_object_iterator  intern;
62 	zval                  current;
63 	void                 *object;
64 } spl_filesystem_iterator;
65 
spl_filesystem_object_to_iterator(spl_filesystem_object * obj)66 static inline spl_filesystem_iterator* spl_filesystem_object_to_iterator(spl_filesystem_object *obj)
67 {
68 	spl_filesystem_iterator    *it;
69 
70 	it = ecalloc(1, sizeof(spl_filesystem_iterator));
71 	it->object = (void *)obj;
72 	zend_iterator_init(&it->intern);
73 	return it;
74 }
75 
spl_filesystem_iterator_to_object(spl_filesystem_iterator * it)76 static inline spl_filesystem_object* spl_filesystem_iterator_to_object(spl_filesystem_iterator *it)
77 {
78 	return (spl_filesystem_object*)it->object;
79 }
80 
81 #define CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(spl_filesystem_object_pointer) \
82 	if (!(spl_filesystem_object_pointer)->u.file.stream) { \
83 		zend_throw_error(NULL, "Object not initialized"); \
84 		RETURN_THROWS(); \
85 	}
86 
87 #define CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern) \
88 	if (!(intern)->u.dir.dirp) { \
89 		zend_throw_error(NULL, "Object not initialized"); \
90 		RETURN_THROWS(); \
91 	}
92 
spl_filesystem_file_free_line(spl_filesystem_object * intern)93 static void spl_filesystem_file_free_line(spl_filesystem_object *intern) /* {{{ */
94 {
95 	if (intern->u.file.current_line) {
96 		zend_string_release_ex(intern->u.file.current_line, /* persistent */ false);
97 		intern->u.file.current_line = NULL;
98 	}
99 	if (!Z_ISUNDEF(intern->u.file.current_zval)) {
100 		zval_ptr_dtor(&intern->u.file.current_zval);
101 		ZVAL_UNDEF(&intern->u.file.current_zval);
102 	}
103 } /* }}} */
104 
spl_filesystem_object_destroy_object(zend_object * object)105 static void spl_filesystem_object_destroy_object(zend_object *object) /* {{{ */
106 {
107 	spl_filesystem_object *intern = spl_filesystem_from_obj(object);
108 
109 	zend_objects_destroy_object(object);
110 
111 	switch(intern->type) {
112 	case SPL_FS_DIR:
113 		if (intern->u.dir.dirp) {
114 			php_stream_close(intern->u.dir.dirp);
115 			intern->u.dir.dirp = NULL;
116 		}
117 		break;
118 	case SPL_FS_FILE:
119 		if (intern->u.file.stream) {
120 			/*
121 			if (intern->u.file.zcontext) {
122 			   zend_list_delref(Z_RESVAL_P(intern->zcontext));
123 			}
124 			*/
125 			if (!intern->u.file.stream->is_persistent) {
126 				php_stream_close(intern->u.file.stream);
127 			} else {
128 				php_stream_pclose(intern->u.file.stream);
129 			}
130 			intern->u.file.stream = NULL;
131 			ZVAL_UNDEF(&intern->u.file.zresource);
132 		}
133 		break;
134 	default:
135 		break;
136 	}
137 } /* }}} */
138 
spl_filesystem_object_free_storage(zend_object * object)139 static void spl_filesystem_object_free_storage(zend_object *object) /* {{{ */
140 {
141 	spl_filesystem_object *intern = spl_filesystem_from_obj(object);
142 
143 	if (intern->oth_handler && intern->oth_handler->dtor) {
144 		intern->oth_handler->dtor(intern);
145 	}
146 
147 	zend_object_std_dtor(&intern->std);
148 
149 	if (intern->path) {
150 		zend_string_release(intern->path);
151 	}
152 	if (intern->file_name) {
153 		zend_string_release(intern->file_name);
154 	}
155 	switch(intern->type) {
156 	case SPL_FS_INFO:
157 		break;
158 	case SPL_FS_DIR:
159 		if (intern->u.dir.sub_path) {
160 			zend_string_release(intern->u.dir.sub_path);
161 		}
162 		break;
163 	case SPL_FS_FILE:
164 		if (intern->u.file.open_mode) {
165 			zend_string_release(intern->u.file.open_mode);
166 		}
167 		if (intern->orig_path) {
168 			zend_string_release(intern->orig_path);
169 		}
170 		spl_filesystem_file_free_line(intern);
171 		break;
172 	}
173 } /* }}} */
174 
175 /* {{{ spl_ce_dir_object_new */
176 /* creates the object by
177    - allocating memory
178    - initializing the object members
179    - storing the object
180    - setting it's handlers
181 
182    called from
183    - clone
184    - new
185  */
spl_filesystem_object_new(zend_class_entry * class_type)186 static zend_object *spl_filesystem_object_new(zend_class_entry *class_type)
187 {
188 	spl_filesystem_object *intern;
189 
190 	intern = emalloc(sizeof(spl_filesystem_object) + zend_object_properties_size(class_type));
191 	/* Avoid initializing the entirety of spl_filesystem_object.u.dir.entry. */
192 	memset(intern, 0,
193 		MAX(XtOffsetOf(spl_filesystem_object, u.dir.entry),
194 			XtOffsetOf(spl_filesystem_object, u.file.escape) + sizeof(int)));
195 	/* intern->type = SPL_FS_INFO; done by set 0 */
196 	intern->file_class = spl_ce_SplFileObject;
197 	intern->info_class = spl_ce_SplFileInfo;
198 
199 	zend_object_std_init(&intern->std, class_type);
200 	object_properties_init(&intern->std, class_type);
201 
202 	return &intern->std;
203 }
204 /* }}} */
205 
spl_intern_is_glob(const spl_filesystem_object * intern)206 static inline bool spl_intern_is_glob(const spl_filesystem_object *intern)
207 {
208 	/* NULL check on `dirp` is necessary as destructors may interfere. */
209 	return intern->u.dir.dirp && php_stream_is(intern->u.dir.dirp, &php_glob_stream_ops);
210 }
211 
spl_filesystem_object_get_path(const spl_filesystem_object * intern)212 PHPAPI zend_string *spl_filesystem_object_get_path(const spl_filesystem_object *intern) /* {{{ */
213 {
214 #ifdef HAVE_GLOB
215 	if (intern->type == SPL_FS_DIR && spl_intern_is_glob(intern)) {
216 		size_t len = 0;
217 		char *tmp = php_glob_stream_get_path(intern->u.dir.dirp, &len);
218 		if (len == 0) {
219 			return NULL;
220 		}
221 		return zend_string_init(tmp, len, /* persistent */ false);
222 	}
223 #endif
224 	if (!intern->path) {
225 		return NULL;
226 	}
227 	return zend_string_copy(intern->path);
228 } /* }}} */
229 
spl_filesystem_object_get_file_name(spl_filesystem_object * intern)230 static zend_result spl_filesystem_object_get_file_name(spl_filesystem_object *intern) /* {{{ */
231 {
232 	if (intern->file_name) {
233 		/* already known */
234 		return SUCCESS;
235 	}
236 
237 	switch (intern->type) {
238 		case SPL_FS_INFO:
239 		case SPL_FS_FILE:
240 			zend_throw_error(NULL, "Object not initialized");
241 			return FAILURE;
242 		case SPL_FS_DIR: {
243 			size_t name_len;
244 			zend_string *path;
245 			char slash = SPL_HAS_FLAG(intern->flags, SPL_FILE_DIR_UNIXPATHS) ? '/' : DEFAULT_SLASH;
246 
247 			path = spl_filesystem_object_get_path(intern);
248 			/* if there is parent path, amend it, otherwise just use the given path as is */
249 			name_len = strlen(intern->u.dir.entry.d_name);
250 			if (!path) {
251 				intern->file_name = zend_string_init(intern->u.dir.entry.d_name, name_len, 0);
252 				return SUCCESS;
253 			}
254 
255 			ZEND_ASSERT(ZSTR_LEN(path) != 0);
256 			intern->file_name = zend_string_concat3(
257 				ZSTR_VAL(path), ZSTR_LEN(path), &slash, 1, intern->u.dir.entry.d_name, name_len);
258 			zend_string_release_ex(path, /* persistent */ false);
259 			break;
260 		}
261 	}
262 	return SUCCESS;
263 } /* }}} */
264 
265 /* TODO Make void or have callers check return value */
spl_filesystem_dir_read(spl_filesystem_object * intern)266 static bool spl_filesystem_dir_read(spl_filesystem_object *intern) /* {{{ */
267 {
268 	if (intern->file_name) {
269 		/* invalidate */
270 		zend_string_release(intern->file_name);
271 		intern->file_name = NULL;
272 	}
273 	if (!intern->u.dir.dirp || !php_stream_readdir(intern->u.dir.dirp, &intern->u.dir.entry)) {
274 		intern->u.dir.entry.d_name[0] = '\0';
275 		return 0;
276 	} else {
277 		return 1;
278 	}
279 }
280 /* }}} */
281 
282 #define IS_SLASH_AT(zs, pos) (IS_SLASH(zs[pos]))
283 
spl_filesystem_is_dot(const char * d_name)284 static inline bool spl_filesystem_is_dot(const char * d_name) /* {{{ */
285 {
286 	return !strcmp(d_name, ".") || !strcmp(d_name, "..");
287 }
288 /* }}} */
289 
290 /* {{{ spl_filesystem_dir_open */
291 /* open a directory resource
292  * Can emit an E_WARNING as it reports errors from php_stream_opendir() */
spl_filesystem_dir_open(spl_filesystem_object * intern,zend_string * path)293 static void spl_filesystem_dir_open(spl_filesystem_object* intern, zend_string *path)
294 {
295 	bool skip_dots = SPL_HAS_FLAG(intern->flags, SPL_FILE_DIR_SKIPDOTS);
296 
297 	intern->type = SPL_FS_DIR;
298 	intern->u.dir.dirp = php_stream_opendir(ZSTR_VAL(path), REPORT_ERRORS, FG(default_context));
299 
300 	if (ZSTR_LEN(path) > 1 && IS_SLASH_AT(ZSTR_VAL(path), ZSTR_LEN(path)-1)) {
301 		intern->path = zend_string_init(ZSTR_VAL(path), ZSTR_LEN(path)-1, 0);
302 	} else {
303 		intern->path = zend_string_copy(path);
304 	}
305 	intern->u.dir.index = 0;
306 
307 	if (EG(exception) || intern->u.dir.dirp == NULL) {
308 		intern->u.dir.entry.d_name[0] = '\0';
309 		if (!EG(exception)) {
310 			/* open failed w/out notice (turned to exception due to EH_THROW) */
311 			zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
312 				"Failed to open directory \"%s\"", ZSTR_VAL(path));
313 		}
314 	} else {
315 		do {
316 			spl_filesystem_dir_read(intern);
317 		} while (skip_dots && spl_filesystem_is_dot(intern->u.dir.entry.d_name));
318 	}
319 }
320 /* }}} */
321 
322 /* Can generate E_WARNINGS as we report errors from stream initialized via
323  * php_stream_open_wrapper_ex() */
spl_filesystem_file_open(spl_filesystem_object * intern,bool use_include_path)324 static zend_result spl_filesystem_file_open(spl_filesystem_object *intern, bool use_include_path) /* {{{ */
325 {
326 	zval tmp;
327 
328 	intern->type = SPL_FS_FILE;
329 	php_stat(intern->file_name, FS_IS_DIR, &tmp);
330 	if (Z_TYPE(tmp) == IS_TRUE) {
331 		zend_string_release(intern->u.file.open_mode);
332 		intern->u.file.open_mode = NULL;
333 		intern->file_name = NULL;
334 		zend_throw_exception_ex(spl_ce_LogicException, 0, "Cannot use SplFileObject with directories");
335 		return FAILURE;
336 	}
337 
338 	intern->u.file.context = php_stream_context_from_zval(intern->u.file.zcontext, 0);
339 	intern->u.file.stream = php_stream_open_wrapper_ex(ZSTR_VAL(intern->file_name), ZSTR_VAL(intern->u.file.open_mode), (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, intern->u.file.context);
340 
341 	if (!ZSTR_LEN(intern->file_name) || !intern->u.file.stream) {
342 		if (!EG(exception)) {
343 			zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot open file '%s'", ZSTR_VAL(intern->file_name));
344 		}
345 		zend_string_release(intern->u.file.open_mode);
346 		intern->u.file.open_mode = NULL;
347 		intern->file_name = NULL; /* until here it is not a copy */
348 		return FAILURE;
349 	}
350 
351 	/* prevent closing the stream outside of SplFileObject */
352 	intern->u.file.stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
353 
354 	/*
355 	if (intern->u.file.zcontext) {
356 		//zend_list_addref(Z_RES_VAL(intern->u.file.zcontext));
357 		Z_ADDREF_P(intern->u.file.zcontext);
358 	}
359 	*/
360 
361 	if (ZSTR_LEN(intern->file_name) > 1 && IS_SLASH_AT(ZSTR_VAL(intern->file_name), ZSTR_LEN(intern->file_name)-1)) {
362 		intern->file_name = zend_string_init(ZSTR_VAL(intern->file_name), ZSTR_LEN(intern->file_name)-1, 0);
363 	} else {
364 		intern->file_name = zend_string_copy(intern->file_name);
365 	}
366 
367 	intern->orig_path = zend_string_init(intern->u.file.stream->orig_path, strlen(intern->u.file.stream->orig_path), 0);
368 
369 	/* avoid reference counting in debug mode, thus do it manually */
370 	ZVAL_RES(&intern->u.file.zresource, intern->u.file.stream->res);
371 	/*!!! TODO: maybe bug?
372 	Z_SET_REFCOUNT(intern->u.file.zresource, 1);
373 	*/
374 
375 	intern->u.file.delimiter = ',';
376 	intern->u.file.enclosure = '"';
377 	intern->u.file.escape = (unsigned char) '\\';
378 	intern->u.file.is_escape_default = true;
379 
380 	intern->u.file.func_getCurr = zend_hash_str_find_ptr(&intern->std.ce->function_table, "getcurrentline", sizeof("getcurrentline") - 1);
381 
382 	return SUCCESS;
383 } /* }}} */
384 
385 /* {{{ spl_filesystem_object_clone */
386 /* Local zend_object creation (on stack)
387    Load the 'other' object
388    Create a new empty object (See spl_filesystem_object_new)
389    Open the directory
390    Clone other members (properties)
391  */
spl_filesystem_object_clone(zend_object * old_object)392 static zend_object *spl_filesystem_object_clone(zend_object *old_object)
393 {
394 	zend_object *new_object;
395 	spl_filesystem_object *intern;
396 	spl_filesystem_object *source;
397 
398 	source = spl_filesystem_from_obj(old_object);
399 	new_object = spl_filesystem_object_new(old_object->ce);
400 	intern = spl_filesystem_from_obj(new_object);
401 
402 	intern->flags = source->flags;
403 
404 	switch (source->type) {
405 		case SPL_FS_INFO:
406 			if (source->path != NULL) {
407 				intern->path = zend_string_copy(source->path);
408 			}
409 			if (source->file_name != NULL) {
410 				intern->file_name = zend_string_copy(source->file_name);
411 			}
412 			break;
413 		case SPL_FS_DIR: {
414 			spl_filesystem_dir_open(intern, source->path);
415 			/* read until we hit the position in which we were before */
416 			bool skip_dots = SPL_HAS_FLAG(source->flags, SPL_FILE_DIR_SKIPDOTS);
417 			int index;
418 			for (index = 0; index < source->u.dir.index; ++index) {
419 				do {
420 					spl_filesystem_dir_read(intern);
421 				} while (skip_dots && spl_filesystem_is_dot(intern->u.dir.entry.d_name));
422 			}
423 			intern->u.dir.index = index;
424 			break;
425 		}
426 		case SPL_FS_FILE:
427 			ZEND_UNREACHABLE();
428 	}
429 
430 	intern->file_class = source->file_class;
431 	intern->info_class = source->info_class;
432 	intern->oth = source->oth;
433 	intern->oth_handler = source->oth_handler;
434 
435 	zend_objects_clone_members(new_object, old_object);
436 
437 	if (intern->oth_handler && intern->oth_handler->clone) {
438 		intern->oth_handler->clone(source, intern);
439 	}
440 
441 	return new_object;
442 }
443 /* }}} */
444 
spl_filesystem_info_set_filename(spl_filesystem_object * intern,zend_string * path)445 static void spl_filesystem_info_set_filename(spl_filesystem_object *intern, zend_string *path) /* {{{ */
446 {
447 	size_t path_len;
448 
449 	if (intern->file_name) {
450 		zend_string_release(intern->file_name);
451 	}
452 
453 	path_len = ZSTR_LEN(path);
454 	if (path_len > 1 && IS_SLASH_AT(ZSTR_VAL(path), path_len-1)) {
455 		do {
456 			path_len--;
457 		} while (path_len > 1 && IS_SLASH_AT(ZSTR_VAL(path), path_len - 1));
458 		intern->file_name = zend_string_init(ZSTR_VAL(path), path_len, 0);
459 	} else {
460 		intern->file_name = zend_string_copy(path);
461 	}
462 	while (path_len > 1 && !IS_SLASH_AT(ZSTR_VAL(path), path_len-1)) {
463 		path_len--;
464 	}
465 	if (path_len) {
466 		path_len--;
467 	}
468 
469 	if (intern->path) {
470 		zend_string_release(intern->path);
471 	}
472 	intern->path = zend_string_init(ZSTR_VAL(path), path_len, 0);
473 } /* }}} */
474 
475 // TODO Do not pass return_value pointer but actually use value returned by function at call site?
spl_filesystem_object_create_info(zend_string * file_path,zend_class_entry * ce,zval * return_value)476 static spl_filesystem_object *spl_filesystem_object_create_info(zend_string *file_path, zend_class_entry *ce, zval *return_value) /* {{{ */
477 {
478 	spl_filesystem_object *intern;
479 	zval arg1;
480 
481 	ZEND_ASSERT(file_path && ZSTR_LEN(file_path) > 0);
482 	ZEND_ASSERT(ce != NULL);
483 
484 	intern = spl_filesystem_from_obj(spl_filesystem_object_new(ce));
485 	RETVAL_OBJ(&intern->std);
486 
487 	if (ce->constructor->common.scope != spl_ce_SplFileInfo) {
488 		ZVAL_STR_COPY(&arg1, file_path);
489 		zend_call_method_with_1_params(Z_OBJ_P(return_value), ce, &ce->constructor, "__construct", NULL, &arg1);
490 		zval_ptr_dtor(&arg1);
491 	} else {
492 		spl_filesystem_info_set_filename(intern, file_path);
493 	}
494 
495 	return intern;
496 } /* }}} */
497 
spl_filesystem_object_create_type(int num_args,spl_filesystem_object * source,int type,zend_class_entry * ce,zval * return_value)498 static spl_filesystem_object *spl_filesystem_object_create_type(int num_args, spl_filesystem_object *source, int type, zend_class_entry *ce, zval *return_value) /* {{{ */
499 {
500 	spl_filesystem_object *intern;
501 	bool use_include_path = 0;
502 	zval arg1, arg2;
503 	zend_error_handling error_handling;
504 
505 	switch (source->type) {
506 		case SPL_FS_INFO:
507 		case SPL_FS_FILE:
508 			break;
509 		case SPL_FS_DIR:
510 			if (!source->u.dir.entry.d_name[0]) {
511 				zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Could not open file");
512 				return NULL;
513 			}
514 	}
515 
516 	switch (type) {
517 		case SPL_FS_INFO:
518 			ce = ce ? ce : source->info_class;
519 
520 			intern = spl_filesystem_from_obj(spl_filesystem_object_new(ce));
521 			RETVAL_OBJ(&intern->std);
522 
523 			if (spl_filesystem_object_get_file_name(source) == FAILURE) {
524 				return NULL;
525 			}
526 
527 			if (ce->constructor->common.scope != spl_ce_SplFileInfo) {
528 				ZVAL_STR_COPY(&arg1, source->file_name);
529 				zend_call_method_with_1_params(Z_OBJ_P(return_value), ce, &ce->constructor, "__construct", NULL, &arg1);
530 				zval_ptr_dtor(&arg1);
531 			} else {
532 				intern->file_name = zend_string_copy(source->file_name);
533 				intern->path = spl_filesystem_object_get_path(source);
534 			}
535 			break;
536 		case SPL_FS_FILE:
537 		{
538 			ce = ce ? ce : source->file_class;
539 
540 			zend_string *open_mode = ZSTR_CHAR('r');
541 			zval *resource = NULL;
542 
543 			if (zend_parse_parameters(num_args, "|Sbr!",
544 				&open_mode, &use_include_path, &resource) == FAILURE
545 			) {
546 				return NULL;
547 			}
548 
549 			intern = spl_filesystem_from_obj(spl_filesystem_object_new(ce));
550 			RETVAL_OBJ(&intern->std);
551 
552 			if (spl_filesystem_object_get_file_name(source) == FAILURE) {
553 				return NULL;
554 			}
555 
556 			if (ce->constructor->common.scope != spl_ce_SplFileObject) {
557 				ZVAL_STR_COPY(&arg1, source->file_name);
558 				ZVAL_STR_COPY(&arg2, open_mode);
559 				zend_call_method_with_2_params(Z_OBJ_P(return_value), ce, &ce->constructor, "__construct", NULL, &arg1, &arg2);
560 				zval_ptr_dtor(&arg1);
561 				zval_ptr_dtor(&arg2);
562 			} else {
563 				intern->file_name = source->file_name;
564 				intern->path = spl_filesystem_object_get_path(source);
565 				intern->u.file.open_mode = zend_string_copy(open_mode);
566 				intern->u.file.zcontext = resource;
567 
568 				/* spl_filesystem_file_open() can generate E_WARNINGs which we want to promote to exceptions */
569 				zend_replace_error_handling(EH_THROW, spl_ce_RuntimeException, &error_handling);
570 				if (spl_filesystem_file_open(intern, use_include_path) == FAILURE) {
571 					zend_restore_error_handling(&error_handling);
572 					zval_ptr_dtor(return_value);
573 					ZVAL_NULL(return_value);
574 					return NULL;
575 				}
576 				zend_restore_error_handling(&error_handling);
577 			}
578 			break;
579 		}
580 		case SPL_FS_DIR:
581 			zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Operation not supported");
582 			return NULL;
583 	}
584 	return NULL;
585 } /* }}} */
586 
spl_filesystem_is_invalid_or_dot(const char * d_name)587 static bool spl_filesystem_is_invalid_or_dot(const char * d_name) /* {{{ */
588 {
589 	return d_name[0] == '\0' || spl_filesystem_is_dot(d_name);
590 }
591 /* }}} */
592 
spl_filesystem_object_get_pathname(spl_filesystem_object * intern)593 static zend_string *spl_filesystem_object_get_pathname(spl_filesystem_object *intern) { /* {{{ */
594 	switch (intern->type) {
595 		case SPL_FS_INFO:
596 		case SPL_FS_FILE:
597 			return intern->file_name;
598 		case SPL_FS_DIR:
599 			if (intern->u.dir.entry.d_name[0]) {
600 				spl_filesystem_object_get_file_name(intern);
601 				return intern->file_name;
602 			}
603 	}
604 	return NULL;
605 }
606 /* }}} */
607 
spl_filesystem_object_get_debug_info(zend_object * object)608 static inline HashTable *spl_filesystem_object_get_debug_info(zend_object *object) /* {{{ */
609 {
610 	spl_filesystem_object *intern = spl_filesystem_from_obj(object);
611 	zval tmp;
612 	HashTable *debug_info;
613 	zend_string *path_name;
614 
615 	// TODO Do zend_new_array() + zend_hash_copy() trick?
616 	debug_info = zend_array_dup(zend_std_get_properties_ex(&intern->std));
617 
618 	path_name = spl_filesystem_object_get_pathname(intern);
619 	if (path_name) {
620 		ZVAL_STR_COPY(&tmp, path_name);
621 	} else {
622 		ZVAL_EMPTY_STRING(&tmp);
623 	}
624 	/* IMPORTANT: Do not free path_name as spl_filesystem_object_get_pathname()
625 	 * updates/sets the intern->file_name and returns the pointer to
626 	 * intern->file_name which must remain allocated. */
627 	spl_set_private_debug_info_property(spl_ce_SplFileInfo, "pathName", strlen("pathName"), debug_info, &tmp);
628 
629 	if (intern->file_name) {
630 		zend_string *path = spl_filesystem_object_get_path(intern);
631 		if (path && ZSTR_LEN(path) && ZSTR_LEN(path) < ZSTR_LEN(intern->file_name)) {
632 			/* +1 to skip the trailing / of the path in the file name */
633 			ZVAL_STRINGL(&tmp, ZSTR_VAL(intern->file_name) + ZSTR_LEN(path) + 1, ZSTR_LEN(intern->file_name) - (ZSTR_LEN(path) + 1));
634 		} else {
635 			ZVAL_STR_COPY(&tmp, intern->file_name);
636 		}
637 		if (path) {
638 			zend_string_release_ex(path, /* persistent */ false);
639 		}
640 
641 		spl_set_private_debug_info_property(spl_ce_SplFileInfo, "fileName", strlen("fileName"), debug_info, &tmp);
642 	}
643 	if (intern->type == SPL_FS_DIR) {
644 #ifdef HAVE_GLOB
645 		if (spl_intern_is_glob(intern)) {
646 			ZVAL_STR_COPY(&tmp, intern->path);
647 		} else {
648 			ZVAL_FALSE(&tmp);
649 		}
650 		spl_set_private_debug_info_property(spl_ce_DirectoryIterator, "glob", strlen("glob"), debug_info, &tmp);
651 #endif
652 		if (intern->u.dir.sub_path) {
653 			ZVAL_STR_COPY(&tmp, intern->u.dir.sub_path);
654 		} else {
655 			ZVAL_EMPTY_STRING(&tmp);
656 		}
657 		spl_set_private_debug_info_property(spl_ce_RecursiveDirectoryIterator, "subPathName", strlen("subPathName"), debug_info, &tmp);
658 	}
659 	if (intern->type == SPL_FS_FILE) {
660 		ZVAL_STR_COPY(&tmp, intern->u.file.open_mode);
661 		spl_set_private_debug_info_property(spl_ce_SplFileObject, "openMode", strlen("openMode"), debug_info, &tmp);
662 
663 		ZVAL_STR(&tmp, ZSTR_CHAR((zend_uchar)intern->u.file.delimiter));
664 		spl_set_private_debug_info_property(spl_ce_SplFileObject, "delimiter", strlen("delimiter"), debug_info, &tmp);
665 
666 		ZVAL_STR(&tmp, ZSTR_CHAR((zend_uchar)intern->u.file.enclosure));
667 		spl_set_private_debug_info_property(spl_ce_SplFileObject, "enclosure", strlen("enclosure"), debug_info, &tmp);
668 	}
669 
670 	return debug_info;
671 }
672 /* }}} */
673 
spl_filesystem_object_get_method_check(zend_object ** object,zend_string * method,const zval * key)674 static zend_function *spl_filesystem_object_get_method_check(zend_object **object, zend_string *method, const zval *key) /* {{{ */
675 {
676 	spl_filesystem_object *fsobj = spl_filesystem_from_obj(*object);
677 
678 	if (fsobj->u.dir.dirp == NULL && fsobj->orig_path == NULL) {
679 		zend_throw_error(NULL, "The parent constructor was not called: the object is in an invalid state");
680 		return NULL;
681 	}
682 
683 	return zend_std_get_method(object, method, key);
684 }
685 /* }}} */
686 
687 #define DIT_CTOR_FLAGS  0x00000001
688 #define DIT_CTOR_GLOB   0x00000002
689 
spl_filesystem_object_construct(INTERNAL_FUNCTION_PARAMETERS,zend_long ctor_flags)690 static void spl_filesystem_object_construct(INTERNAL_FUNCTION_PARAMETERS, zend_long ctor_flags) /* {{{ */
691 {
692 	spl_filesystem_object *intern;
693 	zend_string *path;
694 	zend_result parsed;
695 	zend_long flags = (ctor_flags & ~DIT_CTOR_FLAGS);
696 	zend_error_handling error_handling;
697 
698 	if (SPL_HAS_FLAG(ctor_flags, DIT_CTOR_FLAGS)) {
699 		flags |= SPL_FILE_DIR_KEY_AS_PATHNAME|SPL_FILE_DIR_CURRENT_AS_FILEINFO;
700 		parsed = zend_parse_parameters(ZEND_NUM_ARGS(), "P|l", &path, &flags);
701 	} else {
702 		flags |= SPL_FILE_DIR_KEY_AS_PATHNAME|SPL_FILE_DIR_CURRENT_AS_SELF;
703 		parsed = zend_parse_parameters(ZEND_NUM_ARGS(), "P", &path);
704 	}
705 	if (parsed == FAILURE) {
706 		RETURN_THROWS();
707 	}
708 
709 	if (ZSTR_LEN(path) == 0) {
710 		zend_argument_must_not_be_empty_error(1);
711 		RETURN_THROWS();
712 	}
713 
714 	intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
715 	if (intern->path) {
716 		/* object is already initialized */
717 		zend_throw_error(NULL, "Directory object is already initialized");
718 		RETURN_THROWS();
719 	}
720 	intern->flags = flags;
721 
722 	/* spl_filesystem_dir_open() may emit an E_WARNING */
723 	zend_replace_error_handling(EH_THROW, spl_ce_UnexpectedValueException, &error_handling);
724 #ifdef HAVE_GLOB
725 	if (SPL_HAS_FLAG(ctor_flags, DIT_CTOR_GLOB) && !zend_string_starts_with_literal(path, "glob://")) {
726 		path = zend_strpprintf(0, "glob://%s", ZSTR_VAL(path));
727 		spl_filesystem_dir_open(intern, path);
728 		zend_string_release(path);
729 	} else
730 #endif
731 	{
732 		spl_filesystem_dir_open(intern, path);
733 
734 	}
735 	zend_restore_error_handling(&error_handling);
736 }
737 /* }}} */
738 
739 /* {{{ Cronstructs a new dir iterator from a path. */
PHP_METHOD(DirectoryIterator,__construct)740 PHP_METHOD(DirectoryIterator, __construct)
741 {
742 	spl_filesystem_object_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
743 }
744 /* }}} */
745 
746 /* {{{ Rewind dir back to the start */
PHP_METHOD(DirectoryIterator,rewind)747 PHP_METHOD(DirectoryIterator, rewind)
748 {
749 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
750 
751 	if (zend_parse_parameters_none() == FAILURE) {
752 		RETURN_THROWS();
753 	}
754 
755 	CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern);
756 	intern->u.dir.index = 0;
757 	php_stream_rewinddir(intern->u.dir.dirp);
758 	spl_filesystem_dir_read(intern);
759 }
760 /* }}} */
761 
762 /* {{{ Return current dir entry */
PHP_METHOD(DirectoryIterator,key)763 PHP_METHOD(DirectoryIterator, key)
764 {
765 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
766 
767 	if (zend_parse_parameters_none() == FAILURE) {
768 		RETURN_THROWS();
769 	}
770 
771 	CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern);
772 	RETURN_LONG(intern->u.dir.index);
773 }
774 /* }}} */
775 
776 /* {{{ Return this (needed for Iterator interface) */
PHP_METHOD(DirectoryIterator,current)777 PHP_METHOD(DirectoryIterator, current)
778 {
779 	if (zend_parse_parameters_none() == FAILURE) {
780 		RETURN_THROWS();
781 	}
782 
783 	CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)));
784 	RETURN_OBJ_COPY(Z_OBJ_P(ZEND_THIS));
785 }
786 /* }}} */
787 
788 /* {{{ Move to next entry */
PHP_METHOD(DirectoryIterator,next)789 PHP_METHOD(DirectoryIterator, next)
790 {
791 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
792 	bool skip_dots = SPL_HAS_FLAG(intern->flags, SPL_FILE_DIR_SKIPDOTS);
793 
794 	if (zend_parse_parameters_none() == FAILURE) {
795 		RETURN_THROWS();
796 	}
797 
798 	CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern);
799 	intern->u.dir.index++;
800 	do {
801 		spl_filesystem_dir_read(intern);
802 	} while (skip_dots && spl_filesystem_is_dot(intern->u.dir.entry.d_name));
803 	if (intern->file_name) {
804 		zend_string_release(intern->file_name);
805 		intern->file_name = NULL;
806 	}
807 }
808 /* }}} */
809 
810 /* {{{ Seek to the given position */
PHP_METHOD(DirectoryIterator,seek)811 PHP_METHOD(DirectoryIterator, seek)
812 {
813 	spl_filesystem_object *intern    = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
814 	zval retval;
815 	zend_long pos;
816 
817 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &pos) == FAILURE) {
818 		RETURN_THROWS();
819 	}
820 
821 	CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern);
822 	if (intern->u.dir.index > pos) {
823 		/* we first rewind */
824 		zend_call_method_with_0_params(Z_OBJ_P(ZEND_THIS), Z_OBJCE_P(ZEND_THIS), &intern->u.dir.func_rewind, "rewind", NULL);
825 	}
826 
827 	while (intern->u.dir.index < pos) {
828 		bool valid = false;
829 		zend_call_method_with_0_params(Z_OBJ_P(ZEND_THIS), Z_OBJCE_P(ZEND_THIS), &intern->u.dir.func_valid, "valid", &retval);
830 		valid = zend_is_true(&retval);
831 		zval_ptr_dtor(&retval);
832 		if (!valid) {
833 			zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Seek position " ZEND_LONG_FMT " is out of range", pos);
834 			RETURN_THROWS();
835 		}
836 		zend_call_method_with_0_params(Z_OBJ_P(ZEND_THIS), Z_OBJCE_P(ZEND_THIS), &intern->u.dir.func_next, "next", NULL);
837 	}
838 } /* }}} */
839 
840 /* {{{ Check whether dir contains more entries */
PHP_METHOD(DirectoryIterator,valid)841 PHP_METHOD(DirectoryIterator, valid)
842 {
843 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
844 
845 	if (zend_parse_parameters_none() == FAILURE) {
846 		RETURN_THROWS();
847 	}
848 
849 	CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern);
850 	RETURN_BOOL(intern->u.dir.entry.d_name[0] != '\0');
851 }
852 /* }}} */
853 
854 /* {{{ Return the path */
PHP_METHOD(SplFileInfo,getPath)855 PHP_METHOD(SplFileInfo, getPath)
856 {
857 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
858 	zend_string *path;
859 
860 	if (zend_parse_parameters_none() == FAILURE) {
861 		RETURN_THROWS();
862 	}
863 
864   	path = spl_filesystem_object_get_path(intern);
865 	if (path) {
866 		RETURN_STR(path);
867 	} else {
868 		RETURN_EMPTY_STRING();
869 	}
870 }
871 /* }}} */
872 
873 /* {{{ Return filename only */
PHP_METHOD(SplFileInfo,getFilename)874 PHP_METHOD(SplFileInfo, getFilename)
875 {
876 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
877 	zend_string *path;
878 
879 	if (zend_parse_parameters_none() == FAILURE) {
880 		RETURN_THROWS();
881 	}
882 
883 	if (!intern->file_name) {
884 		zend_throw_error(NULL, "Object not initialized");
885 		RETURN_THROWS();
886 	}
887 
888 	path = spl_filesystem_object_get_path(intern);
889 
890 	if (path && ZSTR_LEN(path) && ZSTR_LEN(path) < ZSTR_LEN(intern->file_name)) {
891 		/* +1 to skip the trailing / of the path in the file name */
892 		size_t path_len = ZSTR_LEN(path) + 1;
893 		RETVAL_STRINGL(ZSTR_VAL(intern->file_name) + path_len, ZSTR_LEN(intern->file_name) - path_len);
894 	} else {
895 		RETVAL_STR_COPY(intern->file_name);
896 	}
897 	if (path) {
898 		zend_string_release_ex(path, /* persistent */ false);
899 	}
900 }
901 /* }}} */
902 
903 /* {{{ Return filename of current dir entry */
PHP_METHOD(DirectoryIterator,getFilename)904 PHP_METHOD(DirectoryIterator, getFilename)
905 {
906 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
907 
908 	if (zend_parse_parameters_none() == FAILURE) {
909 		RETURN_THROWS();
910 	}
911 
912 	CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern);
913 	RETURN_STRING(intern->u.dir.entry.d_name);
914 }
915 /* }}} */
916 
917 /* {{{ Returns file extension component of path */
PHP_METHOD(SplFileInfo,getExtension)918 PHP_METHOD(SplFileInfo, getExtension)
919 {
920 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
921 	char *fname = NULL;
922 	const char *p;
923 	size_t flen;
924 	zend_string *path;
925 	size_t idx;
926 	zend_string *ret;
927 
928 	if (zend_parse_parameters_none() == FAILURE) {
929 		RETURN_THROWS();
930 	}
931 
932 	if (!intern->file_name) {
933 		zend_throw_error(NULL, "Object not initialized");
934 		RETURN_THROWS();
935 	}
936 
937 	path = spl_filesystem_object_get_path(intern);
938 
939 	if (path && ZSTR_LEN(path) && ZSTR_LEN(path) < ZSTR_LEN(intern->file_name)) {
940 		fname = ZSTR_VAL(intern->file_name) + ZSTR_LEN(path) + 1;
941 		flen = ZSTR_LEN(intern->file_name) - (ZSTR_LEN(path) + 1);
942 	} else {
943 		fname = ZSTR_VAL(intern->file_name);
944 		flen = ZSTR_LEN(intern->file_name);
945 	}
946 	if (path) {
947 		zend_string_release_ex(path, /* persistent */ false);
948 	}
949 
950 	ret = php_basename(fname, flen, NULL, 0);
951 
952 	p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
953 	if (p) {
954 		idx = p - ZSTR_VAL(ret);
955 		RETVAL_STRINGL(ZSTR_VAL(ret) + idx + 1, ZSTR_LEN(ret) - idx - 1);
956 		zend_string_release_ex(ret, 0);
957 		return;
958 	} else {
959 		zend_string_release_ex(ret, 0);
960 		RETURN_EMPTY_STRING();
961 	}
962 }
963 /* }}}*/
964 
965 /* {{{ Returns the file extension component of path */
PHP_METHOD(DirectoryIterator,getExtension)966 PHP_METHOD(DirectoryIterator, getExtension)
967 {
968 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
969 	const char *p;
970 	size_t idx;
971 	zend_string *fname;
972 
973 	if (zend_parse_parameters_none() == FAILURE) {
974 		RETURN_THROWS();
975 	}
976 
977 	CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern);
978 	fname = php_basename(intern->u.dir.entry.d_name, strlen(intern->u.dir.entry.d_name), NULL, 0);
979 
980 	p = zend_memrchr(ZSTR_VAL(fname), '.', ZSTR_LEN(fname));
981 	if (p) {
982 		idx = p - ZSTR_VAL(fname);
983 		RETVAL_STRINGL(ZSTR_VAL(fname) + idx + 1, ZSTR_LEN(fname) - idx - 1);
984 		zend_string_release_ex(fname, 0);
985 	} else {
986 		zend_string_release_ex(fname, 0);
987 		RETURN_EMPTY_STRING();
988 	}
989 }
990 /* }}} */
991 
992 /* {{{ Returns filename component of path */
PHP_METHOD(SplFileInfo,getBasename)993 PHP_METHOD(SplFileInfo, getBasename)
994 {
995 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
996 	char *fname, *suffix = 0;
997 	size_t flen;
998 	size_t slen = 0;
999 	zend_string *path;
1000 
1001 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &suffix, &slen) == FAILURE) {
1002 		RETURN_THROWS();
1003 	}
1004 
1005 	if (!intern->file_name) {
1006 		zend_throw_error(NULL, "Object not initialized");
1007 		RETURN_THROWS();
1008 	}
1009 
1010 	path = spl_filesystem_object_get_path(intern);
1011 
1012 	if (path && ZSTR_LEN(path) && ZSTR_LEN(path) < ZSTR_LEN(intern->file_name)) {
1013 		/* +1 to skip the trailing / of the path in the file name */
1014 		fname = ZSTR_VAL(intern->file_name) + ZSTR_LEN(path) + 1;
1015 		flen = ZSTR_LEN(intern->file_name) - (ZSTR_LEN(path) + 1);
1016 	} else {
1017 		fname = ZSTR_VAL(intern->file_name);
1018 		flen = ZSTR_LEN(intern->file_name);
1019 	}
1020 	if (path) {
1021 		zend_string_release_ex(path, /* persistent */ false);
1022 	}
1023 
1024 	RETURN_STR(php_basename(fname, flen, suffix, slen));
1025 }
1026 /* }}}*/
1027 
1028 /* {{{ Returns filename component of current dir entry */
PHP_METHOD(DirectoryIterator,getBasename)1029 PHP_METHOD(DirectoryIterator, getBasename)
1030 {
1031 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1032 	char *suffix = 0;
1033 	size_t slen = 0;
1034 	zend_string *fname;
1035 
1036 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &suffix, &slen) == FAILURE) {
1037 		RETURN_THROWS();
1038 	}
1039 
1040 	CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern);
1041 	fname = php_basename(intern->u.dir.entry.d_name, strlen(intern->u.dir.entry.d_name), suffix, slen);
1042 
1043 	RETURN_STR(fname);
1044 }
1045 /* }}} */
1046 
1047 /* {{{ Return path and filename */
PHP_METHOD(SplFileInfo,getPathname)1048 PHP_METHOD(SplFileInfo, getPathname)
1049 {
1050 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1051 	zend_string *path;
1052 
1053 	if (zend_parse_parameters_none() == FAILURE) {
1054 		RETURN_THROWS();
1055 	}
1056 	path = spl_filesystem_object_get_pathname(intern);
1057 	if (path) {
1058 		RETURN_STR_COPY(path);
1059 	} else {
1060 		RETURN_EMPTY_STRING();
1061 	}
1062 }
1063 /* }}} */
1064 
1065 /* {{{ Return getPathname() or getFilename() depending on flags */
PHP_METHOD(FilesystemIterator,key)1066 PHP_METHOD(FilesystemIterator, key)
1067 {
1068 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1069 
1070 	if (zend_parse_parameters_none() == FAILURE) {
1071 		RETURN_THROWS();
1072 	}
1073 
1074 	if (SPL_FILE_DIR_KEY(intern, SPL_FILE_DIR_KEY_AS_FILENAME)) {
1075 		RETURN_STRING(intern->u.dir.entry.d_name);
1076 	} else {
1077 		if (spl_filesystem_object_get_file_name(intern) == FAILURE) {
1078 			RETURN_THROWS();
1079 		}
1080 		RETURN_STR_COPY(intern->file_name);
1081 	}
1082 }
1083 /* }}} */
1084 
1085 /* {{{ Return getFilename(), getFileInfo() or $this depending on flags */
PHP_METHOD(FilesystemIterator,current)1086 PHP_METHOD(FilesystemIterator, current)
1087 {
1088 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1089 
1090 	if (zend_parse_parameters_none() == FAILURE) {
1091 		RETURN_THROWS();
1092 	}
1093 
1094 	if (SPL_FILE_DIR_CURRENT(intern, SPL_FILE_DIR_CURRENT_AS_PATHNAME)) {
1095 		if (spl_filesystem_object_get_file_name(intern) == FAILURE) {
1096 			RETURN_THROWS();
1097 		}
1098 		RETURN_STR_COPY(intern->file_name);
1099 	} else if (SPL_FILE_DIR_CURRENT(intern, SPL_FILE_DIR_CURRENT_AS_FILEINFO)) {
1100 		if (spl_filesystem_object_get_file_name(intern) == FAILURE) {
1101 			RETURN_THROWS();
1102 		}
1103 		spl_filesystem_object_create_type(0, intern, SPL_FS_INFO, NULL, return_value);
1104 	} else {
1105 		RETURN_OBJ_COPY(Z_OBJ_P(ZEND_THIS));
1106 	}
1107 }
1108 /* }}} */
1109 
1110 /* {{{ Returns true if current entry is '.' or  '..' */
PHP_METHOD(DirectoryIterator,isDot)1111 PHP_METHOD(DirectoryIterator, isDot)
1112 {
1113 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1114 
1115 	if (zend_parse_parameters_none() == FAILURE) {
1116 		RETURN_THROWS();
1117 	}
1118 
1119 	CHECK_DIRECTORY_ITERATOR_IS_INITIALIZED(intern);
1120 	RETURN_BOOL(spl_filesystem_is_dot(intern->u.dir.entry.d_name));
1121 }
1122 /* }}} */
1123 
1124 /* {{{ Constructs a new SplFileInfo from a path. */
1125 /* When the constructor gets called the object is already created
1126    by the engine, so we must only call 'additional' initializations.
1127  */
PHP_METHOD(SplFileInfo,__construct)1128 PHP_METHOD(SplFileInfo, __construct)
1129 {
1130 	spl_filesystem_object *intern;
1131 	zend_string *path;
1132 
1133 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "P", &path) == FAILURE) {
1134 		RETURN_THROWS();
1135 	}
1136 
1137 	intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1138 
1139 	spl_filesystem_info_set_filename(intern, path);
1140 
1141 	/* intern->type = SPL_FS_INFO; already set */
1142 }
1143 /* }}} */
1144 
1145 /* {{{ FileInfoFunction */
1146 #define FileInfoFunction(func_name, func_num) \
1147 PHP_METHOD(SplFileInfo, func_name) \
1148 { \
1149 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS)); \
1150 	zend_error_handling error_handling; \
1151 	if (zend_parse_parameters_none() == FAILURE) { \
1152 		RETURN_THROWS(); \
1153 	} \
1154 	if (spl_filesystem_object_get_file_name(intern) == FAILURE) { \
1155 		RETURN_THROWS(); \
1156 	} \
1157 	zend_replace_error_handling(EH_THROW, spl_ce_RuntimeException, &error_handling);\
1158 	php_stat(intern->file_name, func_num, return_value); \
1159 	zend_restore_error_handling(&error_handling); \
1160 }
1161 /* }}} */
1162 
1163 /* {{{ Get file permissions */
FileInfoFunction(getPerms,FS_PERMS)1164 FileInfoFunction(getPerms, FS_PERMS)
1165 /* }}} */
1166 
1167 /* {{{ Get file inode */
1168 FileInfoFunction(getInode, FS_INODE)
1169 /* }}} */
1170 
1171 /* {{{ Get file size */
1172 FileInfoFunction(getSize, FS_SIZE)
1173 /* }}} */
1174 
1175 /* {{{ Get file owner */
1176 FileInfoFunction(getOwner, FS_OWNER)
1177 /* }}} */
1178 
1179 /* {{{ Get file group */
1180 FileInfoFunction(getGroup, FS_GROUP)
1181 /* }}} */
1182 
1183 /* {{{ Get last access time of file */
1184 FileInfoFunction(getATime, FS_ATIME)
1185 /* }}} */
1186 
1187 /* {{{ Get last modification time of file */
1188 FileInfoFunction(getMTime, FS_MTIME)
1189 /* }}} */
1190 
1191 /* {{{ Get inode modification time of file */
1192 FileInfoFunction(getCTime, FS_CTIME)
1193 /* }}} */
1194 
1195 /* {{{ Get file type */
1196 FileInfoFunction(getType, FS_TYPE)
1197 /* }}} */
1198 
1199 /* {{{ Returns true if file can be written */
1200 FileInfoFunction(isWritable, FS_IS_W)
1201 /* }}} */
1202 
1203 /* {{{ Returns true if file can be read */
1204 FileInfoFunction(isReadable, FS_IS_R)
1205 /* }}} */
1206 
1207 /* {{{ Returns true if file is executable */
1208 FileInfoFunction(isExecutable, FS_IS_X)
1209 /* }}} */
1210 
1211 /* {{{ Returns true if file is a regular file */
1212 FileInfoFunction(isFile, FS_IS_FILE)
1213 /* }}} */
1214 
1215 /* {{{ Returns true if file is directory */
1216 FileInfoFunction(isDir, FS_IS_DIR)
1217 /* }}} */
1218 
1219 /* {{{ Returns true if file is symbolic link */
1220 FileInfoFunction(isLink, FS_IS_LINK)
1221 /* }}} */
1222 
1223 /* {{{ Return the target of a symbolic link */
1224 PHP_METHOD(SplFileInfo, getLinkTarget)
1225 {
1226 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1227 	ssize_t ret;
1228 	char buff[MAXPATHLEN];
1229 
1230 	if (zend_parse_parameters_none() == FAILURE) {
1231 		RETURN_THROWS();
1232 	}
1233 
1234 	if (intern->file_name == NULL) {
1235 		if (spl_filesystem_object_get_file_name(intern) == FAILURE) {
1236 			RETURN_THROWS();
1237 		}
1238 	}
1239 #if defined(PHP_WIN32) || defined(HAVE_SYMLINK)
1240 	if (intern->file_name == NULL) {
1241 		zend_value_error("Filename must not be empty");
1242 		RETURN_THROWS();
1243 	}
1244 	if (!IS_ABSOLUTE_PATH(ZSTR_VAL(intern->file_name), ZSTR_LEN(intern->file_name))) {
1245 		char expanded_path[MAXPATHLEN];
1246 		if (!expand_filepath_with_mode(ZSTR_VAL(intern->file_name), expanded_path, NULL, 0, CWD_EXPAND )) {
1247 			php_error_docref(NULL, E_WARNING, "No such file or directory");
1248 			RETURN_FALSE;
1249 		}
1250 		ret = php_sys_readlink(expanded_path, buff, MAXPATHLEN - 1);
1251 	} else {
1252 		ret = php_sys_readlink(ZSTR_VAL(intern->file_name), buff,  MAXPATHLEN-1);
1253 	}
1254 #else
1255 	ret = -1; /* always fail if not implemented */
1256 #endif
1257 
1258 	if (ret == -1) {
1259 		zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Unable to read link %s, error: %s", ZSTR_VAL(intern->file_name), strerror(errno));
1260 		RETVAL_FALSE;
1261 	} else {
1262 		/* Append NULL to the end of the string */
1263 		buff[ret] = '\0';
1264 
1265 		RETVAL_STRINGL(buff, ret);
1266 	}
1267 }
1268 /* }}} */
1269 
1270 /* {{{ Return the resolved path */
PHP_METHOD(SplFileInfo,getRealPath)1271 PHP_METHOD(SplFileInfo, getRealPath)
1272 {
1273 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1274 	char buff[MAXPATHLEN];
1275 	char *filename;
1276 
1277 	if (zend_parse_parameters_none() == FAILURE) {
1278 		RETURN_THROWS();
1279 	}
1280 
1281 	if (intern->type == SPL_FS_DIR && !intern->file_name && intern->u.dir.entry.d_name[0]) {
1282 		if (spl_filesystem_object_get_file_name(intern) == FAILURE) {
1283 			RETURN_THROWS();
1284 		}
1285 	}
1286 
1287 	if (intern->orig_path) {
1288 		filename = ZSTR_VAL(intern->orig_path);
1289 	} else {
1290 		filename = intern->file_name ? ZSTR_VAL(intern->file_name) : NULL;
1291 	}
1292 
1293 
1294 	if (filename && VCWD_REALPATH(filename, buff)) {
1295 #ifdef ZTS
1296 		if (VCWD_ACCESS(buff, F_OK)) {
1297 			RETURN_FALSE;
1298 		} else
1299 #endif
1300 		RETURN_STRING(buff);
1301 	} else {
1302 		RETURN_FALSE;
1303 	}
1304 }
1305 /* }}} */
1306 
1307 /* {{{ Open the current file */
PHP_METHOD(SplFileInfo,openFile)1308 PHP_METHOD(SplFileInfo, openFile)
1309 {
1310 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1311 
1312 	spl_filesystem_object_create_type(ZEND_NUM_ARGS(), intern, SPL_FS_FILE, NULL, return_value);
1313 }
1314 /* }}} */
1315 
1316 /* {{{ Class to use in openFile() */
PHP_METHOD(SplFileInfo,setFileClass)1317 PHP_METHOD(SplFileInfo, setFileClass)
1318 {
1319 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1320 	zend_class_entry *ce = spl_ce_SplFileObject;
1321 
1322 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|C", &ce) == FAILURE) {
1323 		RETURN_THROWS();
1324 	}
1325 
1326 	intern->file_class = ce;
1327 }
1328 /* }}} */
1329 
1330 /* {{{ Class to use in getFileInfo(), getPathInfo() */
PHP_METHOD(SplFileInfo,setInfoClass)1331 PHP_METHOD(SplFileInfo, setInfoClass)
1332 {
1333 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1334 	zend_class_entry *ce = spl_ce_SplFileInfo;
1335 
1336 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|C", &ce) == FAILURE) {
1337 		RETURN_THROWS();
1338 	}
1339 
1340 	intern->info_class = ce;
1341 }
1342 /* }}} */
1343 
1344 /* {{{ Get/copy file info */
PHP_METHOD(SplFileInfo,getFileInfo)1345 PHP_METHOD(SplFileInfo, getFileInfo)
1346 {
1347 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1348 	zend_class_entry *ce = intern->info_class;
1349 
1350 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|C!", &ce) == FAILURE) {
1351 		RETURN_THROWS();
1352 	}
1353 
1354 	spl_filesystem_object_create_type(ZEND_NUM_ARGS(), intern, SPL_FS_INFO, ce, return_value);
1355 }
1356 /* }}} */
1357 
1358 /* {{{ Get/copy file info */
PHP_METHOD(SplFileInfo,getPathInfo)1359 PHP_METHOD(SplFileInfo, getPathInfo)
1360 {
1361 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1362 	zend_class_entry *ce = NULL;
1363 	zend_string *path;
1364 
1365 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|C!", &ce) == FAILURE) {
1366 		RETURN_THROWS();
1367 	}
1368 
1369 	if (ce == NULL) {
1370 		ce = intern->info_class;
1371 	}
1372 
1373 	path = spl_filesystem_object_get_pathname(intern);
1374 	if (path && ZSTR_LEN(path)) {
1375 		zend_string *dpath = zend_string_init(ZSTR_VAL(path), ZSTR_LEN(path), 0);
1376 		ZSTR_LEN(dpath) = zend_dirname(ZSTR_VAL(dpath), ZSTR_LEN(path));
1377 		spl_filesystem_object_create_info(dpath, ce, return_value);
1378 		zend_string_release(dpath);
1379 	}
1380 }
1381 /* }}} */
1382 
1383 /* {{{ */
PHP_METHOD(SplFileInfo,__debugInfo)1384 PHP_METHOD(SplFileInfo, __debugInfo)
1385 {
1386 	if (zend_parse_parameters_none() == FAILURE) {
1387 		RETURN_THROWS();
1388 	}
1389 
1390 	RETURN_ARR(spl_filesystem_object_get_debug_info(Z_OBJ_P(ZEND_THIS)));
1391 } /* }}} */
1392 
1393 /* {{{ */
PHP_METHOD(SplFileInfo,_bad_state_ex)1394 PHP_METHOD(SplFileInfo, _bad_state_ex)
1395 {
1396 	if (zend_parse_parameters_none() == FAILURE) {
1397 		RETURN_THROWS();
1398 	}
1399 	zend_throw_error(NULL, "The parent constructor was not called: the object is in an invalid state");
1400 	RETURN_THROWS();
1401 }
1402 /* }}} */
1403 
1404 /* {{{ Constructs a new dir iterator from a path. */
PHP_METHOD(FilesystemIterator,__construct)1405 PHP_METHOD(FilesystemIterator, __construct)
1406 {
1407 	spl_filesystem_object_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIT_CTOR_FLAGS | SPL_FILE_DIR_SKIPDOTS);
1408 }
1409 /* }}} */
1410 
1411 /* {{{ Rewind dir back to the start */
PHP_METHOD(FilesystemIterator,rewind)1412 PHP_METHOD(FilesystemIterator, rewind)
1413 {
1414 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1415 	bool skip_dots = SPL_HAS_FLAG(intern->flags, SPL_FILE_DIR_SKIPDOTS);
1416 
1417 	if (zend_parse_parameters_none() == FAILURE) {
1418 		RETURN_THROWS();
1419 	}
1420 
1421 	intern->u.dir.index = 0;
1422 	if (intern->u.dir.dirp) {
1423 		php_stream_rewinddir(intern->u.dir.dirp);
1424 	}
1425 	do {
1426 		spl_filesystem_dir_read(intern);
1427 	} while (skip_dots && spl_filesystem_is_dot(intern->u.dir.entry.d_name));
1428 }
1429 /* }}} */
1430 
1431 /* {{{ Get handling flags */
PHP_METHOD(FilesystemIterator,getFlags)1432 PHP_METHOD(FilesystemIterator, getFlags)
1433 {
1434 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1435 
1436 	if (zend_parse_parameters_none() == FAILURE) {
1437 		RETURN_THROWS();
1438 	}
1439 
1440 	RETURN_LONG(intern->flags & (SPL_FILE_DIR_KEY_MODE_MASK | SPL_FILE_DIR_CURRENT_MODE_MASK | SPL_FILE_DIR_OTHERS_MASK));
1441 } /* }}} */
1442 
1443 /* {{{ Set handling flags */
PHP_METHOD(FilesystemIterator,setFlags)1444 PHP_METHOD(FilesystemIterator, setFlags)
1445 {
1446 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1447 	zend_long flags;
1448 
1449 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &flags) == FAILURE) {
1450 		RETURN_THROWS();
1451 	}
1452 
1453 	intern->flags &= ~(SPL_FILE_DIR_KEY_MODE_MASK|SPL_FILE_DIR_CURRENT_MODE_MASK|SPL_FILE_DIR_OTHERS_MASK);
1454 	intern->flags |= ((SPL_FILE_DIR_KEY_MODE_MASK|SPL_FILE_DIR_CURRENT_MODE_MASK|SPL_FILE_DIR_OTHERS_MASK) & flags);
1455 } /* }}} */
1456 
1457 /* {{{ Returns whether current entry is a directory and not '.' or '..' */
PHP_METHOD(RecursiveDirectoryIterator,hasChildren)1458 PHP_METHOD(RecursiveDirectoryIterator, hasChildren)
1459 {
1460 	bool allow_links = 0;
1461 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1462 
1463 	ZEND_PARSE_PARAMETERS_START(0, 1)
1464 		Z_PARAM_OPTIONAL
1465 		Z_PARAM_BOOL(allow_links)
1466 	ZEND_PARSE_PARAMETERS_END();
1467 
1468 	if (spl_filesystem_is_invalid_or_dot(intern->u.dir.entry.d_name)) {
1469 		RETURN_FALSE;
1470 	} else {
1471 		if (intern->u.dir.entry.d_type == DT_DIR) {
1472 			RETURN_TRUE;
1473 		} else if (intern->u.dir.entry.d_type == DT_REG) {
1474 			RETURN_FALSE;
1475 		}
1476 		if (spl_filesystem_object_get_file_name(intern) == FAILURE) {
1477 			RETURN_THROWS();
1478 		}
1479 		php_stat(intern->file_name, FS_LPERMS, return_value);
1480 		if (Z_TYPE_P(return_value) == IS_FALSE) {
1481 			return;
1482 		} else if (!S_ISLNK(Z_LVAL_P(return_value))) {
1483 			RETURN_BOOL(S_ISDIR(Z_LVAL_P(return_value)));
1484 		} else {
1485 			if (!allow_links
1486 			 && !(intern->flags & SPL_FILE_DIR_FOLLOW_SYMLINKS)) {
1487 				RETURN_FALSE;
1488 			}
1489 			php_stat(intern->file_name, FS_IS_DIR, return_value);
1490 		}
1491     }
1492 }
1493 /* }}} */
1494 
1495 /* {{{ Returns an iterator for the current entry if it is a directory */
PHP_METHOD(RecursiveDirectoryIterator,getChildren)1496 PHP_METHOD(RecursiveDirectoryIterator, getChildren)
1497 {
1498 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1499 	spl_filesystem_object *subdir;
1500 	char slash = SPL_HAS_FLAG(intern->flags, SPL_FILE_DIR_UNIXPATHS) ? '/' : DEFAULT_SLASH;
1501 
1502 	if (zend_parse_parameters_none() == FAILURE) {
1503 		RETURN_THROWS();
1504 	}
1505 
1506 	if (spl_filesystem_object_get_file_name(intern) == FAILURE) {
1507 		RETURN_THROWS();
1508 	}
1509 
1510 	zval params[2];
1511 	ZVAL_STR_COPY(&params[0], intern->file_name);
1512 	ZVAL_LONG(&params[1], intern->flags);
1513 
1514 	zend_result is_initialized = object_init_with_constructor(return_value, Z_OBJCE_P(ZEND_THIS), 2, params, NULL);
1515 	zval_ptr_dtor_str(&params[0]);
1516 	if (is_initialized == FAILURE) {
1517 		RETURN_THROWS();
1518 	}
1519 
1520 	subdir = spl_filesystem_from_obj(Z_OBJ_P(return_value));
1521 	if (subdir) {
1522 		size_t name_len = strlen(intern->u.dir.entry.d_name);
1523 		if (intern->u.dir.sub_path && ZSTR_LEN(intern->u.dir.sub_path)) {
1524 			zend_string *sub_path = zend_string_alloc(ZSTR_LEN(intern->u.dir.sub_path) + 1 + name_len, 0);
1525 			memcpy(ZSTR_VAL(sub_path), ZSTR_VAL(intern->u.dir.sub_path), ZSTR_LEN(intern->u.dir.sub_path));
1526 			ZSTR_VAL(sub_path)[ZSTR_LEN(intern->u.dir.sub_path)] = slash;
1527 			memcpy(ZSTR_VAL(sub_path) + ZSTR_LEN(intern->u.dir.sub_path) + 1, intern->u.dir.entry.d_name, name_len);
1528 			ZSTR_VAL(sub_path)[ZSTR_LEN(intern->u.dir.sub_path) + 1 + name_len] = 0;
1529 			subdir->u.dir.sub_path = sub_path;
1530 		} else {
1531 			subdir->u.dir.sub_path = zend_string_init(intern->u.dir.entry.d_name, name_len, 0);
1532 		}
1533 		subdir->info_class = intern->info_class;
1534 		subdir->file_class = intern->file_class;
1535 		subdir->oth = intern->oth;
1536 	}
1537 }
1538 /* }}} */
1539 
1540 /* {{{ Get sub path */
PHP_METHOD(RecursiveDirectoryIterator,getSubPath)1541 PHP_METHOD(RecursiveDirectoryIterator, getSubPath)
1542 {
1543 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1544 
1545 	if (zend_parse_parameters_none() == FAILURE) {
1546 		RETURN_THROWS();
1547 	}
1548 
1549 	if (intern->u.dir.sub_path) {
1550 		RETURN_STR_COPY(intern->u.dir.sub_path);
1551 	} else {
1552 		RETURN_EMPTY_STRING();
1553 	}
1554 }
1555 /* }}} */
1556 
1557 /* {{{ Get sub path and file name */
PHP_METHOD(RecursiveDirectoryIterator,getSubPathname)1558 PHP_METHOD(RecursiveDirectoryIterator, getSubPathname)
1559 {
1560 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1561 	char slash = SPL_HAS_FLAG(intern->flags, SPL_FILE_DIR_UNIXPATHS) ? '/' : DEFAULT_SLASH;
1562 
1563 	if (zend_parse_parameters_none() == FAILURE) {
1564 		RETURN_THROWS();
1565 	}
1566 
1567 	if (intern->u.dir.sub_path) {
1568 		RETURN_NEW_STR(strpprintf(0, "%s%c%s", ZSTR_VAL(intern->u.dir.sub_path), slash, intern->u.dir.entry.d_name));
1569 	} else {
1570 		RETURN_STRING(intern->u.dir.entry.d_name);
1571 	}
1572 }
1573 /* }}} */
1574 
1575 /* {{{ Cronstructs a new dir iterator from a path. */
PHP_METHOD(RecursiveDirectoryIterator,__construct)1576 PHP_METHOD(RecursiveDirectoryIterator, __construct)
1577 {
1578 	spl_filesystem_object_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIT_CTOR_FLAGS);
1579 }
1580 /* }}} */
1581 
1582 #ifdef HAVE_GLOB
1583 /* {{{ Cronstructs a new dir iterator from a glob expression (no glob:// needed). */
PHP_METHOD(GlobIterator,__construct)1584 PHP_METHOD(GlobIterator, __construct)
1585 {
1586 	spl_filesystem_object_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIT_CTOR_FLAGS|DIT_CTOR_GLOB);
1587 }
1588 /* }}} */
1589 
1590 /* {{{ Return the number of directories and files found by globbing */
PHP_METHOD(GlobIterator,count)1591 PHP_METHOD(GlobIterator, count)
1592 {
1593 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
1594 
1595 	if (zend_parse_parameters_none() == FAILURE) {
1596 		RETURN_THROWS();
1597 	}
1598 
1599 	if (spl_intern_is_glob(intern)) {
1600 		RETURN_LONG(php_glob_stream_get_count(intern->u.dir.dirp, NULL));
1601 	} else {
1602 		/* This can happen by abusing destructors. */
1603 		/* TODO: relax this from E_ERROR to an exception */
1604 		php_error_docref(NULL, E_ERROR, "GlobIterator lost glob state");
1605 	}
1606 }
1607 /* }}} */
1608 #endif /* HAVE_GLOB */
1609 
1610 /* {{{ forward declarations to the iterator handlers */
1611 static void spl_filesystem_dir_it_dtor(zend_object_iterator *iter);
1612 static zend_result spl_filesystem_dir_it_valid(zend_object_iterator *iter);
1613 static zval *spl_filesystem_dir_it_current_data(zend_object_iterator *iter);
1614 static void spl_filesystem_dir_it_current_key(zend_object_iterator *iter, zval *key);
1615 static void spl_filesystem_dir_it_move_forward(zend_object_iterator *iter);
1616 static void spl_filesystem_dir_it_rewind(zend_object_iterator *iter);
1617 
1618 /* iterator handler table */
1619 static const zend_object_iterator_funcs spl_filesystem_dir_it_funcs = {
1620 	spl_filesystem_dir_it_dtor,
1621 	spl_filesystem_dir_it_valid,
1622 	spl_filesystem_dir_it_current_data,
1623 	spl_filesystem_dir_it_current_key,
1624 	spl_filesystem_dir_it_move_forward,
1625 	spl_filesystem_dir_it_rewind,
1626 	NULL,
1627 	NULL, /* get_gc */
1628 };
1629 /* }}} */
1630 
1631 /* {{{ spl_ce_dir_get_iterator */
spl_filesystem_dir_get_iterator(zend_class_entry * ce,zval * object,int by_ref)1632 static zend_object_iterator *spl_filesystem_dir_get_iterator(zend_class_entry *ce, zval *object, int by_ref)
1633 {
1634 	spl_filesystem_iterator *iterator;
1635 	spl_filesystem_object *dir_object;
1636 
1637 	if (by_ref) {
1638 		zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
1639 		return NULL;
1640 	}
1641 	dir_object = spl_filesystem_from_obj(Z_OBJ_P(object));
1642 	iterator = spl_filesystem_object_to_iterator(dir_object);
1643 	ZVAL_OBJ_COPY(&iterator->intern.data, Z_OBJ_P(object));
1644 	iterator->intern.funcs = &spl_filesystem_dir_it_funcs;
1645 	/* ->current must be initialized; rewind doesn't set it and valid
1646 	 * doesn't check whether it's set */
1647 	iterator->current = *object;
1648 
1649 	return &iterator->intern;
1650 }
1651 /* }}} */
1652 
1653 /* {{{ spl_filesystem_dir_it_dtor */
spl_filesystem_dir_it_dtor(zend_object_iterator * iter)1654 static void spl_filesystem_dir_it_dtor(zend_object_iterator *iter)
1655 {
1656 	spl_filesystem_iterator *iterator = (spl_filesystem_iterator *)iter;
1657 	zval_ptr_dtor(&iterator->intern.data);
1658 }
1659 /* }}} */
1660 
1661 /* {{{ spl_filesystem_dir_it_valid */
spl_filesystem_dir_it_valid(zend_object_iterator * iter)1662 static zend_result spl_filesystem_dir_it_valid(zend_object_iterator *iter)
1663 {
1664 	spl_filesystem_object *object = spl_filesystem_iterator_to_object((spl_filesystem_iterator *)iter);
1665 
1666 	return object->u.dir.entry.d_name[0] != '\0' ? SUCCESS : FAILURE;
1667 }
1668 /* }}} */
1669 
1670 /* {{{ spl_filesystem_dir_it_current_data */
spl_filesystem_dir_it_current_data(zend_object_iterator * iter)1671 static zval *spl_filesystem_dir_it_current_data(zend_object_iterator *iter)
1672 {
1673 	spl_filesystem_iterator *iterator = (spl_filesystem_iterator *)iter;
1674 
1675 	return &iterator->current;
1676 }
1677 /* }}} */
1678 
1679 /* {{{ spl_filesystem_dir_it_current_key */
spl_filesystem_dir_it_current_key(zend_object_iterator * iter,zval * key)1680 static void spl_filesystem_dir_it_current_key(zend_object_iterator *iter, zval *key)
1681 {
1682 	spl_filesystem_object *object = spl_filesystem_iterator_to_object((spl_filesystem_iterator *)iter);
1683 
1684 	ZVAL_LONG(key, object->u.dir.index);
1685 }
1686 /* }}} */
1687 
1688 /* {{{ spl_filesystem_dir_it_move_forward */
spl_filesystem_dir_it_move_forward(zend_object_iterator * iter)1689 static void spl_filesystem_dir_it_move_forward(zend_object_iterator *iter)
1690 {
1691 	spl_filesystem_object *object = spl_filesystem_iterator_to_object((spl_filesystem_iterator *)iter);
1692 
1693 	object->u.dir.index++;
1694 	spl_filesystem_dir_read(object);
1695 	if (object->file_name) {
1696 		zend_string_release(object->file_name);
1697 		object->file_name = NULL;
1698 	}
1699 }
1700 /* }}} */
1701 
1702 /* {{{ spl_filesystem_dir_it_rewind */
spl_filesystem_dir_it_rewind(zend_object_iterator * iter)1703 static void spl_filesystem_dir_it_rewind(zend_object_iterator *iter)
1704 {
1705 	spl_filesystem_object *object = spl_filesystem_iterator_to_object((spl_filesystem_iterator *)iter);
1706 
1707 	object->u.dir.index = 0;
1708 	if (object->u.dir.dirp) {
1709 		php_stream_rewinddir(object->u.dir.dirp);
1710 	}
1711 	spl_filesystem_dir_read(object);
1712 }
1713 /* }}} */
1714 
1715 /* {{{ spl_filesystem_tree_it_dtor */
spl_filesystem_tree_it_dtor(zend_object_iterator * iter)1716 static void spl_filesystem_tree_it_dtor(zend_object_iterator *iter)
1717 {
1718 	spl_filesystem_iterator *iterator = (spl_filesystem_iterator *)iter;
1719 	zval_ptr_dtor(&iterator->intern.data);
1720 	zval_ptr_dtor(&iterator->current);
1721 }
1722 /* }}} */
1723 
1724 /* {{{ spl_filesystem_tree_it_current_data */
spl_filesystem_tree_it_current_data(zend_object_iterator * iter)1725 static zval *spl_filesystem_tree_it_current_data(zend_object_iterator *iter)
1726 {
1727 	spl_filesystem_iterator *iterator = (spl_filesystem_iterator *)iter;
1728 	spl_filesystem_object   *object   = spl_filesystem_iterator_to_object(iterator);
1729 
1730 	if (SPL_FILE_DIR_CURRENT(object, SPL_FILE_DIR_CURRENT_AS_PATHNAME)) {
1731 		if (Z_ISUNDEF(iterator->current)) {
1732 			if (spl_filesystem_object_get_file_name(object) == FAILURE) {
1733 				return NULL;
1734 			}
1735 			ZVAL_STR_COPY(&iterator->current, object->file_name);
1736 		}
1737 		return &iterator->current;
1738 	} else if (SPL_FILE_DIR_CURRENT(object, SPL_FILE_DIR_CURRENT_AS_FILEINFO)) {
1739 		if (Z_ISUNDEF(iterator->current)) {
1740 			if (spl_filesystem_object_get_file_name(object) == FAILURE) {
1741 				return NULL;
1742 			}
1743 			spl_filesystem_object_create_type(0, object, SPL_FS_INFO, NULL, &iterator->current);
1744 		}
1745 		return &iterator->current;
1746 	} else {
1747 		return &iterator->intern.data;
1748 	}
1749 }
1750 /* }}} */
1751 
1752 /* {{{ spl_filesystem_tree_it_current_key */
spl_filesystem_tree_it_current_key(zend_object_iterator * iter,zval * key)1753 static void spl_filesystem_tree_it_current_key(zend_object_iterator *iter, zval *key)
1754 {
1755 	spl_filesystem_object *object = spl_filesystem_iterator_to_object((spl_filesystem_iterator *)iter);
1756 
1757 	if (SPL_FILE_DIR_KEY(object, SPL_FILE_DIR_KEY_AS_FILENAME)) {
1758 		ZVAL_STRING(key, object->u.dir.entry.d_name);
1759 	} else {
1760 		if (spl_filesystem_object_get_file_name(object) == FAILURE) {
1761 			return;
1762 		}
1763 		ZVAL_STR_COPY(key, object->file_name);
1764 	}
1765 }
1766 /* }}} */
1767 
1768 /* {{{ spl_filesystem_tree_it_move_forward */
spl_filesystem_tree_it_move_forward(zend_object_iterator * iter)1769 static void spl_filesystem_tree_it_move_forward(zend_object_iterator *iter)
1770 {
1771 	spl_filesystem_iterator *iterator = (spl_filesystem_iterator *)iter;
1772 	spl_filesystem_object   *object   = spl_filesystem_iterator_to_object(iterator);
1773 	bool skip_dots = SPL_HAS_FLAG(object->flags, SPL_FILE_DIR_SKIPDOTS);
1774 
1775 	object->u.dir.index++;
1776 	do {
1777 		spl_filesystem_dir_read(object);
1778 	} while (skip_dots && spl_filesystem_is_dot(object->u.dir.entry.d_name));
1779 	if (object->file_name) {
1780 		zend_string_release(object->file_name);
1781 		object->file_name = NULL;
1782 	}
1783 	if (!Z_ISUNDEF(iterator->current)) {
1784 		zval_ptr_dtor(&iterator->current);
1785 		ZVAL_UNDEF(&iterator->current);
1786 	}
1787 }
1788 /* }}} */
1789 
1790 /* {{{ spl_filesystem_tree_it_rewind */
spl_filesystem_tree_it_rewind(zend_object_iterator * iter)1791 static void spl_filesystem_tree_it_rewind(zend_object_iterator *iter)
1792 {
1793 	spl_filesystem_iterator *iterator = (spl_filesystem_iterator *)iter;
1794 	spl_filesystem_object   *object   = spl_filesystem_iterator_to_object(iterator);
1795 	bool skip_dots = SPL_HAS_FLAG(object->flags, SPL_FILE_DIR_SKIPDOTS);
1796 
1797 	object->u.dir.index = 0;
1798 	if (object->u.dir.dirp) {
1799 		php_stream_rewinddir(object->u.dir.dirp);
1800 	}
1801 	do {
1802 		spl_filesystem_dir_read(object);
1803 	} while (skip_dots && spl_filesystem_is_dot(object->u.dir.entry.d_name));
1804 	if (!Z_ISUNDEF(iterator->current)) {
1805 		zval_ptr_dtor(&iterator->current);
1806 		ZVAL_UNDEF(&iterator->current);
1807 	}
1808 }
1809 /* }}} */
1810 
1811 /* {{{ iterator handler table */
1812 static const zend_object_iterator_funcs spl_filesystem_tree_it_funcs = {
1813 	spl_filesystem_tree_it_dtor,
1814 	spl_filesystem_dir_it_valid,
1815 	spl_filesystem_tree_it_current_data,
1816 	spl_filesystem_tree_it_current_key,
1817 	spl_filesystem_tree_it_move_forward,
1818 	spl_filesystem_tree_it_rewind,
1819 	NULL,
1820 	NULL, /* get_gc */
1821 };
1822 /* }}} */
1823 
1824 /* {{{ spl_ce_dir_get_iterator */
spl_filesystem_tree_get_iterator(zend_class_entry * ce,zval * object,int by_ref)1825 static zend_object_iterator *spl_filesystem_tree_get_iterator(zend_class_entry *ce, zval *object, int by_ref)
1826 {
1827 	spl_filesystem_iterator *iterator;
1828 	spl_filesystem_object *dir_object;
1829 
1830 	if (by_ref) {
1831 		zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
1832 		return NULL;
1833 	}
1834 	dir_object = spl_filesystem_from_obj(Z_OBJ_P(object));
1835 	iterator = spl_filesystem_object_to_iterator(dir_object);
1836 
1837 	ZVAL_OBJ_COPY(&iterator->intern.data, Z_OBJ_P(object));
1838 	iterator->intern.funcs = &spl_filesystem_tree_it_funcs;
1839 
1840 	return &iterator->intern;
1841 }
1842 /* }}} */
1843 
spl_filesystem_file_cannot_read(spl_filesystem_object * intern)1844 static ZEND_COLD void spl_filesystem_file_cannot_read(spl_filesystem_object *intern)
1845 {
1846 	zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot read from file %s", ZSTR_VAL(intern->file_name));
1847 }
1848 
spl_filesystem_file_read_ex(spl_filesystem_object * intern,bool silent,zend_long line_add,bool csv)1849 static zend_result spl_filesystem_file_read_ex(spl_filesystem_object *intern, bool silent, zend_long line_add, bool csv)
1850 {
1851 	char *buf;
1852 	size_t line_len = 0;
1853 
1854 	spl_filesystem_file_free_line(intern);
1855 
1856 	if (php_stream_eof(intern->u.file.stream)) {
1857 		if (!silent) {
1858 			spl_filesystem_file_cannot_read(intern);
1859 		}
1860 		return FAILURE;
1861 	}
1862 
1863 	if (intern->u.file.max_line_len > 0) {
1864 		buf = safe_emalloc((intern->u.file.max_line_len + 1), sizeof(char), 0);
1865 		if (php_stream_get_line(intern->u.file.stream, buf, intern->u.file.max_line_len + 1, &line_len) == NULL) {
1866 			efree(buf);
1867 			buf = NULL;
1868 		} else {
1869 			buf[line_len] = '\0';
1870 		}
1871 	} else {
1872 		buf = php_stream_get_line(intern->u.file.stream, NULL, 0, &line_len);
1873 	}
1874 
1875 	if (!buf) {
1876 		intern->u.file.current_line = ZSTR_EMPTY_ALLOC();
1877 	} else {
1878 		if (!csv && SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_DROP_NEW_LINE)) {
1879 			if (line_len > 0 && buf[line_len - 1] == '\n') {
1880 				line_len--;
1881 				if (line_len > 0 && buf[line_len - 1] == '\r') {
1882 					line_len--;
1883 				}
1884 				buf[line_len] = '\0';
1885 			}
1886 		}
1887 
1888 		intern->u.file.current_line = zend_string_init(buf, line_len, /* persistent */ false);
1889 		efree(buf);
1890 	}
1891 	intern->u.file.current_line_num += line_add;
1892 
1893 	return SUCCESS;
1894 } /* }}} */
1895 
spl_filesystem_file_read(spl_filesystem_object * intern,bool silent,bool csv)1896 static inline zend_result spl_filesystem_file_read(spl_filesystem_object *intern, bool silent, bool csv)
1897 {
1898 	zend_long line_add = (intern->u.file.current_line) ? 1 : 0;
1899 	return spl_filesystem_file_read_ex(intern, silent, line_add, csv);
1900 }
1901 
is_line_empty(const spl_filesystem_object * intern)1902 static bool is_line_empty(const spl_filesystem_object *intern)
1903 {
1904 	const char *current_line = ZSTR_VAL(intern->u.file.current_line);
1905 	size_t current_line_len = ZSTR_LEN(intern->u.file.current_line);
1906 	return current_line_len == 0 || (
1907 		SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_CSV)
1908 		&& SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_DROP_NEW_LINE)
1909 		&& (
1910 			(current_line_len == 1 && current_line[0] == '\n')
1911 			|| (current_line_len == 2 && current_line[0] == '\r' && current_line[1] == '\n')
1912 		)
1913 	);
1914 }
1915 
spl_filesystem_file_read_csv(spl_filesystem_object * intern,char delimiter,char enclosure,int escape,zval * return_value,bool silent)1916 static zend_result spl_filesystem_file_read_csv(spl_filesystem_object *intern, char delimiter, char enclosure, int escape, zval *return_value, bool silent) /* {{{ */
1917 {
1918 	do {
1919 		zend_result ret = spl_filesystem_file_read(intern, silent, /* csv */ true);
1920 		if (ret != SUCCESS) {
1921 			return ret;
1922 		}
1923 	} while (is_line_empty(intern) && SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_SKIP_EMPTY));
1924 
1925 	/* We need to duplicate the current line content as php_fgetcsv() will free it.
1926 	 * This is because it might reach the end of the line when it's in an enclosure and
1927 	 * thus must fetch the next line from the stream */
1928 	size_t buf_len = ZSTR_LEN(intern->u.file.current_line);
1929 	char *buf = estrndup(ZSTR_VAL(intern->u.file.current_line), buf_len);
1930 
1931 	if (!Z_ISUNDEF(intern->u.file.current_zval)) {
1932 		zval_ptr_dtor(&intern->u.file.current_zval);
1933 		ZVAL_UNDEF(&intern->u.file.current_zval);
1934 	}
1935 
1936 	HashTable *values = php_fgetcsv(intern->u.file.stream, delimiter, enclosure, escape, buf_len, buf);
1937 	if (values == NULL) {
1938 		values = php_bc_fgetcsv_empty_line();
1939 	}
1940 	ZVAL_ARR(&intern->u.file.current_zval, values);
1941 	if (return_value) {
1942 		ZVAL_COPY(return_value, &intern->u.file.current_zval);
1943 	}
1944 	return SUCCESS;
1945 }
1946 /* }}} */
1947 
spl_filesystem_file_read_line_ex(zval * this_ptr,spl_filesystem_object * intern,bool silent)1948 static zend_result spl_filesystem_file_read_line_ex(zval * this_ptr, spl_filesystem_object *intern, bool silent) /* {{{ */
1949 {
1950 	zval retval;
1951 
1952 	/* 1) use fgetcsv? 2) overloaded call the function, 3) do it directly */
1953 	if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_CSV)) {
1954 		return spl_filesystem_file_read_csv(intern, intern->u.file.delimiter, intern->u.file.enclosure, intern->u.file.escape, NULL, silent);
1955 	}
1956 	if (intern->u.file.func_getCurr->common.scope != spl_ce_SplFileObject) {
1957 		spl_filesystem_file_free_line(intern);
1958 
1959 		if (php_stream_eof(intern->u.file.stream)) {
1960 			if (!silent) {
1961 				spl_filesystem_file_cannot_read(intern);
1962 			}
1963 			return FAILURE;
1964 		}
1965 		zend_call_method_with_0_params(Z_OBJ_P(this_ptr), Z_OBJCE_P(this_ptr), &intern->u.file.func_getCurr, "getCurrentLine", &retval);
1966 		if (Z_ISUNDEF(retval)) {
1967 			return FAILURE;
1968 		}
1969 
1970 		if (Z_TYPE(retval) != IS_STRING) {
1971 			zend_type_error("%s::getCurrentLine(): Return value must be of type string, %s returned",
1972 				ZSTR_VAL(Z_OBJCE_P(this_ptr)->name), zend_zval_value_name(&retval));
1973 			zval_ptr_dtor(&retval);
1974 			return FAILURE;
1975 		}
1976 
1977 		if (intern->u.file.current_line || !Z_ISUNDEF(intern->u.file.current_zval)) {
1978 			intern->u.file.current_line_num++;
1979 		}
1980 		spl_filesystem_file_free_line(intern);
1981 		intern->u.file.current_line = zend_string_copy(Z_STR(retval));
1982 		zval_ptr_dtor(&retval);
1983 		return SUCCESS;
1984 	} else {
1985 		return spl_filesystem_file_read(intern, silent, /* csv */ false);
1986 	}
1987 } /* }}} */
1988 
spl_filesystem_file_read_line(zval * this_ptr,spl_filesystem_object * intern,bool silent)1989 static zend_result spl_filesystem_file_read_line(zval * this_ptr, spl_filesystem_object *intern, bool silent) /* {{{ */
1990 {
1991 	zend_result ret = spl_filesystem_file_read_line_ex(this_ptr, intern, silent);
1992 
1993 	while (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_SKIP_EMPTY) && ret == SUCCESS && is_line_empty(intern)) {
1994 		spl_filesystem_file_free_line(intern);
1995 		ret = spl_filesystem_file_read_line_ex(this_ptr, intern, silent);
1996 	}
1997 
1998 	return ret;
1999 }
2000 /* }}} */
2001 
spl_filesystem_file_rewind(zval * this_ptr,spl_filesystem_object * intern)2002 static void spl_filesystem_file_rewind(zval * this_ptr, spl_filesystem_object *intern) /* {{{ */
2003 {
2004 	if (!intern->u.file.stream) {
2005 		zend_throw_error(NULL, "Object not initialized");
2006 		return;
2007 	}
2008 	if (-1 == php_stream_rewind(intern->u.file.stream)) {
2009 		zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot rewind file %s", ZSTR_VAL(intern->file_name));
2010 		return;
2011 	}
2012 
2013 	spl_filesystem_file_free_line(intern);
2014 	intern->u.file.current_line_num = 0;
2015 
2016 	if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_AHEAD)) {
2017 		spl_filesystem_file_read_line(this_ptr, intern, true);
2018 	}
2019 } /* }}} */
2020 
2021 /* {{{ Construct a new file object */
PHP_METHOD(SplFileObject,__construct)2022 PHP_METHOD(SplFileObject, __construct)
2023 {
2024 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2025 	zend_string *file_name = NULL;
2026 	zend_string *open_mode = ZSTR_CHAR('r');
2027 	zval *stream_context = NULL;
2028 	bool use_include_path = 0;
2029 	size_t path_len;
2030 	zend_error_handling error_handling;
2031 
2032 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|Sbr!", &file_name, &open_mode, &use_include_path, &stream_context) == FAILURE) {
2033 		RETURN_THROWS();
2034 	}
2035 
2036 	/* Prevent reinitialization of Object */
2037 	if (UNEXPECTED(intern->u.file.stream)) {
2038 		zend_throw_error(NULL, "Cannot call constructor twice");
2039 		RETURN_THROWS();
2040 	}
2041 
2042 	intern->u.file.open_mode = zend_string_copy(open_mode);
2043 	/* file_name and zcontext are copied by spl_filesystem_file_open() */
2044 	intern->file_name = file_name;
2045 	intern->u.file.zcontext = stream_context;
2046 
2047 	/* spl_filesystem_file_open() can generate E_WARNINGs which we want to promote to exceptions */
2048 	zend_replace_error_handling(EH_THROW, spl_ce_RuntimeException, &error_handling);
2049 	zend_result retval = spl_filesystem_file_open(intern, use_include_path);
2050 	zend_restore_error_handling(&error_handling);
2051 	if (retval == FAILURE) {
2052 		RETURN_THROWS();
2053 	}
2054 
2055 	path_len = strlen(intern->u.file.stream->orig_path);
2056 
2057 	if (path_len > 1 && IS_SLASH_AT(intern->u.file.stream->orig_path, path_len-1)) {
2058 		path_len--;
2059 	}
2060 
2061 	while (path_len > 1 && !IS_SLASH_AT(intern->u.file.stream->orig_path, path_len-1)) {
2062 		path_len--;
2063 	}
2064 
2065 	if (path_len) {
2066 		path_len--;
2067 	}
2068 
2069 	intern->path = zend_string_init(intern->u.file.stream->orig_path, path_len, 0);
2070 } /* }}} */
2071 
2072 /* {{{ Construct a new temp file object */
PHP_METHOD(SplTempFileObject,__construct)2073 PHP_METHOD(SplTempFileObject, __construct)
2074 {
2075 	zend_string *file_name;
2076 	zend_long max_memory = PHP_STREAM_MAX_MEM;
2077 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2078 	zend_error_handling error_handling;
2079 
2080 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &max_memory) == FAILURE) {
2081 		RETURN_THROWS();
2082 	}
2083 
2084 	/* Prevent reinitialization of Object */
2085 	if (UNEXPECTED(intern->u.file.stream)) {
2086 		zend_throw_error(NULL, "Cannot call constructor twice");
2087 		RETURN_THROWS();
2088 	}
2089 
2090 	if (max_memory < 0) {
2091 		file_name = ZSTR_INIT_LITERAL("php://memory", 0);
2092 	} else if (ZEND_NUM_ARGS()) {
2093 		file_name = zend_strpprintf(0, "php://temp/maxmemory:" ZEND_LONG_FMT, max_memory);
2094 	} else {
2095 		file_name = ZSTR_INIT_LITERAL("php://temp", 0);
2096 	}
2097 	intern->file_name = file_name;
2098 	intern->u.file.open_mode = ZSTR_INIT_LITERAL("wb", 0);
2099 
2100 	/* spl_filesystem_file_open() can generate E_WARNINGs which we want to promote to exceptions */
2101 	zend_replace_error_handling(EH_THROW, spl_ce_RuntimeException, &error_handling);
2102 	if (spl_filesystem_file_open(intern, /* use_include_path */ false) == SUCCESS) {
2103 		intern->path = ZSTR_EMPTY_ALLOC();
2104 	}
2105 	zend_string_release(file_name);
2106 	zend_restore_error_handling(&error_handling);
2107 } /* }}} */
2108 
2109 /* {{{ Rewind the file and read the first line */
PHP_METHOD(SplFileObject,rewind)2110 PHP_METHOD(SplFileObject, rewind)
2111 {
2112 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2113 
2114 	if (zend_parse_parameters_none() == FAILURE) {
2115 		RETURN_THROWS();
2116 	}
2117 
2118 	spl_filesystem_file_rewind(ZEND_THIS, intern);
2119 } /* }}} */
2120 
2121 /* {{{ Return whether end of file is reached */
PHP_METHOD(SplFileObject,eof)2122 PHP_METHOD(SplFileObject, eof)
2123 {
2124 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2125 
2126 	if (zend_parse_parameters_none() == FAILURE) {
2127 		RETURN_THROWS();
2128 	}
2129 
2130 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2131 
2132 	RETURN_BOOL(php_stream_eof(intern->u.file.stream));
2133 } /* }}} */
2134 
2135 /* {{{ Return !eof() */
PHP_METHOD(SplFileObject,valid)2136 PHP_METHOD(SplFileObject, valid)
2137 {
2138 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2139 
2140 	if (zend_parse_parameters_none() == FAILURE) {
2141 		RETURN_THROWS();
2142 	}
2143 
2144 	if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_AHEAD)) {
2145 		RETURN_BOOL(intern->u.file.current_line || !Z_ISUNDEF(intern->u.file.current_zval));
2146 	}
2147 	if (!intern->u.file.stream) {
2148 		RETURN_FALSE;
2149 	}
2150 	RETURN_BOOL(!php_stream_eof(intern->u.file.stream));
2151 } /* }}} */
2152 
2153 /* {{{ Return next line from file */
PHP_METHOD(SplFileObject,fgets)2154 PHP_METHOD(SplFileObject, fgets)
2155 {
2156 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2157 
2158 	if (zend_parse_parameters_none() == FAILURE) {
2159 		RETURN_THROWS();
2160 	}
2161 
2162 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2163 
2164 	if (spl_filesystem_file_read_ex(intern, /* silent */ false, /* line_add */ 1, /* csv */ false) == FAILURE) {
2165 		RETURN_THROWS();
2166 	}
2167 	RETURN_STR_COPY(intern->u.file.current_line);
2168 } /* }}} */
2169 
2170 /* {{{ Return current line from file */
PHP_METHOD(SplFileObject,current)2171 PHP_METHOD(SplFileObject, current)
2172 {
2173 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2174 
2175 	if (zend_parse_parameters_none() == FAILURE) {
2176 		RETURN_THROWS();
2177 	}
2178 
2179 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2180 
2181 	if (!intern->u.file.current_line && Z_ISUNDEF(intern->u.file.current_zval)) {
2182 		spl_filesystem_file_read_line(ZEND_THIS, intern, true);
2183 	}
2184 	if (intern->u.file.current_line && (!SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_CSV) || Z_ISUNDEF(intern->u.file.current_zval))) {
2185 		RETURN_STR_COPY(intern->u.file.current_line);
2186 	} else if (!Z_ISUNDEF(intern->u.file.current_zval)) {
2187 		ZEND_ASSERT(!Z_ISREF(intern->u.file.current_zval));
2188 		ZEND_ASSERT(Z_TYPE(intern->u.file.current_zval) == IS_ARRAY);
2189 		RETURN_COPY(&intern->u.file.current_zval);
2190 	}
2191 	RETURN_FALSE;
2192 } /* }}} */
2193 
2194 /* {{{ Return line number */
PHP_METHOD(SplFileObject,key)2195 PHP_METHOD(SplFileObject, key)
2196 {
2197 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2198 
2199 	if (zend_parse_parameters_none() == FAILURE) {
2200 		RETURN_THROWS();
2201 	}
2202 
2203 	/* Do not read the next line to support correct counting with fgetc()
2204 	if (!intern->u.file.current_line) {
2205 		spl_filesystem_file_read_line(ZEND_THIS, intern);
2206 	} */
2207 	RETURN_LONG(intern->u.file.current_line_num);
2208 } /* }}} */
2209 
2210 /* {{{ Read next line */
PHP_METHOD(SplFileObject,next)2211 PHP_METHOD(SplFileObject, next)
2212 {
2213 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2214 
2215 	if (zend_parse_parameters_none() == FAILURE) {
2216 		RETURN_THROWS();
2217 	}
2218 
2219 	spl_filesystem_file_free_line(intern);
2220 	if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_AHEAD)) {
2221 		spl_filesystem_file_read_line(ZEND_THIS, intern, true);
2222 	}
2223 	intern->u.file.current_line_num++;
2224 } /* }}} */
2225 
2226 /* {{{ Set file handling flags */
PHP_METHOD(SplFileObject,setFlags)2227 PHP_METHOD(SplFileObject, setFlags)
2228 {
2229 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2230 
2231 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &intern->flags) == FAILURE) {
2232 		RETURN_THROWS();
2233 	}
2234 } /* }}} */
2235 
2236 /* {{{ Get file handling flags */
PHP_METHOD(SplFileObject,getFlags)2237 PHP_METHOD(SplFileObject, getFlags)
2238 {
2239 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2240 
2241 	if (zend_parse_parameters_none() == FAILURE) {
2242 		RETURN_THROWS();
2243 	}
2244 
2245 	RETURN_LONG(intern->flags & SPL_FILE_OBJECT_MASK);
2246 } /* }}} */
2247 
2248 /* {{{ Set maximum line length */
PHP_METHOD(SplFileObject,setMaxLineLen)2249 PHP_METHOD(SplFileObject, setMaxLineLen)
2250 {
2251 	zend_long max_len;
2252 
2253 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2254 
2255 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &max_len) == FAILURE) {
2256 		RETURN_THROWS();
2257 	}
2258 
2259 	if (max_len < 0) {
2260 		zend_argument_value_error(1, "must be greater than or equal to 0");
2261 		RETURN_THROWS();
2262 	}
2263 
2264 	intern->u.file.max_line_len = max_len;
2265 } /* }}} */
2266 
2267 /* {{{ Get maximum line length */
PHP_METHOD(SplFileObject,getMaxLineLen)2268 PHP_METHOD(SplFileObject, getMaxLineLen)
2269 {
2270 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2271 
2272 	if (zend_parse_parameters_none() == FAILURE) {
2273 		RETURN_THROWS();
2274 	}
2275 
2276 	RETURN_LONG((zend_long)intern->u.file.max_line_len);
2277 } /* }}} */
2278 
2279 /* {{{ Return false */
PHP_METHOD(SplFileObject,hasChildren)2280 PHP_METHOD(SplFileObject, hasChildren)
2281 {
2282 	if (zend_parse_parameters_none() == FAILURE) {
2283 		RETURN_THROWS();
2284 	}
2285 
2286 	RETURN_FALSE;
2287 } /* }}} */
2288 
2289 /* {{{ Read NULL */
PHP_METHOD(SplFileObject,getChildren)2290 PHP_METHOD(SplFileObject, getChildren)
2291 {
2292 	if (zend_parse_parameters_none() == FAILURE) {
2293 		RETURN_THROWS();
2294 	}
2295 	/* return NULL */
2296 } /* }}} */
2297 
spl_csv_enclosure_param_handling(const zend_string * escape_str,const spl_filesystem_object * intern,uint32_t arg_num)2298 static int spl_csv_enclosure_param_handling(const zend_string* escape_str, const spl_filesystem_object *intern, uint32_t arg_num)
2299 {
2300 	if (escape_str == NULL) {
2301 		if (intern->u.file.is_escape_default) {
2302 			php_error_docref(NULL, E_DEPRECATED, "the $escape parameter must be provided,"
2303 				" as its default value will change,"
2304 				" either explicitly or via SplFileObject::setCsvControl()");
2305 			if (UNEXPECTED(EG(exception))) {
2306 				return PHP_CSV_ESCAPE_ERROR;
2307 			}
2308 		}
2309 		return intern->u.file.escape;
2310 	} else {
2311 		return php_csv_handle_escape_argument(escape_str, arg_num);
2312 	}
2313 }
2314 
2315 /* {{{ Return current line as CSV */
PHP_METHOD(SplFileObject,fgetcsv)2316 PHP_METHOD(SplFileObject, fgetcsv)
2317 {
2318 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2319 	char delimiter = intern->u.file.delimiter, enclosure = intern->u.file.enclosure;
2320 	char *delim = NULL, *enclo = NULL;
2321 	size_t d_len = 0, e_len = 0;
2322 	zend_string *escape_str = NULL;
2323 
2324 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ssS", &delim, &d_len, &enclo, &e_len, &escape_str) == FAILURE) {
2325 		RETURN_THROWS();
2326 	}
2327 
2328 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2329 
2330 	if (delim) {
2331 		if (d_len != 1) {
2332 			zend_argument_value_error(1, "must be a single character");
2333 			RETURN_THROWS();
2334 		}
2335 		delimiter = delim[0];
2336 	}
2337 	if (enclo) {
2338 		if (e_len != 1) {
2339 			zend_argument_value_error(2, "must be a single character");
2340 			RETURN_THROWS();
2341 		}
2342 		enclosure = enclo[0];
2343 	}
2344 	int escape_char = spl_csv_enclosure_param_handling(escape_str, intern, 3);
2345 	if (escape_char == PHP_CSV_ESCAPE_ERROR) {
2346 		RETURN_THROWS();
2347 	}
2348 
2349 	if (spl_filesystem_file_read_csv(intern, delimiter, enclosure, escape_char, return_value, true) == FAILURE) {
2350 		RETURN_FALSE;
2351 	}
2352 }
2353 /* }}} */
2354 
2355 /* {{{ Output a field array as a CSV line */
PHP_METHOD(SplFileObject,fputcsv)2356 PHP_METHOD(SplFileObject, fputcsv)
2357 {
2358 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2359 	char delimiter = intern->u.file.delimiter, enclosure = intern->u.file.enclosure;
2360 	char *delim = NULL, *enclo = NULL;
2361 	size_t d_len = 0, e_len = 0;
2362 	zend_long ret;
2363 	zval *fields = NULL;
2364 	zend_string *escape_str = NULL;
2365 	zend_string *eol = NULL;
2366 
2367 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|ssSS", &fields, &delim, &d_len, &enclo, &e_len, &escape_str, &eol) == FAILURE) {
2368 		RETURN_THROWS();
2369 	}
2370 
2371 	if (delim) {
2372 		if (d_len != 1) {
2373 			zend_argument_value_error(2, "must be a single character");
2374 			RETURN_THROWS();
2375 		}
2376 		delimiter = delim[0];
2377 	}
2378 	if (enclo) {
2379 		if (e_len != 1) {
2380 			zend_argument_value_error(3, "must be a single character");
2381 			RETURN_THROWS();
2382 		}
2383 		enclosure = enclo[0];
2384 	}
2385 	int escape_char = spl_csv_enclosure_param_handling(escape_str, intern, 4);
2386 	if (escape_char == PHP_CSV_ESCAPE_ERROR) {
2387 		RETURN_THROWS();
2388 	}
2389 
2390 	ret = php_fputcsv(intern->u.file.stream, fields, delimiter, enclosure, escape_char, eol);
2391 	if (ret < 0) {
2392 		RETURN_FALSE;
2393 	}
2394 	RETURN_LONG(ret);
2395 }
2396 /* }}} */
2397 
2398 /* {{{ Set the delimiter, enclosure and escape character used in fgetcsv */
PHP_METHOD(SplFileObject,setCsvControl)2399 PHP_METHOD(SplFileObject, setCsvControl)
2400 {
2401 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2402 	char delimiter = ',', enclosure = '"';
2403 	char *delim = NULL, *enclo = NULL;
2404 	size_t d_len = 0, e_len = 0;
2405 	zend_string *escape_str = NULL;
2406 
2407 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ssS", &delim, &d_len, &enclo, &e_len, &escape_str) == FAILURE) {
2408 		RETURN_THROWS();
2409 	}
2410 
2411 	if (delim) {
2412 		if (d_len != 1) {
2413 			zend_argument_value_error(1, "must be a single character");
2414 			RETURN_THROWS();
2415 		}
2416 		delimiter = delim[0];
2417 	}
2418 	if (enclo) {
2419 		if (e_len != 1) {
2420 			zend_argument_value_error(2, "must be a single character");
2421 			RETURN_THROWS();
2422 		}
2423 		enclosure = enclo[0];
2424 	}
2425 	int escape_char = php_csv_handle_escape_argument(escape_str, 3);
2426 	if (escape_char == PHP_CSV_ESCAPE_ERROR) {
2427 		RETURN_THROWS();
2428 	}
2429 	if (escape_str != NULL) {
2430 		intern->u.file.is_escape_default = false;
2431 	}
2432 
2433 	intern->u.file.delimiter = delimiter;
2434 	intern->u.file.enclosure = enclosure;
2435 	intern->u.file.escape    = escape_char;
2436 }
2437 /* }}} */
2438 
2439 /* {{{ Get the delimiter, enclosure and escape character used in fgetcsv */
PHP_METHOD(SplFileObject,getCsvControl)2440 PHP_METHOD(SplFileObject, getCsvControl)
2441 {
2442 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2443 	char delimiter[2], enclosure[2], escape[2];
2444 
2445 	if (zend_parse_parameters_none() == FAILURE) {
2446 		RETURN_THROWS();
2447 	}
2448 
2449 	array_init(return_value);
2450 
2451 	delimiter[0] = intern->u.file.delimiter;
2452 	delimiter[1] = '\0';
2453 	enclosure[0] = intern->u.file.enclosure;
2454 	enclosure[1] = '\0';
2455 	if (intern->u.file.escape == PHP_CSV_NO_ESCAPE) {
2456 		escape[0] = '\0';
2457 	} else {
2458 		escape[0] = (unsigned char) intern->u.file.escape;
2459 		escape[1] = '\0';
2460 	}
2461 
2462 	add_next_index_string(return_value, delimiter);
2463 	add_next_index_string(return_value, enclosure);
2464 	add_next_index_string(return_value, escape);
2465 }
2466 /* }}} */
2467 
2468 /* {{{ Portable file locking */
PHP_METHOD(SplFileObject,flock)2469 PHP_METHOD(SplFileObject, flock)
2470 {
2471 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2472 	zval *wouldblock = NULL;
2473 	zend_long operation = 0;
2474 
2475 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|z", &operation, &wouldblock) == FAILURE) {
2476 		RETURN_THROWS();
2477 	}
2478 
2479 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2480 
2481 	php_flock_common(intern->u.file.stream, operation, 1, wouldblock, return_value);
2482 }
2483 /* }}} */
2484 
2485 /* {{{ Flush the file */
PHP_METHOD(SplFileObject,fflush)2486 PHP_METHOD(SplFileObject, fflush)
2487 {
2488 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2489 
2490 	if (zend_parse_parameters_none() == FAILURE) {
2491 		RETURN_THROWS();
2492 	}
2493 
2494 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2495 
2496 	RETURN_BOOL(!php_stream_flush(intern->u.file.stream));
2497 } /* }}} */
2498 
2499 /* {{{ Return current file position */
PHP_METHOD(SplFileObject,ftell)2500 PHP_METHOD(SplFileObject, ftell)
2501 {
2502 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2503 	zend_long ret;
2504 
2505 	if (zend_parse_parameters_none() == FAILURE) {
2506 		RETURN_THROWS();
2507 	}
2508 
2509 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2510 
2511 	ret = php_stream_tell(intern->u.file.stream);
2512 
2513 	if (ret == -1) {
2514 		RETURN_FALSE;
2515 	} else {
2516 		RETURN_LONG(ret);
2517 	}
2518 } /* }}} */
2519 
2520 /* {{{ Seek to a position */
PHP_METHOD(SplFileObject,fseek)2521 PHP_METHOD(SplFileObject, fseek)
2522 {
2523 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2524 	zend_long pos, whence = SEEK_SET;
2525 
2526 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &pos, &whence) == FAILURE) {
2527 		RETURN_THROWS();
2528 	}
2529 
2530 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2531 
2532 	spl_filesystem_file_free_line(intern);
2533 	RETURN_LONG(php_stream_seek(intern->u.file.stream, pos, (int)whence));
2534 } /* }}} */
2535 
2536 /* {{{ Get a character from the file */
PHP_METHOD(SplFileObject,fgetc)2537 PHP_METHOD(SplFileObject, fgetc)
2538 {
2539 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2540 
2541 	if (zend_parse_parameters_none() == FAILURE) {
2542 		RETURN_THROWS();
2543 	}
2544 
2545 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2546 
2547 	spl_filesystem_file_free_line(intern);
2548 
2549 	int result = php_stream_getc(intern->u.file.stream);
2550 
2551 	if (result == EOF) {
2552 		RETURN_FALSE;
2553 	}
2554 	if (result == '\n') {
2555 		intern->u.file.current_line_num++;
2556 	}
2557 
2558 	RETURN_STR(ZSTR_CHAR((zend_uchar)result));
2559 } /* }}} */
2560 
2561 /* {{{ Output all remaining data from a file pointer */
PHP_METHOD(SplFileObject,fpassthru)2562 PHP_METHOD(SplFileObject, fpassthru)
2563 {
2564 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2565 
2566 	if (zend_parse_parameters_none() == FAILURE) {
2567 		RETURN_THROWS();
2568 	}
2569 
2570 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2571 
2572 	RETURN_LONG(php_stream_passthru(intern->u.file.stream));
2573 } /* }}} */
2574 
2575 /* {{{ Implements a mostly ANSI compatible fscanf() */
PHP_METHOD(SplFileObject,fscanf)2576 PHP_METHOD(SplFileObject, fscanf)
2577 {
2578 	uint32_t num_varargs = 0;
2579 	zend_string *format_str;
2580 	zval *varargs= NULL;
2581 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2582 
2583 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S*", &format_str, &varargs, &num_varargs) == FAILURE) {
2584 		RETURN_THROWS();
2585 	}
2586 
2587 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2588 
2589 	/* Get next line */
2590 	if (spl_filesystem_file_read(intern, /* silent */ false, /* csv */ false) == FAILURE) {
2591 		RETURN_THROWS();
2592 	}
2593 
2594 	int result = php_sscanf_internal(ZSTR_VAL(intern->u.file.current_line), ZSTR_VAL(format_str), (int)num_varargs, varargs, 0, return_value);
2595 
2596 	if (SCAN_ERROR_WRONG_PARAM_COUNT == result) {
2597 		WRONG_PARAM_COUNT;
2598 	}
2599 }
2600 /* }}} */
2601 
2602 /* {{{ Binary-safe file write */
PHP_METHOD(SplFileObject,fwrite)2603 PHP_METHOD(SplFileObject, fwrite)
2604 {
2605 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2606 	char *str;
2607 	size_t str_len;
2608 	zend_long length = 0;
2609 	bool length_is_null = true;
2610 	ssize_t written;
2611 
2612 	ZEND_PARSE_PARAMETERS_START(1, 2)
2613 		Z_PARAM_STRING(str, str_len)
2614 		Z_PARAM_OPTIONAL
2615 		Z_PARAM_LONG_OR_NULL(length, length_is_null)
2616 	ZEND_PARSE_PARAMETERS_END();
2617 
2618 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2619 
2620 	if (!length_is_null) {
2621 		if (length >= 0) {
2622 			str_len = MIN((size_t)length, str_len);
2623 		} else {
2624 			/* Negative length given, nothing to write */
2625 			str_len = 0;
2626 		}
2627 	}
2628 	if (!str_len) {
2629 		RETURN_LONG(0);
2630 	}
2631 
2632 	written = php_stream_write(intern->u.file.stream, str, str_len);
2633 	if (written < 0) {
2634 		RETURN_FALSE;
2635 	}
2636 	RETURN_LONG(written);
2637 } /* }}} */
2638 
PHP_METHOD(SplFileObject,fread)2639 PHP_METHOD(SplFileObject, fread)
2640 {
2641 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2642 	zend_long length = 0;
2643 	zend_string *str;
2644 
2645 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &length) == FAILURE) {
2646 		RETURN_THROWS();
2647 	}
2648 
2649 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2650 
2651 	if (length <= 0) {
2652 		zend_argument_value_error(1, "must be greater than 0");
2653 		RETURN_THROWS();
2654 	}
2655 
2656 	str = php_stream_read_to_str(intern->u.file.stream, length);
2657 	if (!str) {
2658 		RETURN_FALSE;
2659 	}
2660 	RETURN_STR(str);
2661 }
2662 
2663 /* {{{ Stat() on a filehandle */
PHP_METHOD(SplFileObject,fstat)2664 PHP_METHOD(SplFileObject, fstat)
2665 {
2666 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2667 
2668 	if (zend_parse_parameters_none() == FAILURE) {
2669 		RETURN_THROWS();
2670 	}
2671 
2672 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2673 
2674 	php_fstat(intern->u.file.stream, return_value);
2675 }
2676 /* }}} */
2677 
2678 /* {{{ Truncate file to 'size' length */
PHP_METHOD(SplFileObject,ftruncate)2679 PHP_METHOD(SplFileObject, ftruncate)
2680 {
2681 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2682 	zend_long size;
2683 
2684 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &size) == FAILURE) {
2685 		RETURN_THROWS();
2686 	}
2687 
2688 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2689 
2690 	if (!php_stream_truncate_supported(intern->u.file.stream)) {
2691 		zend_throw_exception_ex(spl_ce_LogicException, 0, "Can't truncate file %s", ZSTR_VAL(intern->file_name));
2692 		RETURN_THROWS();
2693 	}
2694 
2695 	RETURN_BOOL(0 == php_stream_truncate_set_size(intern->u.file.stream, size));
2696 } /* }}} */
2697 
2698 /* {{{ Seek to specified line */
PHP_METHOD(SplFileObject,seek)2699 PHP_METHOD(SplFileObject, seek)
2700 {
2701 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2702 	zend_long line_pos, i;
2703 
2704 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &line_pos) == FAILURE) {
2705 		RETURN_THROWS();
2706 	}
2707 
2708 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2709 
2710 	if (line_pos < 0) {
2711 		zend_argument_value_error(1, "must be greater than or equal to 0");
2712 		RETURN_THROWS();
2713 	}
2714 
2715 	spl_filesystem_file_rewind(ZEND_THIS, intern);
2716 
2717 	for (i = 0; i < line_pos; i++) {
2718 		if (spl_filesystem_file_read_line(ZEND_THIS, intern, true) == FAILURE) {
2719 			return;
2720 		}
2721 	}
2722 	if (line_pos > 0 && !SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_AHEAD)) {
2723 		intern->u.file.current_line_num++;
2724 		spl_filesystem_file_free_line(intern);
2725 	}
2726 } /* }}} */
2727 
PHP_METHOD(SplFileObject,__toString)2728 PHP_METHOD(SplFileObject, __toString)
2729 {
2730 	if (zend_parse_parameters_none() == FAILURE) {
2731 		RETURN_THROWS();
2732 	}
2733 
2734 	spl_filesystem_object *intern = spl_filesystem_from_obj(Z_OBJ_P(ZEND_THIS));
2735 
2736 	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
2737 
2738 	if (!intern->u.file.current_line) {
2739 		ZEND_ASSERT(Z_ISUNDEF(intern->u.file.current_zval));
2740 		zend_result result = spl_filesystem_file_read_line(ZEND_THIS, intern, false);
2741 		if (UNEXPECTED(result != SUCCESS)) {
2742 			RETURN_THROWS();
2743 		}
2744 	}
2745 
2746 	RETURN_STR_COPY(intern->u.file.current_line);
2747 }
2748 
2749 /* {{{ PHP_MINIT_FUNCTION(spl_directory) */
PHP_MINIT_FUNCTION(spl_directory)2750 PHP_MINIT_FUNCTION(spl_directory)
2751 {
2752 	spl_ce_SplFileInfo = register_class_SplFileInfo(zend_ce_stringable);
2753 	spl_ce_SplFileInfo->create_object = spl_filesystem_object_new;
2754 	spl_ce_SplFileInfo->default_object_handlers = &spl_filesystem_object_handlers;
2755 
2756 	memcpy(&spl_filesystem_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2757 	spl_filesystem_object_handlers.offset = XtOffsetOf(spl_filesystem_object, std);
2758 	spl_filesystem_object_handlers.clone_obj = spl_filesystem_object_clone;
2759 	spl_filesystem_object_handlers.dtor_obj = spl_filesystem_object_destroy_object;
2760 	spl_filesystem_object_handlers.free_obj = spl_filesystem_object_free_storage;
2761 
2762 	spl_ce_DirectoryIterator = register_class_DirectoryIterator(spl_ce_SplFileInfo, spl_ce_SeekableIterator);
2763 	spl_ce_DirectoryIterator->create_object = spl_filesystem_object_new;
2764 	spl_ce_DirectoryIterator->get_iterator = spl_filesystem_dir_get_iterator;
2765 
2766 	spl_ce_FilesystemIterator = register_class_FilesystemIterator(spl_ce_DirectoryIterator);
2767 	spl_ce_FilesystemIterator->create_object = spl_filesystem_object_new;
2768 	spl_ce_FilesystemIterator->get_iterator = spl_filesystem_tree_get_iterator;
2769 
2770 	spl_ce_RecursiveDirectoryIterator = register_class_RecursiveDirectoryIterator(spl_ce_FilesystemIterator, spl_ce_RecursiveIterator);
2771 	spl_ce_RecursiveDirectoryIterator->create_object = spl_filesystem_object_new;
2772 
2773 	memcpy(&spl_filesystem_object_check_handlers, &spl_filesystem_object_handlers, sizeof(zend_object_handlers));
2774 	spl_filesystem_object_check_handlers.clone_obj = NULL;
2775 	spl_filesystem_object_check_handlers.get_method = spl_filesystem_object_get_method_check;
2776 
2777 #ifdef HAVE_GLOB
2778 	spl_ce_GlobIterator = register_class_GlobIterator(spl_ce_FilesystemIterator, zend_ce_countable);
2779 	spl_ce_GlobIterator->create_object = spl_filesystem_object_new;
2780 	spl_ce_GlobIterator->default_object_handlers = &spl_filesystem_object_check_handlers;
2781 #endif
2782 
2783 	spl_ce_SplFileObject = register_class_SplFileObject(spl_ce_SplFileInfo, spl_ce_RecursiveIterator, spl_ce_SeekableIterator);
2784 	spl_ce_SplFileObject->default_object_handlers = &spl_filesystem_object_check_handlers;
2785 	spl_ce_SplFileObject->create_object = spl_filesystem_object_new;
2786 
2787 	spl_ce_SplTempFileObject = register_class_SplTempFileObject(spl_ce_SplFileObject);
2788 	spl_ce_SplTempFileObject->create_object = spl_filesystem_object_new;
2789 
2790 	return SUCCESS;
2791 }
2792 /* }}} */
2793