1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2018 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Marcus Boerger <helly@php.net>                              |
16   +----------------------------------------------------------------------+
17 */
18
19#ifdef HAVE_CONFIG_H
20#include "config.h"
21#endif
22
23#include "php.h"
24#include "php_ini.h"
25#include "php_main.h"
26#include "ext/standard/info.h"
27#include "php_spl.h"
28#include "spl_functions.h"
29#include "spl_engine.h"
30#include "spl_array.h"
31#include "spl_directory.h"
32#include "spl_iterators.h"
33#include "spl_exceptions.h"
34#include "spl_observer.h"
35#include "spl_dllist.h"
36#include "spl_fixedarray.h"
37#include "spl_heap.h"
38#include "zend_exceptions.h"
39#include "zend_interfaces.h"
40#include "ext/standard/php_mt_rand.h"
41#include "main/snprintf.h"
42
43#ifdef COMPILE_DL_SPL
44ZEND_GET_MODULE(spl)
45#endif
46
47ZEND_DECLARE_MODULE_GLOBALS(spl)
48
49#define SPL_DEFAULT_FILE_EXTENSIONS ".inc,.php"
50
51static zend_function *spl_autoload_fn = NULL;
52static zend_function *spl_autoload_call_fn = NULL;
53
54/* {{{ PHP_GINIT_FUNCTION
55 */
56static PHP_GINIT_FUNCTION(spl)
57{
58	spl_globals->autoload_extensions     = NULL;
59	spl_globals->autoload_functions      = NULL;
60	spl_globals->autoload_running        = 0;
61}
62/* }}} */
63
64static zend_class_entry * spl_find_ce_by_name(zend_string *name, zend_bool autoload)
65{
66	zend_class_entry *ce;
67
68	if (!autoload) {
69		zend_string *lc_name = zend_string_tolower(name);
70
71		ce = zend_hash_find_ptr(EG(class_table), lc_name);
72		zend_string_release(lc_name);
73	} else {
74 		ce = zend_lookup_class(name);
75 	}
76 	if (ce == NULL) {
77		php_error_docref(NULL, E_WARNING, "Class %s does not exist%s", ZSTR_VAL(name), autoload ? " and could not be loaded" : "");
78		return NULL;
79	}
80
81	return ce;
82}
83
84/* {{{ proto array class_parents(object instance [, bool autoload = true])
85 Return an array containing the names of all parent classes */
86PHP_FUNCTION(class_parents)
87{
88	zval *obj;
89	zend_class_entry *parent_class, *ce;
90	zend_bool autoload = 1;
91
92	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &obj, &autoload) == FAILURE) {
93		RETURN_FALSE;
94	}
95
96	if (Z_TYPE_P(obj) != IS_OBJECT && Z_TYPE_P(obj) != IS_STRING) {
97		php_error_docref(NULL, E_WARNING, "object or string expected");
98		RETURN_FALSE;
99	}
100
101	if (Z_TYPE_P(obj) == IS_STRING) {
102		if (NULL == (ce = spl_find_ce_by_name(Z_STR_P(obj), autoload))) {
103			RETURN_FALSE;
104		}
105	} else {
106		ce = Z_OBJCE_P(obj);
107	}
108
109	array_init(return_value);
110	parent_class = ce->parent;
111	while (parent_class) {
112		spl_add_class_name(return_value, parent_class, 0, 0);
113		parent_class = parent_class->parent;
114	}
115}
116/* }}} */
117
118/* {{{ proto array class_implements(mixed what [, bool autoload ])
119 Return all classes and interfaces implemented by SPL */
120PHP_FUNCTION(class_implements)
121{
122	zval *obj;
123	zend_bool autoload = 1;
124	zend_class_entry *ce;
125
126	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &obj, &autoload) == FAILURE) {
127		RETURN_FALSE;
128	}
129	if (Z_TYPE_P(obj) != IS_OBJECT && Z_TYPE_P(obj) != IS_STRING) {
130		php_error_docref(NULL, E_WARNING, "object or string expected");
131		RETURN_FALSE;
132	}
133
134	if (Z_TYPE_P(obj) == IS_STRING) {
135		if (NULL == (ce = spl_find_ce_by_name(Z_STR_P(obj), autoload))) {
136			RETURN_FALSE;
137		}
138	} else {
139		ce = Z_OBJCE_P(obj);
140	}
141
142	array_init(return_value);
143	spl_add_interfaces(return_value, ce, 1, ZEND_ACC_INTERFACE);
144}
145/* }}} */
146
147/* {{{ proto array class_uses(mixed what [, bool autoload ])
148 Return all traits used by a class. */
149PHP_FUNCTION(class_uses)
150{
151	zval *obj;
152	zend_bool autoload = 1;
153	zend_class_entry *ce;
154
155	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &obj, &autoload) == FAILURE) {
156		RETURN_FALSE;
157	}
158	if (Z_TYPE_P(obj) != IS_OBJECT && Z_TYPE_P(obj) != IS_STRING) {
159		php_error_docref(NULL, E_WARNING, "object or string expected");
160		RETURN_FALSE;
161	}
162
163	if (Z_TYPE_P(obj) == IS_STRING) {
164		if (NULL == (ce = spl_find_ce_by_name(Z_STR_P(obj), autoload))) {
165			RETURN_FALSE;
166		}
167	} else {
168		ce = Z_OBJCE_P(obj);
169	}
170
171	array_init(return_value);
172	spl_add_traits(return_value, ce, 1, ZEND_ACC_TRAIT);
173}
174/* }}} */
175
176#define SPL_ADD_CLASS(class_name, z_list, sub, allow, ce_flags) \
177	spl_add_classes(spl_ce_ ## class_name, z_list, sub, allow, ce_flags)
178
179#define SPL_LIST_CLASSES(z_list, sub, allow, ce_flags) \
180	SPL_ADD_CLASS(AppendIterator, z_list, sub, allow, ce_flags); \
181	SPL_ADD_CLASS(ArrayIterator, z_list, sub, allow, ce_flags); \
182	SPL_ADD_CLASS(ArrayObject, z_list, sub, allow, ce_flags); \
183	SPL_ADD_CLASS(BadFunctionCallException, z_list, sub, allow, ce_flags); \
184	SPL_ADD_CLASS(BadMethodCallException, z_list, sub, allow, ce_flags); \
185	SPL_ADD_CLASS(CachingIterator, z_list, sub, allow, ce_flags); \
186	SPL_ADD_CLASS(CallbackFilterIterator, z_list, sub, allow, ce_flags); \
187	SPL_ADD_CLASS(DirectoryIterator, z_list, sub, allow, ce_flags); \
188	SPL_ADD_CLASS(DomainException, z_list, sub, allow, ce_flags); \
189	SPL_ADD_CLASS(EmptyIterator, z_list, sub, allow, ce_flags); \
190	SPL_ADD_CLASS(FilesystemIterator, z_list, sub, allow, ce_flags); \
191	SPL_ADD_CLASS(FilterIterator, z_list, sub, allow, ce_flags); \
192	SPL_ADD_CLASS(GlobIterator, z_list, sub, allow, ce_flags); \
193	SPL_ADD_CLASS(InfiniteIterator, z_list, sub, allow, ce_flags); \
194	SPL_ADD_CLASS(InvalidArgumentException, z_list, sub, allow, ce_flags); \
195	SPL_ADD_CLASS(IteratorIterator, z_list, sub, allow, ce_flags); \
196	SPL_ADD_CLASS(LengthException, z_list, sub, allow, ce_flags); \
197	SPL_ADD_CLASS(LimitIterator, z_list, sub, allow, ce_flags); \
198	SPL_ADD_CLASS(LogicException, z_list, sub, allow, ce_flags); \
199	SPL_ADD_CLASS(MultipleIterator, z_list, sub, allow, ce_flags); \
200	SPL_ADD_CLASS(NoRewindIterator, z_list, sub, allow, ce_flags); \
201	SPL_ADD_CLASS(OuterIterator, z_list, sub, allow, ce_flags); \
202	SPL_ADD_CLASS(OutOfBoundsException, z_list, sub, allow, ce_flags); \
203	SPL_ADD_CLASS(OutOfRangeException, z_list, sub, allow, ce_flags); \
204	SPL_ADD_CLASS(OverflowException, z_list, sub, allow, ce_flags); \
205	SPL_ADD_CLASS(ParentIterator, z_list, sub, allow, ce_flags); \
206	SPL_ADD_CLASS(RangeException, z_list, sub, allow, ce_flags); \
207	SPL_ADD_CLASS(RecursiveArrayIterator, z_list, sub, allow, ce_flags); \
208	SPL_ADD_CLASS(RecursiveCachingIterator, z_list, sub, allow, ce_flags); \
209	SPL_ADD_CLASS(RecursiveCallbackFilterIterator, z_list, sub, allow, ce_flags); \
210	SPL_ADD_CLASS(RecursiveDirectoryIterator, z_list, sub, allow, ce_flags); \
211	SPL_ADD_CLASS(RecursiveFilterIterator, z_list, sub, allow, ce_flags); \
212	SPL_ADD_CLASS(RecursiveIterator, z_list, sub, allow, ce_flags); \
213	SPL_ADD_CLASS(RecursiveIteratorIterator, z_list, sub, allow, ce_flags); \
214	SPL_ADD_CLASS(RecursiveRegexIterator, z_list, sub, allow, ce_flags); \
215	SPL_ADD_CLASS(RecursiveTreeIterator, z_list, sub, allow, ce_flags); \
216	SPL_ADD_CLASS(RegexIterator, z_list, sub, allow, ce_flags); \
217	SPL_ADD_CLASS(RuntimeException, z_list, sub, allow, ce_flags); \
218	SPL_ADD_CLASS(SeekableIterator, z_list, sub, allow, ce_flags); \
219	SPL_ADD_CLASS(SplDoublyLinkedList, z_list, sub, allow, ce_flags); \
220	SPL_ADD_CLASS(SplFileInfo, z_list, sub, allow, ce_flags); \
221	SPL_ADD_CLASS(SplFileObject, z_list, sub, allow, ce_flags); \
222	SPL_ADD_CLASS(SplFixedArray, z_list, sub, allow, ce_flags); \
223	SPL_ADD_CLASS(SplHeap, z_list, sub, allow, ce_flags); \
224	SPL_ADD_CLASS(SplMinHeap, z_list, sub, allow, ce_flags); \
225	SPL_ADD_CLASS(SplMaxHeap, z_list, sub, allow, ce_flags); \
226	SPL_ADD_CLASS(SplObjectStorage, z_list, sub, allow, ce_flags); \
227	SPL_ADD_CLASS(SplObserver, z_list, sub, allow, ce_flags); \
228	SPL_ADD_CLASS(SplPriorityQueue, z_list, sub, allow, ce_flags); \
229	SPL_ADD_CLASS(SplQueue, z_list, sub, allow, ce_flags); \
230	SPL_ADD_CLASS(SplStack, z_list, sub, allow, ce_flags); \
231	SPL_ADD_CLASS(SplSubject, z_list, sub, allow, ce_flags); \
232	SPL_ADD_CLASS(SplTempFileObject, z_list, sub, allow, ce_flags); \
233	SPL_ADD_CLASS(UnderflowException, z_list, sub, allow, ce_flags); \
234	SPL_ADD_CLASS(UnexpectedValueException, z_list, sub, allow, ce_flags); \
235
236/* {{{ proto array spl_classes()
237 Return an array containing the names of all clsses and interfaces defined in SPL */
238PHP_FUNCTION(spl_classes)
239{
240	array_init(return_value);
241
242	SPL_LIST_CLASSES(return_value, 0, 0, 0)
243}
244/* }}} */
245
246static int spl_autoload(zend_string *class_name, zend_string *lc_name, const char *ext, int ext_len) /* {{{ */
247{
248	char *class_file;
249	int class_file_len;
250	zval dummy;
251	zend_file_handle file_handle;
252	zend_op_array *new_op_array;
253	zval result;
254	int ret;
255
256	class_file_len = (int)spprintf(&class_file, 0, "%s%.*s", ZSTR_VAL(lc_name), ext_len, ext);
257
258#if DEFAULT_SLASH != '\\'
259	{
260		char *ptr = class_file;
261		char *end = ptr + class_file_len;
262
263		while ((ptr = memchr(ptr, '\\', (end - ptr))) != NULL) {
264			*ptr = DEFAULT_SLASH;
265		}
266	}
267#endif
268
269	ret = php_stream_open_for_zend_ex(class_file, &file_handle, USE_PATH|STREAM_OPEN_FOR_INCLUDE);
270
271	if (ret == SUCCESS) {
272		zend_string *opened_path;
273		if (!file_handle.opened_path) {
274			file_handle.opened_path = zend_string_init(class_file, class_file_len, 0);
275		}
276		opened_path = zend_string_copy(file_handle.opened_path);
277		ZVAL_NULL(&dummy);
278		if (zend_hash_add(&EG(included_files), opened_path, &dummy)) {
279			new_op_array = zend_compile_file(&file_handle, ZEND_REQUIRE);
280			zend_destroy_file_handle(&file_handle);
281		} else {
282			new_op_array = NULL;
283			zend_file_handle_dtor(&file_handle);
284		}
285		zend_string_release_ex(opened_path, 0);
286		if (new_op_array) {
287			ZVAL_UNDEF(&result);
288			zend_execute(new_op_array, &result);
289
290			destroy_op_array(new_op_array);
291			efree(new_op_array);
292			if (!EG(exception)) {
293				zval_ptr_dtor(&result);
294			}
295
296			efree(class_file);
297			return zend_hash_exists(EG(class_table), lc_name);
298		}
299	}
300	efree(class_file);
301	return 0;
302} /* }}} */
303
304/* {{{ proto void spl_autoload(string class_name [, string file_extensions])
305 Default implementation for __autoload() */
306PHP_FUNCTION(spl_autoload)
307{
308	int pos_len, pos1_len;
309	char *pos, *pos1;
310	zend_string *class_name, *lc_name, *file_exts = SPL_G(autoload_extensions);
311
312	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|S", &class_name, &file_exts) == FAILURE) {
313		RETURN_FALSE;
314	}
315
316	if (file_exts == NULL) { /* autoload_extensions is not initialized, set to defaults */
317		pos = SPL_DEFAULT_FILE_EXTENSIONS;
318		pos_len = sizeof(SPL_DEFAULT_FILE_EXTENSIONS) - 1;
319	} else {
320		pos = ZSTR_VAL(file_exts);
321		pos_len = (int)ZSTR_LEN(file_exts);
322	}
323
324	lc_name = zend_string_tolower(class_name);
325	while (pos && *pos && !EG(exception)) {
326		pos1 = strchr(pos, ',');
327		if (pos1) {
328			pos1_len = (int)(pos1 - pos);
329		} else {
330			pos1_len = pos_len;
331		}
332		if (spl_autoload(class_name, lc_name, pos, pos1_len)) {
333			break; /* loaded */
334		}
335		pos = pos1 ? pos1 + 1 : NULL;
336		pos_len = pos1? pos_len - pos1_len - 1 : 0;
337	}
338	zend_string_release(lc_name);
339} /* }}} */
340
341/* {{{ proto string spl_autoload_extensions([string file_extensions])
342 Register and return default file extensions for spl_autoload */
343PHP_FUNCTION(spl_autoload_extensions)
344{
345	zend_string *file_exts = NULL;
346
347	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &file_exts) == FAILURE) {
348		return;
349	}
350	if (file_exts) {
351		if (SPL_G(autoload_extensions)) {
352			zend_string_release_ex(SPL_G(autoload_extensions), 0);
353		}
354		SPL_G(autoload_extensions) = zend_string_copy(file_exts);
355	}
356
357	if (SPL_G(autoload_extensions) == NULL) {
358		RETURN_STRINGL(SPL_DEFAULT_FILE_EXTENSIONS, sizeof(SPL_DEFAULT_FILE_EXTENSIONS) - 1);
359	} else {
360		zend_string_addref(SPL_G(autoload_extensions));
361		RETURN_STR(SPL_G(autoload_extensions));
362	}
363} /* }}} */
364
365typedef struct {
366	zend_function *func_ptr;
367	zval obj;
368	zval closure;
369	zend_class_entry *ce;
370} autoload_func_info;
371
372static void autoload_func_info_dtor(zval *element)
373{
374	autoload_func_info *alfi = (autoload_func_info*)Z_PTR_P(element);
375	if (!Z_ISUNDEF(alfi->obj)) {
376		zval_ptr_dtor(&alfi->obj);
377	}
378	if (alfi->func_ptr &&
379		UNEXPECTED(alfi->func_ptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
380		zend_string_release_ex(alfi->func_ptr->common.function_name, 0);
381		zend_free_trampoline(alfi->func_ptr);
382	}
383	if (!Z_ISUNDEF(alfi->closure)) {
384		zval_ptr_dtor(&alfi->closure);
385	}
386	efree(alfi);
387}
388
389/* {{{ proto void spl_autoload_call(string class_name)
390 Try all registerd autoload function to load the requested class */
391PHP_FUNCTION(spl_autoload_call)
392{
393	zval *class_name, retval;
394	zend_string *lc_name, *func_name;
395	autoload_func_info *alfi;
396
397	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &class_name) == FAILURE || Z_TYPE_P(class_name) != IS_STRING) {
398		return;
399	}
400
401	if (SPL_G(autoload_functions)) {
402		HashPosition pos;
403		zend_ulong num_idx;
404		zend_function *func;
405		zend_fcall_info fci;
406		zend_fcall_info_cache fcic;
407		zend_class_entry *called_scope = zend_get_called_scope(execute_data);
408		int l_autoload_running = SPL_G(autoload_running);
409
410		SPL_G(autoload_running) = 1;
411		lc_name = zend_string_tolower(Z_STR_P(class_name));
412
413		fci.size = sizeof(fci);
414		fci.retval = &retval;
415		fci.param_count = 1;
416		fci.params = class_name;
417		fci.no_separation = 1;
418
419		ZVAL_UNDEF(&fci.function_name); /* Unused */
420
421		zend_hash_internal_pointer_reset_ex(SPL_G(autoload_functions), &pos);
422		while (zend_hash_get_current_key_ex(SPL_G(autoload_functions), &func_name, &num_idx, &pos) == HASH_KEY_IS_STRING) {
423			alfi = zend_hash_get_current_data_ptr_ex(SPL_G(autoload_functions), &pos);
424			func = alfi->func_ptr;
425			if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
426				func = emalloc(sizeof(zend_op_array));
427				memcpy(func, alfi->func_ptr, sizeof(zend_op_array));
428				zend_string_addref(func->op_array.function_name);
429			}
430			ZVAL_UNDEF(&retval);
431			fcic.function_handler = func;
432			if (Z_ISUNDEF(alfi->obj)) {
433				fci.object = NULL;
434				fcic.object = NULL;
435				if (alfi->ce &&
436				    (!called_scope ||
437				     !instanceof_function(called_scope, alfi->ce))) {
438					fcic.called_scope = alfi->ce;
439				} else {
440					fcic.called_scope = called_scope;
441				}
442			} else {
443				fci.object = Z_OBJ(alfi->obj);
444				fcic.object = Z_OBJ(alfi->obj);
445				fcic.called_scope = Z_OBJCE(alfi->obj);
446			}
447
448			zend_call_function(&fci, &fcic);
449			zval_ptr_dtor(&retval);
450
451			if (EG(exception)) {
452				break;
453			}
454
455			if (pos + 1 == SPL_G(autoload_functions)->nNumUsed ||
456			    zend_hash_exists(EG(class_table), lc_name)) {
457				break;
458			}
459			zend_hash_move_forward_ex(SPL_G(autoload_functions), &pos);
460		}
461		zend_string_release_ex(lc_name, 0);
462		SPL_G(autoload_running) = l_autoload_running;
463	} else {
464		/* do not use or overwrite &EG(autoload_func) here */
465		zend_fcall_info fcall_info;
466		zend_fcall_info_cache fcall_cache;
467
468		ZVAL_UNDEF(&retval);
469
470		fcall_info.size = sizeof(fcall_info);
471		ZVAL_UNDEF(&fcall_info.function_name);
472		fcall_info.retval = &retval;
473		fcall_info.param_count = 1;
474		fcall_info.params = class_name;
475		fcall_info.object = NULL;
476		fcall_info.no_separation = 1;
477
478		fcall_cache.function_handler = spl_autoload_fn;
479		fcall_cache.called_scope = NULL;
480		fcall_cache.object = NULL;
481
482		zend_call_function(&fcall_info, &fcall_cache);
483		zval_ptr_dtor(&retval);
484	}
485} /* }}} */
486
487#define HT_MOVE_TAIL_TO_HEAD(ht)						        \
488	do {												        \
489		Bucket tmp = (ht)->arData[(ht)->nNumUsed-1];				\
490		memmove((ht)->arData + 1, (ht)->arData,					\
491			sizeof(Bucket) * ((ht)->nNumUsed - 1));				\
492		(ht)->arData[0] = tmp;									\
493		zend_hash_rehash(ht);						        	\
494	} while (0)
495
496/* {{{ proto bool spl_autoload_register([mixed autoload_function [, bool throw [, bool prepend]]])
497 Register given function as __autoload() implementation */
498PHP_FUNCTION(spl_autoload_register)
499{
500	zend_string *func_name;
501	char *error = NULL;
502	zend_string *lc_name;
503	zval *zcallable = NULL;
504	zend_bool do_throw = 1;
505	zend_bool prepend  = 0;
506	zend_function *spl_func_ptr;
507	autoload_func_info alfi;
508	zend_object *obj_ptr;
509	zend_fcall_info_cache fcc;
510
511	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "|zbb", &zcallable, &do_throw, &prepend) == FAILURE) {
512		return;
513	}
514
515	if (ZEND_NUM_ARGS()) {
516		if (!zend_is_callable_ex(zcallable, NULL, IS_CALLABLE_STRICT, &func_name, &fcc, &error)) {
517			alfi.ce = fcc.calling_scope;
518			alfi.func_ptr = fcc.function_handler;
519			obj_ptr = fcc.object;
520			if (Z_TYPE_P(zcallable) == IS_ARRAY) {
521				if (!obj_ptr && alfi.func_ptr && !(alfi.func_ptr->common.fn_flags & ZEND_ACC_STATIC)) {
522					if (do_throw) {
523						zend_throw_exception_ex(spl_ce_LogicException, 0, "Passed array specifies a non static method but no object (%s)", error);
524					}
525					if (error) {
526						efree(error);
527					}
528					zend_string_release_ex(func_name, 0);
529					RETURN_FALSE;
530				} else if (do_throw) {
531					zend_throw_exception_ex(spl_ce_LogicException, 0, "Passed array does not specify %s %smethod (%s)", alfi.func_ptr ? "a callable" : "an existing", !obj_ptr ? "static " : "", error);
532				}
533				if (error) {
534					efree(error);
535				}
536				zend_string_release_ex(func_name, 0);
537				RETURN_FALSE;
538			} else if (Z_TYPE_P(zcallable) == IS_STRING) {
539				if (do_throw) {
540					zend_throw_exception_ex(spl_ce_LogicException, 0, "Function '%s' not %s (%s)", ZSTR_VAL(func_name), alfi.func_ptr ? "callable" : "found", error);
541				}
542				if (error) {
543					efree(error);
544				}
545				zend_string_release_ex(func_name, 0);
546				RETURN_FALSE;
547			} else {
548				if (do_throw) {
549					zend_throw_exception_ex(spl_ce_LogicException, 0, "Illegal value passed (%s)", error);
550				}
551				if (error) {
552					efree(error);
553				}
554				zend_string_release_ex(func_name, 0);
555				RETURN_FALSE;
556			}
557		} else if (fcc.function_handler->type == ZEND_INTERNAL_FUNCTION &&
558				   fcc.function_handler->internal_function.handler == zif_spl_autoload_call) {
559			if (do_throw) {
560				zend_throw_exception_ex(spl_ce_LogicException, 0, "Function spl_autoload_call() cannot be registered");
561			}
562			if (error) {
563				efree(error);
564			}
565			zend_string_release_ex(func_name, 0);
566			RETURN_FALSE;
567		}
568		alfi.ce = fcc.calling_scope;
569		alfi.func_ptr = fcc.function_handler;
570		obj_ptr = fcc.object;
571		if (error) {
572			efree(error);
573		}
574
575		if (Z_TYPE_P(zcallable) == IS_OBJECT) {
576			ZVAL_COPY(&alfi.closure, zcallable);
577
578			lc_name = zend_string_alloc(ZSTR_LEN(func_name) + sizeof(uint32_t), 0);
579			zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(func_name), ZSTR_LEN(func_name));
580			memcpy(ZSTR_VAL(lc_name) + ZSTR_LEN(func_name), &Z_OBJ_HANDLE_P(zcallable), sizeof(uint32_t));
581			ZSTR_VAL(lc_name)[ZSTR_LEN(lc_name)] = '\0';
582		} else {
583			ZVAL_UNDEF(&alfi.closure);
584			/* Skip leading \ */
585			if (ZSTR_VAL(func_name)[0] == '\\') {
586				lc_name = zend_string_alloc(ZSTR_LEN(func_name) - 1, 0);
587				zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(func_name) + 1, ZSTR_LEN(func_name) - 1);
588			} else {
589				lc_name = zend_string_tolower(func_name);
590			}
591		}
592		zend_string_release_ex(func_name, 0);
593
594		if (SPL_G(autoload_functions) && zend_hash_exists(SPL_G(autoload_functions), lc_name)) {
595			if (!Z_ISUNDEF(alfi.closure)) {
596				Z_DELREF_P(&alfi.closure);
597			}
598			goto skip;
599		}
600
601		if (obj_ptr && !(alfi.func_ptr->common.fn_flags & ZEND_ACC_STATIC)) {
602			/* add object id to the hash to ensure uniqueness, for more reference look at bug #40091 */
603			lc_name = zend_string_extend(lc_name, ZSTR_LEN(lc_name) + sizeof(uint32_t), 0);
604			memcpy(ZSTR_VAL(lc_name) + ZSTR_LEN(lc_name) - sizeof(uint32_t), &obj_ptr->handle, sizeof(uint32_t));
605			ZSTR_VAL(lc_name)[ZSTR_LEN(lc_name)] = '\0';
606			ZVAL_OBJ(&alfi.obj, obj_ptr);
607			Z_ADDREF(alfi.obj);
608		} else {
609			ZVAL_UNDEF(&alfi.obj);
610		}
611
612		if (!SPL_G(autoload_functions)) {
613			ALLOC_HASHTABLE(SPL_G(autoload_functions));
614			zend_hash_init(SPL_G(autoload_functions), 1, NULL, autoload_func_info_dtor, 0);
615		}
616
617		spl_func_ptr = spl_autoload_fn;
618
619		if (EG(autoload_func) == spl_func_ptr) { /* registered already, so we insert that first */
620			autoload_func_info spl_alfi;
621
622			spl_alfi.func_ptr = spl_func_ptr;
623			ZVAL_UNDEF(&spl_alfi.obj);
624			ZVAL_UNDEF(&spl_alfi.closure);
625			spl_alfi.ce = NULL;
626			zend_hash_add_mem(SPL_G(autoload_functions), spl_autoload_fn->common.function_name,
627					&spl_alfi, sizeof(autoload_func_info));
628			if (prepend && SPL_G(autoload_functions)->nNumOfElements > 1) {
629				/* Move the newly created element to the head of the hashtable */
630				HT_MOVE_TAIL_TO_HEAD(SPL_G(autoload_functions));
631			}
632		}
633
634		if (UNEXPECTED(alfi.func_ptr == &EG(trampoline))) {
635			zend_function *copy = emalloc(sizeof(zend_op_array));
636
637			memcpy(copy, alfi.func_ptr, sizeof(zend_op_array));
638			alfi.func_ptr->common.function_name = NULL;
639			alfi.func_ptr = copy;
640		}
641		if (zend_hash_add_mem(SPL_G(autoload_functions), lc_name, &alfi, sizeof(autoload_func_info)) == NULL) {
642			if (obj_ptr && !(alfi.func_ptr->common.fn_flags & ZEND_ACC_STATIC)) {
643				Z_DELREF(alfi.obj);
644			}
645			if (!Z_ISUNDEF(alfi.closure)) {
646				Z_DELREF(alfi.closure);
647			}
648			if (UNEXPECTED(alfi.func_ptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
649				zend_string_release_ex(alfi.func_ptr->common.function_name, 0);
650				zend_free_trampoline(alfi.func_ptr);
651			}
652		}
653		if (prepend && SPL_G(autoload_functions)->nNumOfElements > 1) {
654			/* Move the newly created element to the head of the hashtable */
655			HT_MOVE_TAIL_TO_HEAD(SPL_G(autoload_functions));
656		}
657skip:
658		zend_string_release_ex(lc_name, 0);
659	}
660
661	if (SPL_G(autoload_functions)) {
662		EG(autoload_func) = spl_autoload_call_fn;
663	} else {
664		EG(autoload_func) = spl_autoload_fn;
665	}
666
667	RETURN_TRUE;
668} /* }}} */
669
670/* {{{ proto bool spl_autoload_unregister(mixed autoload_function)
671 Unregister given function as __autoload() implementation */
672PHP_FUNCTION(spl_autoload_unregister)
673{
674	zend_string *func_name = NULL;
675	char *error = NULL;
676	zend_string *lc_name;
677	zval *zcallable;
678	int success = FAILURE;
679	zend_function *spl_func_ptr;
680	zend_object *obj_ptr;
681	zend_fcall_info_cache fcc;
682
683	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zcallable) == FAILURE) {
684		return;
685	}
686
687	if (!zend_is_callable_ex(zcallable, NULL, IS_CALLABLE_CHECK_SYNTAX_ONLY, &func_name, &fcc, &error)) {
688		zend_throw_exception_ex(spl_ce_LogicException, 0, "Unable to unregister invalid function (%s)", error);
689		if (error) {
690			efree(error);
691		}
692		if (func_name) {
693			zend_string_release_ex(func_name, 0);
694		}
695		RETURN_FALSE;
696	}
697	obj_ptr = fcc.object;
698	if (error) {
699		efree(error);
700	}
701
702	if (Z_TYPE_P(zcallable) == IS_OBJECT) {
703		lc_name = zend_string_alloc(ZSTR_LEN(func_name) + sizeof(uint32_t), 0);
704		zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(func_name), ZSTR_LEN(func_name));
705		memcpy(ZSTR_VAL(lc_name) + ZSTR_LEN(func_name), &Z_OBJ_HANDLE_P(zcallable), sizeof(uint32_t));
706		ZSTR_VAL(lc_name)[ZSTR_LEN(lc_name)] = '\0';
707	} else {
708		/* Skip leading \ */
709		if (ZSTR_VAL(func_name)[0] == '\\') {
710			lc_name = zend_string_alloc(ZSTR_LEN(func_name) - 1, 0);
711			zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(func_name) + 1, ZSTR_LEN(func_name) - 1);
712		} else {
713			lc_name = zend_string_tolower(func_name);
714		}
715	}
716	zend_string_release_ex(func_name, 0);
717
718	if (SPL_G(autoload_functions)) {
719		if (zend_string_equals(lc_name, spl_autoload_call_fn->common.function_name)) {
720			/* remove all */
721			if (!SPL_G(autoload_running)) {
722				zend_hash_destroy(SPL_G(autoload_functions));
723				FREE_HASHTABLE(SPL_G(autoload_functions));
724				SPL_G(autoload_functions) = NULL;
725				EG(autoload_func) = NULL;
726			} else {
727				zend_hash_clean(SPL_G(autoload_functions));
728			}
729			success = SUCCESS;
730		} else {
731			/* remove specific */
732			success = zend_hash_del(SPL_G(autoload_functions), lc_name);
733			if (success != SUCCESS && obj_ptr) {
734				lc_name = zend_string_extend(lc_name, ZSTR_LEN(lc_name) + sizeof(uint32_t), 0);
735				memcpy(ZSTR_VAL(lc_name) + ZSTR_LEN(lc_name) - sizeof(uint32_t), &obj_ptr->handle, sizeof(uint32_t));
736				ZSTR_VAL(lc_name)[ZSTR_LEN(lc_name)] = '\0';
737				success = zend_hash_del(SPL_G(autoload_functions), lc_name);
738			}
739		}
740	} else if (zend_string_equals(lc_name, spl_autoload_fn->common.function_name)) {
741		/* register single spl_autoload() */
742		spl_func_ptr = spl_autoload_fn;
743
744		if (EG(autoload_func) == spl_func_ptr) {
745			success = SUCCESS;
746			EG(autoload_func) = NULL;
747		}
748	}
749
750	zend_string_release_ex(lc_name, 0);
751	RETURN_BOOL(success == SUCCESS);
752} /* }}} */
753
754/* {{{ proto false|array spl_autoload_functions()
755 Return all registered __autoload() functionns */
756PHP_FUNCTION(spl_autoload_functions)
757{
758	zend_function *fptr;
759	autoload_func_info *alfi;
760
761	if (zend_parse_parameters_none() == FAILURE) {
762		return;
763	}
764
765	if (!EG(autoload_func)) {
766		if ((fptr = zend_hash_find_ptr(EG(function_table), ZSTR_KNOWN(ZEND_STR_MAGIC_AUTOLOAD)))) {
767			zval tmp;
768
769			array_init(return_value);
770			ZVAL_STR_COPY(&tmp, ZSTR_KNOWN(ZEND_STR_MAGIC_AUTOLOAD));
771			zend_hash_next_index_insert_new(Z_ARR_P(return_value), &tmp);
772			return;
773		}
774		RETURN_FALSE;
775	}
776
777	fptr = spl_autoload_call_fn;
778
779	if (EG(autoload_func) == fptr) {
780		zend_string *key;
781		array_init(return_value);
782		ZEND_HASH_FOREACH_STR_KEY_PTR(SPL_G(autoload_functions), key, alfi) {
783			if (!Z_ISUNDEF(alfi->closure)) {
784				Z_ADDREF(alfi->closure);
785				add_next_index_zval(return_value, &alfi->closure);
786			} else if (alfi->func_ptr->common.scope) {
787				zval tmp;
788
789				array_init(&tmp);
790				if (!Z_ISUNDEF(alfi->obj)) {
791					Z_ADDREF(alfi->obj);
792					add_next_index_zval(&tmp, &alfi->obj);
793				} else {
794					add_next_index_str(&tmp, zend_string_copy(alfi->ce->name));
795				}
796				add_next_index_str(&tmp, zend_string_copy(alfi->func_ptr->common.function_name));
797				add_next_index_zval(return_value, &tmp);
798			} else {
799				if (strncmp(ZSTR_VAL(alfi->func_ptr->common.function_name), "__lambda_func", sizeof("__lambda_func") - 1)) {
800					add_next_index_str(return_value, zend_string_copy(alfi->func_ptr->common.function_name));
801				} else {
802					add_next_index_str(return_value, zend_string_copy(key));
803				}
804			}
805		} ZEND_HASH_FOREACH_END();
806		return;
807	}
808
809	array_init(return_value);
810	add_next_index_str(return_value, zend_string_copy(EG(autoload_func)->common.function_name));
811} /* }}} */
812
813/* {{{ proto string spl_object_hash(object obj)
814 Return hash id for given object */
815PHP_FUNCTION(spl_object_hash)
816{
817	zval *obj;
818
819	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
820		return;
821	}
822
823	RETURN_NEW_STR(php_spl_object_hash(obj));
824}
825/* }}} */
826
827/* {{{ proto int spl_object_id(object obj)
828 Returns the integer object handle for the given object */
829PHP_FUNCTION(spl_object_id)
830{
831	zval *obj;
832
833	ZEND_PARSE_PARAMETERS_START(1, 1)
834		Z_PARAM_OBJECT(obj)
835	ZEND_PARSE_PARAMETERS_END();
836
837	RETURN_LONG((zend_long)Z_OBJ_HANDLE_P(obj));
838}
839/* }}} */
840
841PHPAPI zend_string *php_spl_object_hash(zval *obj) /* {{{*/
842{
843	intptr_t hash_handle, hash_handlers;
844
845	if (!SPL_G(hash_mask_init)) {
846		SPL_G(hash_mask_handle)   = (intptr_t)(php_mt_rand() >> 1);
847		SPL_G(hash_mask_handlers) = (intptr_t)(php_mt_rand() >> 1);
848		SPL_G(hash_mask_init) = 1;
849	}
850
851	hash_handle   = SPL_G(hash_mask_handle)^(intptr_t)Z_OBJ_HANDLE_P(obj);
852	hash_handlers = SPL_G(hash_mask_handlers);
853
854	return strpprintf(32, "%016zx%016zx", hash_handle, hash_handlers);
855}
856/* }}} */
857
858int spl_build_class_list_string(zval *entry, char **list) /* {{{ */
859{
860	char *res;
861
862	spprintf(&res, 0, "%s, %s", *list, Z_STRVAL_P(entry));
863	efree(*list);
864	*list = res;
865	return ZEND_HASH_APPLY_KEEP;
866} /* }}} */
867
868/* {{{ PHP_MINFO(spl)
869 */
870PHP_MINFO_FUNCTION(spl)
871{
872	zval list;
873	char *strg;
874
875	php_info_print_table_start();
876	php_info_print_table_header(2, "SPL support",        "enabled");
877
878	array_init(&list);
879	SPL_LIST_CLASSES(&list, 0, 1, ZEND_ACC_INTERFACE)
880	strg = estrdup("");
881	zend_hash_apply_with_argument(Z_ARRVAL_P(&list), (apply_func_arg_t)spl_build_class_list_string, &strg);
882	zend_array_destroy(Z_ARR(list));
883	php_info_print_table_row(2, "Interfaces", strg + 2);
884	efree(strg);
885
886	array_init(&list);
887	SPL_LIST_CLASSES(&list, 0, -1, ZEND_ACC_INTERFACE)
888	strg = estrdup("");
889	zend_hash_apply_with_argument(Z_ARRVAL_P(&list), (apply_func_arg_t)spl_build_class_list_string, &strg);
890	zend_array_destroy(Z_ARR(list));
891	php_info_print_table_row(2, "Classes", strg + 2);
892	efree(strg);
893
894	php_info_print_table_end();
895}
896/* }}} */
897
898/* {{{ arginfo */
899ZEND_BEGIN_ARG_INFO_EX(arginfo_iterator_to_array, 0, 0, 1)
900	ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0)
901	ZEND_ARG_INFO(0, use_keys)
902ZEND_END_ARG_INFO();
903
904ZEND_BEGIN_ARG_INFO(arginfo_iterator, 0)
905	ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0)
906ZEND_END_ARG_INFO();
907
908ZEND_BEGIN_ARG_INFO_EX(arginfo_iterator_apply, 0, 0, 2)
909	ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0)
910	ZEND_ARG_INFO(0, function)
911	ZEND_ARG_ARRAY_INFO(0, args, 1)
912ZEND_END_ARG_INFO();
913
914ZEND_BEGIN_ARG_INFO_EX(arginfo_class_parents, 0, 0, 1)
915	ZEND_ARG_INFO(0, instance)
916	ZEND_ARG_INFO(0, autoload)
917ZEND_END_ARG_INFO()
918
919ZEND_BEGIN_ARG_INFO_EX(arginfo_class_implements, 0, 0, 1)
920	ZEND_ARG_INFO(0, what)
921	ZEND_ARG_INFO(0, autoload)
922ZEND_END_ARG_INFO()
923
924ZEND_BEGIN_ARG_INFO_EX(arginfo_class_uses, 0, 0, 1)
925	ZEND_ARG_INFO(0, what)
926	ZEND_ARG_INFO(0, autoload)
927ZEND_END_ARG_INFO()
928
929
930ZEND_BEGIN_ARG_INFO(arginfo_spl_classes, 0)
931ZEND_END_ARG_INFO()
932
933ZEND_BEGIN_ARG_INFO(arginfo_spl_autoload_functions, 0)
934ZEND_END_ARG_INFO()
935
936ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload, 0, 0, 1)
937	ZEND_ARG_INFO(0, class_name)
938	ZEND_ARG_INFO(0, file_extensions)
939ZEND_END_ARG_INFO()
940
941ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_extensions, 0, 0, 0)
942	ZEND_ARG_INFO(0, file_extensions)
943ZEND_END_ARG_INFO()
944
945ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_call, 0, 0, 1)
946	ZEND_ARG_INFO(0, class_name)
947ZEND_END_ARG_INFO()
948
949ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_register, 0, 0, 0)
950	ZEND_ARG_INFO(0, autoload_function)
951	ZEND_ARG_INFO(0, throw)
952	ZEND_ARG_INFO(0, prepend)
953ZEND_END_ARG_INFO()
954
955ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_unregister, 0, 0, 1)
956	ZEND_ARG_INFO(0, autoload_function)
957ZEND_END_ARG_INFO()
958
959ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_object_hash, 0, 0, 1)
960	ZEND_ARG_INFO(0, obj)
961ZEND_END_ARG_INFO()
962
963ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_object_id, 0, 0, 1)
964	ZEND_ARG_INFO(0, obj)
965ZEND_END_ARG_INFO()
966/* }}} */
967
968/* {{{ spl_functions
969 */
970static const zend_function_entry spl_functions[] = {
971	PHP_FE(spl_classes,             arginfo_spl_classes)
972	PHP_FE(spl_autoload,            arginfo_spl_autoload)
973	PHP_FE(spl_autoload_extensions, arginfo_spl_autoload_extensions)
974	PHP_FE(spl_autoload_register,   arginfo_spl_autoload_register)
975	PHP_FE(spl_autoload_unregister, arginfo_spl_autoload_unregister)
976	PHP_FE(spl_autoload_functions,  arginfo_spl_autoload_functions)
977	PHP_FE(spl_autoload_call,       arginfo_spl_autoload_call)
978	PHP_FE(class_parents,           arginfo_class_parents)
979	PHP_FE(class_implements,        arginfo_class_implements)
980	PHP_FE(class_uses,              arginfo_class_uses)
981	PHP_FE(spl_object_hash,         arginfo_spl_object_hash)
982	PHP_FE(spl_object_id,           arginfo_spl_object_id)
983#ifdef SPL_ITERATORS_H
984	PHP_FE(iterator_to_array,       arginfo_iterator_to_array)
985	PHP_FE(iterator_count,          arginfo_iterator)
986	PHP_FE(iterator_apply,          arginfo_iterator_apply)
987#endif /* SPL_ITERATORS_H */
988	PHP_FE_END
989};
990/* }}} */
991
992/* {{{ PHP_MINIT_FUNCTION(spl)
993 */
994PHP_MINIT_FUNCTION(spl)
995{
996	PHP_MINIT(spl_exceptions)(INIT_FUNC_ARGS_PASSTHRU);
997	PHP_MINIT(spl_iterators)(INIT_FUNC_ARGS_PASSTHRU);
998	PHP_MINIT(spl_array)(INIT_FUNC_ARGS_PASSTHRU);
999	PHP_MINIT(spl_directory)(INIT_FUNC_ARGS_PASSTHRU);
1000	PHP_MINIT(spl_dllist)(INIT_FUNC_ARGS_PASSTHRU);
1001	PHP_MINIT(spl_heap)(INIT_FUNC_ARGS_PASSTHRU);
1002	PHP_MINIT(spl_fixedarray)(INIT_FUNC_ARGS_PASSTHRU);
1003	PHP_MINIT(spl_observer)(INIT_FUNC_ARGS_PASSTHRU);
1004
1005	spl_autoload_fn = zend_hash_str_find_ptr(CG(function_table), "spl_autoload", sizeof("spl_autoload") - 1);
1006	spl_autoload_call_fn = zend_hash_str_find_ptr(CG(function_table), "spl_autoload_call", sizeof("spl_autoload_call") - 1);
1007	ZEND_ASSERT(spl_autoload_fn != NULL && spl_autoload_call_fn != NULL);
1008
1009	return SUCCESS;
1010}
1011/* }}} */
1012
1013PHP_RINIT_FUNCTION(spl) /* {{{ */
1014{
1015	SPL_G(autoload_extensions) = NULL;
1016	SPL_G(autoload_functions) = NULL;
1017	SPL_G(hash_mask_init) = 0;
1018	return SUCCESS;
1019} /* }}} */
1020
1021PHP_RSHUTDOWN_FUNCTION(spl) /* {{{ */
1022{
1023	if (SPL_G(autoload_extensions)) {
1024		zend_string_release_ex(SPL_G(autoload_extensions), 0);
1025		SPL_G(autoload_extensions) = NULL;
1026	}
1027	if (SPL_G(autoload_functions)) {
1028		zend_hash_destroy(SPL_G(autoload_functions));
1029		FREE_HASHTABLE(SPL_G(autoload_functions));
1030		SPL_G(autoload_functions) = NULL;
1031	}
1032	if (SPL_G(hash_mask_init)) {
1033		SPL_G(hash_mask_init) = 0;
1034	}
1035	return SUCCESS;
1036} /* }}} */
1037
1038/* {{{ spl_module_entry
1039 */
1040zend_module_entry spl_module_entry = {
1041	STANDARD_MODULE_HEADER,
1042	"SPL",
1043	spl_functions,
1044	PHP_MINIT(spl),
1045	NULL,
1046	PHP_RINIT(spl),
1047	PHP_RSHUTDOWN(spl),
1048	PHP_MINFO(spl),
1049	PHP_SPL_VERSION,
1050	PHP_MODULE_GLOBALS(spl),
1051	PHP_GINIT(spl),
1052	NULL,
1053	NULL,
1054	STANDARD_MODULE_PROPERTIES_EX
1055};
1056/* }}} */
1057
1058/*
1059 * Local variables:
1060 * tab-width: 4
1061 * c-basic-offset: 4
1062 * End:
1063 * vim600: fdm=marker
1064 * vim: noet sw=4 ts=4
1065 */
1066