1 /*
2   +----------------------------------------------------------------------+
3   | Yet Another Framework                                                |
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   | http://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: Xinchen Hui  <laruence@php.net>                              |
14   +----------------------------------------------------------------------+
15 */
16 
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20 
21 #include "php.h"
22 #include "Zend/zend_interfaces.h" /* for zend_class_serialize_deny */
23 #include "ext/standard/php_string.h" /* php_trim */
24 
25 #include "php_yaf.h"
26 #include "yaf_application.h"
27 #include "yaf_namespace.h"
28 #include "yaf_request.h"
29 #include "yaf_loader.h"
30 #include "yaf_exception.h"
31 
32 #define YAF_LOADER_CONTROLLER		"Controller"
33 #define YAF_LOADER_MODEL			"Model"
34 #define YAF_LOADER_PLUGIN			"Plugin"
35 #define YAF_LOADER_RESERVERD		"Yaf_"
36 
37 #define YAF_CLASS_NAME_NORMAL       0
38 #define YAF_CLASS_NAME_MODEL        1
39 #define YAF_CLASS_NAME_PLUGIN       2
40 #define YAF_CLASS_NAME_CONTROLLER   3
41 
42 zend_class_entry *yaf_loader_ce;
43 static zend_object_handlers yaf_loader_obj_handlers;
44 
45 /** {{{ ARG_INFO
46  */
47 ZEND_BEGIN_ARG_INFO_EX(yaf_loader_void_arginfo, 0, 0, 0)
ZEND_END_ARG_INFO()48 ZEND_END_ARG_INFO()
49 
50 ZEND_BEGIN_ARG_INFO_EX(yaf_loader_getinstance_arginfo, 0, 0, 0)
51     ZEND_ARG_INFO(0, local_library_path)
52     ZEND_ARG_INFO(0, global_library_path)
53 ZEND_END_ARG_INFO()
54 
55 ZEND_BEGIN_ARG_INFO_EX(yaf_loader_autoloader_arginfo, 0, 0, 1)
56     ZEND_ARG_INFO(0, class_name)
57 ZEND_END_ARG_INFO()
58 
59 ZEND_BEGIN_ARG_INFO_EX(yaf_loader_regnamespace_arginfo, 0, 0, 1)
60     ZEND_ARG_INFO(0, namespace)
61     ZEND_ARG_INFO(0, path)
62 ZEND_END_ARG_INFO()
63 
64 ZEND_BEGIN_ARG_INFO_EX(yaf_loader_islocalname_arginfo, 0, 0, 1)
65     ZEND_ARG_INFO(0, class_name)
66 ZEND_END_ARG_INFO()
67 
68 ZEND_BEGIN_ARG_INFO_EX(yaf_loader_import_arginfo, 0, 0, 1)
69     ZEND_ARG_INFO(0, file)
70 ZEND_END_ARG_INFO()
71 
72 ZEND_BEGIN_ARG_INFO_EX(yaf_loader_setlib_arginfo, 0, 0, 1)
73     ZEND_ARG_INFO(0, library_path)
74     ZEND_ARG_INFO(0, is_global)
75 ZEND_END_ARG_INFO()
76 
77 ZEND_BEGIN_ARG_INFO_EX(yaf_loader_getlib_arginfo, 0, 0, 0)
78     ZEND_ARG_INFO(0, is_global)
79 ZEND_END_ARG_INFO()
80 /* }}} */
81 
82 static void yaf_loader_obj_free(zend_object *object) /* {{{ */ {
83 	yaf_loader_object *loader = (yaf_loader_object*)object;
84 
85 	if (loader->library) {
86 		zend_string_release(loader->library);
87 	}
88 	if (loader->glibrary) {
89 		zend_string_release(loader->glibrary);
90 	}
91 	if (GC_DELREF(YAF_LOADER_NAMESPACES(loader)) == 0) {
92 		GC_REMOVE_FROM_BUFFER(YAF_LOADER_NAMESPACES(loader));
93 		zend_array_destroy(YAF_LOADER_NAMESPACES(loader));
94 	}
95 	if (loader->properties) {
96 		if (GC_DELREF(loader->properties) == 0) {
97 			GC_REMOVE_FROM_BUFFER(loader->properties);
98 			zend_array_destroy(loader->properties);
99 		}
100 	}
101 
102 	zend_object_std_dtor(object);
103 }
104 /* }}} */
105 
yaf_loader_register(yaf_loader_t * loader)106 int yaf_loader_register(yaf_loader_t *loader) /* {{{ */ {
107 	zval autoload, function, method, ret;
108 
109 	array_init(&autoload);
110 
111     ZVAL_STRING(&method, YAF_AUTOLOAD_FUNC_NAME);
112 	Z_ADDREF_P(loader);
113 	zend_hash_next_index_insert(Z_ARRVAL(autoload), loader);
114 	zend_hash_next_index_insert(Z_ARRVAL(autoload), &method);
115 
116 	ZVAL_STRING(&function, YAF_SPL_AUTOLOAD_REGISTER_NAME);
117 
118 	do {
119 		zend_fcall_info fci = {
120 			sizeof(fci),
121 #if PHP_VERSION_ID < 70100
122 			EG(function_table),
123 #endif
124 			function,
125 #if PHP_VERSION_ID < 70100
126 			NULL,
127 #endif
128 			&ret,
129 			&autoload,
130 			NULL,
131 			1,
132 #if PHP_VERSION_ID < 80000
133             1
134 #else
135            NULL
136 #endif
137 		};
138 
139 		if (zend_call_function(&fci, NULL) == FAILURE) {
140 			zval_ptr_dtor(&function);
141 			zval_ptr_dtor(&autoload);
142 			php_error_docref(NULL,
143 					E_WARNING,
144 					"Unable to register autoload function %s",
145 					YAF_AUTOLOAD_FUNC_NAME);
146 			return 0;
147 		}
148 		zval_ptr_dtor(&function);
149 		zval_ptr_dtor(&autoload);
150 	} while (0);
151 	return 1;
152 }
153 /* }}} */
154 
yaf_loader_set_global_library_path(yaf_loader_object * loader,zend_string * global_library)155 void yaf_loader_set_global_library_path(yaf_loader_object *loader, zend_string *global_library) /* {{{ */ {
156 	if (EXPECTED(loader->glibrary)) {
157 		zend_string_release(loader->glibrary);
158 	}
159 	loader->glibrary = zend_string_copy(global_library);
160 }
161 /* }}} */
162 
yaf_loader_get_namespaces(yaf_loader_object * loader)163 static zend_array *yaf_loader_get_namespaces(yaf_loader_object *loader) /* {{{ */ {
164 	zval *val, rv;
165 	HashTable *ht;
166 	zend_string *name;
167 
168 	ALLOC_HASHTABLE(ht);
169 	zend_hash_init(ht, zend_hash_num_elements(YAF_LOADER_NAMESPACES(loader)), NULL, ZVAL_PTR_DTOR, 0);
170 
171 	ZEND_HASH_FOREACH_STR_KEY_VAL(YAF_LOADER_NAMESPACES(loader), name, val) {
172 		ZEND_ASSERT(name);
173 		if (Z_TYPE_P(val) == IS_NULL) {
174 			ZVAL_STR_COPY(&rv, name);
175 			zend_hash_next_index_insert(ht, &rv);
176 		} else {
177 			zend_hash_update(ht, name, val);
178 			Z_TRY_ADDREF_P(val);
179 		}
180 	} ZEND_HASH_FOREACH_END();
181 
182 	return ht;
183 }
184 /* }}} */
185 
yaf_loader_get_properties(yaf_object * obj)186 static HashTable *yaf_loader_get_properties(yaf_object *obj) /* {{{ */ {
187 	zval rv;
188 	HashTable *ht;
189 #if PHP_VERSION_ID < 80000
190 	yaf_loader_object *loader = Z_YAFLOADEROBJ_P(obj);
191 #else
192 	yaf_loader_object *loader = (yaf_loader_object*)(obj);
193 #endif
194 
195 	if (!loader->properties) {
196 		ALLOC_HASHTABLE(loader->properties);
197 		zend_hash_init(loader->properties, 4, NULL, ZVAL_PTR_DTOR, 0);
198 		YAF_ALLOW_VIOLATION(loader->properties);;
199 	}
200 
201 	ht = loader->properties;
202 
203 	ZVAL_STR_COPY(&rv, loader->library);
204 	zend_hash_str_update(ht, "library:protected", sizeof("library:protected") - 1, &rv);
205 	if (loader->glibrary) {
206 		ZVAL_STR_COPY(&rv, loader->glibrary);
207 	} else {
208 		ZVAL_NULL(&rv);
209 	}
210 	zend_hash_str_update(ht, "global_library:protected", sizeof("global_library:protected") - 1, &rv);
211 
212 	ZVAL_ARR(&rv, yaf_loader_get_namespaces(loader));
213 	zend_hash_str_update(ht, "namespace:protected", sizeof("namespace:protected") - 1, &rv);
214 
215 	ZVAL_BOOL(&rv, yaf_loader_use_spl_autoload(loader));
216 	zend_hash_str_update(ht, "use_spl_autoload:protected", sizeof("use_spl_autoload:protected") - 1, &rv);
217 
218 	ZVAL_BOOL(&rv, yaf_loader_is_lowcase_path(loader));
219 	zend_hash_str_update(ht, "lowercase_path:protected", sizeof("lowercase_path:protected") - 1, &rv);
220 
221 	ZVAL_BOOL(&rv, yaf_loader_is_name_suffix(loader));
222 	zend_hash_str_update(ht, "is_name_suffix:protected", sizeof("is_name_suffix:protected") - 1, &rv);
223 
224 	ZVAL_BOOL(&rv, yaf_loader_has_name_separator(loader));
225 	zend_hash_str_update(ht, "has_name_seperator:protected", sizeof("has_name_seperator:protected") - 1, &rv);
226 
227 	return ht;
228 }
229 /* }}} */
230 
yaf_loader_reset(yaf_loader_object * loader)231 void yaf_loader_reset(yaf_loader_object *loader) /* {{{ */ {
232 	/* for back-compatibility of change of YAF_G after loader in initialized only */
233 	YAF_LOADER_FLAGS(loader) = (zend_uchar)YAF_FLAGS();
234 }
235 /* }}} */
236 
yaf_loader_instance(zend_string * library_path)237 yaf_loader_t *yaf_loader_instance(zend_string *library_path) /* {{{ */ {
238 	yaf_loader_object *loader;
239 	yaf_loader_t *instance = &YAF_G(loader);
240 
241 	if (EXPECTED(IS_OBJECT == Z_TYPE_P(instance))) {
242 		return instance;
243 	}
244 
245 	loader = emalloc(sizeof(yaf_loader_object));
246 	zend_object_std_init(&loader->std, yaf_loader_ce);
247 	loader->std.handlers = &yaf_loader_obj_handlers;
248 
249 	/* yaf_loader_reset(loader); */
250 	YAF_LOADER_FLAGS(loader) = (zend_uchar)YAF_FLAGS();
251 	if (library_path) {
252 		loader->library = zend_string_copy(library_path);
253 	} else {
254 		loader->library = ZSTR_EMPTY_ALLOC();
255 	}
256 
257 	if (*YAF_G(global_library)) {
258 		loader->glibrary = zend_string_init(YAF_G(global_library), strlen(YAF_G(global_library)), 0);
259 	} else {
260 		loader->glibrary = NULL;
261 	}
262 
263 	ZVAL_OBJ(&YAF_G(loader), &loader->std);
264 	if (UNEXPECTED(!yaf_loader_register(&YAF_G(loader)))) {
265 		php_error_docref(NULL, E_WARNING, "Failed to register autoload function");
266 	}
267 
268 	ALLOC_HASHTABLE(YAF_LOADER_NAMESPACES(loader));
269 	zend_hash_init(YAF_LOADER_NAMESPACES(loader), 8, NULL, ZVAL_PTR_DTOR, 0);
270 	YAF_ALLOW_VIOLATION(YAF_LOADER_NAMESPACES(loader));
271 
272 	loader->properties = NULL;
273 
274 	return &YAF_G(loader);
275 }
276 /* }}} */
277 
yaf_loader_register_namespace(yaf_loader_object * loader,zend_string * class_name,zend_string * path)278 int yaf_loader_register_namespace(yaf_loader_object *loader, zend_string *class_name, zend_string *path) /* {{{ */ {
279 	zval *entry, rv;
280 	HashTable *target;
281 	char *delim;
282 	char *name = ZSTR_VAL(class_name);
283 	uint32_t len = ZSTR_LEN(class_name);
284 
285 	ZVAL_NULL(&rv);
286 	target = YAF_LOADER_NAMESPACES(loader);
287 
288 	if (*name == '\\') {
289 		name++;
290 		len--;
291 	}
292 	if (((delim = memchr(name, '\\', len)) || (delim = memchr(name, '_', len)))) {
293 		do {
294 loop:
295 			if ((entry = zend_hash_str_find(target, name, delim - name)) == NULL) {
296 				entry = zend_hash_str_update(target, name, delim - name, &rv);
297 				array_init(entry);
298 			} else if (UNEXPECTED(Z_TYPE_P(entry) != IS_ARRAY)) {
299 				zval_ptr_dtor(entry);
300 				array_init(entry);
301 			}
302 			len -= delim - name + 1;
303 			name = delim + 1;
304 			target = Z_ARRVAL_P(entry);
305 			if (((delim = memchr(name, '\\', len)) || (delim = memchr(name, '_', len)))) {
306 				goto loop;
307 			} else {
308 				entry = zend_hash_str_update(target, name, len, &rv);
309 			}
310 		} while (0);
311 	} else {
312 		entry = zend_hash_str_update(YAF_LOADER_NAMESPACES(loader), name, len, &rv);
313 	}
314 
315 	if (path) {
316 		ZVAL_STR_COPY(entry, path);
317 	}
318 
319 	return 1;
320 }
321 /* }}} */
322 
yaf_loader_register_namespace_multi(yaf_loader_object * loader,zval * namespaces)323 int yaf_loader_register_namespace_multi(yaf_loader_object *loader, zval *namespaces) /* {{{ */ {
324 	zval *pzval;
325 	HashTable *ht;
326 	zend_string *key;
327 
328 	ht = Z_ARRVAL_P(namespaces);
329 	ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, pzval) {
330 		if (key == NULL) {
331 			if (IS_STRING == Z_TYPE_P(pzval)) {
332 				yaf_loader_register_namespace(loader, Z_STR_P(pzval), NULL);
333 			}
334 		} else {
335 			if (IS_STRING == Z_TYPE_P(pzval)) {
336 				yaf_loader_register_namespace(loader, key, Z_STR_P(pzval));
337 			} else {
338 				yaf_loader_register_namespace(loader, key, NULL);
339 			}
340 		}
341 	} ZEND_HASH_FOREACH_END();
342 
343 	return 1;
344 }
345 /* }}} */
346 
yaf_loader_sanitize_path(char * name,uint32_t len)347 static void yaf_loader_sanitize_path(char *name, uint32_t len) /* {{{ */ {
348 	yaf_replace_chr(name, len, '_', DEFAULT_SLASH);
349 }
350 /* }}} */
351 
yaf_loader_sanitize_name(char * name,uint32_t len,char * buf)352 static void yaf_loader_sanitize_name(char *name, uint32_t len, char *buf) /* {{{ */ {
353 	memcpy(buf, name, len);
354 	/* replace all '\' to '_' */
355 	yaf_replace_chr(buf, len, '\\', '_');
356 }
357 /* }}} */
358 
yaf_loader_resolve_namespace(yaf_loader_object * loader,const char * class_name,uint32_t * name_len)359 static zend_string *yaf_loader_resolve_namespace(yaf_loader_object *loader, const char *class_name, uint32_t *name_len) /* {{{ */ {
360 	zval *name;
361 	const char *delim;
362 	uint32_t len = *name_len;
363 	HashTable *target = YAF_LOADER_NAMESPACES(loader);
364 
365 	if ((delim = memchr(class_name, '_', len))) {
366 		do {
367 			if ((name = zend_hash_str_find(target, class_name, delim - class_name))) {
368 				if (Z_TYPE_P(name) == IS_ARRAY) {
369 					target = Z_ARRVAL_P(name);
370 					len -= delim - class_name + 1;
371 					class_name = delim + 1;
372 				} else if (Z_TYPE_P(name) == IS_STRING) {
373 					*name_len = (len - (delim - class_name + 1));
374 					return Z_STR_P(name);
375 				} else {
376 					return (zend_string*)-1; /* use library path */
377 				}
378 			} else {
379 				return NULL;
380 			}
381 		} while ((delim = memchr(class_name, '_', len)));
382 	} else if ((name = zend_hash_str_find(target, class_name, len))) {
383 		return Z_TYPE_P(name) == IS_STRING? Z_STR_P(name) : (zend_string*)-1;
384 	}
385 	return NULL;
386 }
387 /* }}} */
388 
yaf_loader_identify_category(yaf_loader_object * loader,zend_string * class_name)389 static int yaf_loader_identify_category(yaf_loader_object *loader, zend_string *class_name) /* {{{ */ {
390 	char *name = ZSTR_VAL(class_name);
391 	size_t len = ZSTR_LEN(class_name);
392 	char *suspense_name;
393 	int suspense_len;
394 	int suspense_type = YAF_CLASS_NAME_NORMAL;
395 
396 	if (EXPECTED(yaf_loader_is_name_suffix(loader))) {
397 		switch (name[len - 1]) {
398 			case 'l':
399 				suspense_name = YAF_LOADER_MODEL;
400 				suspense_len = sizeof(YAF_LOADER_MODEL) - 1;
401 				suspense_type = YAF_CLASS_NAME_MODEL;
402 				break;
403 			case 'n':
404 				suspense_name = YAF_LOADER_PLUGIN;
405 				suspense_len = sizeof(YAF_LOADER_PLUGIN) - 1;
406 				suspense_type = YAF_CLASS_NAME_PLUGIN;
407 				break;
408 			case 'r':
409 				suspense_name = YAF_LOADER_CONTROLLER;
410 				suspense_len = sizeof(YAF_LOADER_CONTROLLER) - 1;
411 				suspense_type = YAF_CLASS_NAME_CONTROLLER;
412 				break;
413 			default:
414 				return YAF_CLASS_NAME_NORMAL;
415 		}
416 		if (len <= suspense_len || !yaf_slip_equal(name + len - suspense_len, suspense_name, suspense_len - 1)) {
417 			return YAF_CLASS_NAME_NORMAL;
418 		}
419 		if (UNEXPECTED(yaf_loader_has_name_separator(loader))) {
420 			name += (len - suspense_len);
421 			if (len > YAF_G(name_separator_len) &&
422 				memcmp(name - YAF_G(name_separator_len), YAF_G(name_separator), YAF_G(name_separator_len)) == 0) {
423 				return suspense_type;
424 			}
425 			return YAF_CLASS_NAME_NORMAL;
426 		}
427 		return suspense_type;
428 	} else {
429 		switch (*name) {
430 			case 'M':
431 				suspense_name = YAF_LOADER_MODEL;
432 				suspense_len = sizeof(YAF_LOADER_MODEL) - 1;
433 				suspense_type = YAF_CLASS_NAME_MODEL;
434 				break;
435 			case 'P':
436 				suspense_name = YAF_LOADER_PLUGIN;
437 				suspense_len = sizeof(YAF_LOADER_PLUGIN) - 1;
438 				suspense_type = YAF_CLASS_NAME_PLUGIN;
439 				break;
440 			case 'C':
441 				suspense_name = YAF_LOADER_CONTROLLER;
442 				suspense_len = sizeof(YAF_LOADER_CONTROLLER) - 1;
443 				suspense_type = YAF_CLASS_NAME_CONTROLLER;
444 				break;
445 			default:
446 				return YAF_CLASS_NAME_NORMAL;
447 		}
448 		if (len <= suspense_len || !yaf_slip_equal(name + 1, suspense_name + 1, suspense_len - 1)) {
449 			return YAF_CLASS_NAME_NORMAL;
450 		}
451 		if (UNEXPECTED(yaf_loader_has_name_separator(loader))) {
452 			name += suspense_len;
453 			if (len > YAF_G(name_separator_len) &&
454 				memcmp(name, YAF_G(name_separator), YAF_G(name_separator_len)) == 0) {
455 				return suspense_type;
456 			}
457 			return YAF_CLASS_NAME_NORMAL;
458 		}
459 		return suspense_type;
460 	}
461 }
462 /* }}} */
463 
yaf_loader_import(const char * path,uint32_t len)464 ZEND_HOT int yaf_loader_import(const char *path, uint32_t len) /* {{{ */ {
465 	zend_file_handle file_handle;
466 	zend_op_array *op_array;
467 	zend_stat_t sb;
468 
469 	if (UNEXPECTED(VCWD_STAT(path, &sb) == -1)) {
470 		return 0;
471 	}
472 
473 #if PHP_VERSION_ID < 70400
474 	file_handle.filename = path;
475 	file_handle.type = ZEND_HANDLE_FILENAME;
476 	file_handle.free_filename = 0;
477 	file_handle.opened_path = NULL;
478 	file_handle.handle.fp = NULL;
479 #else
480 	/* setup file-handle */
481 	zend_stream_init_filename(&file_handle, path);
482 #endif
483 
484 	if (EXPECTED((op_array = zend_compile_file(&file_handle, ZEND_INCLUDE)))) {
485 		zval result;
486 		if (EXPECTED(file_handle.handle.stream.handle)) {
487 			if (UNEXPECTED(!file_handle.opened_path)) {
488 				file_handle.opened_path = zend_string_init(path, len, 0);
489 			}
490 			zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path);
491 		}
492 
493         ZVAL_UNDEF(&result);
494 		zend_execute(op_array, &result);
495 		destroy_op_array(op_array);
496 		efree_size(op_array, sizeof(zend_op_array));
497         zval_ptr_dtor(&result);
498 		zend_destroy_file_handle(&file_handle);
499 
500 		return 1;
501 	}
502 
503 	zend_destroy_file_handle(&file_handle);
504 	return 0;
505 }
506 /* }}} */
507 
yaf_loader_load_internal(yaf_loader_object * loader,char * filename,size_t fname_len,char * directory,uint32_t directory_len)508 ZEND_HOT int yaf_loader_load_internal(yaf_loader_object *loader, char *filename, size_t fname_len, char *directory, uint32_t directory_len) /* {{{ */ {
509    char *ext;
510    uint32_t ext_len;
511    yaf_application_object *app = yaf_application_instance();
512 
513    if (UNEXPECTED(app->ext)) {
514 	   ext = ZSTR_VAL(app->ext);
515 	   ext_len = ZSTR_LEN(app->ext);
516    } else {
517 	   ext = YAF_DEFAULT_EXT;
518 	   ext_len = sizeof(YAF_DEFAULT_EXT) - 1;
519    }
520 
521    if (UNEXPECTED((directory_len + fname_len + ext_len + 3) > MAXPATHLEN)) {
522 	   directory[directory_len] = '\0';
523 	   return 0;
524    }
525 
526    directory[directory_len] = DEFAULT_SLASH;
527    memcpy(directory + directory_len + 1, filename, fname_len);
528    filename = directory + directory_len + 1;
529    if (UNEXPECTED(yaf_loader_is_lowcase_path(loader))) {
530 	   zend_str_tolower(filename, fname_len);
531    }
532    yaf_loader_sanitize_path(filename, fname_len);
533    directory[directory_len + 1 + fname_len] = '.';
534    memcpy(directory + directory_len + 1 + fname_len + 1, ext, ext_len);
535    /* aussume all the path is not end in slash */
536    directory[directory_len + 1 + fname_len + 1 + ext_len] = '\0';
537    directory_len = directory_len + 1 + fname_len + 1 + ext_len;
538 
539    return yaf_loader_import(directory, directory_len);
540 }
541 /* }}} */
542 
yaf_loader_load_user(yaf_loader_object * loader,char * buf,uint32_t len)543 ZEND_HOT static int yaf_loader_load_user(yaf_loader_object *loader, char *buf, uint32_t len) /* {{{ */ {
544 	zend_string *library_dir;
545 	const char *ext;
546 	char *name = buf;
547 	uint32_t ext_len;
548 	uint32_t origin_len = len;
549 	yaf_application_object *app = yaf_application_instance();
550 
551 	if ((library_dir = yaf_loader_resolve_namespace(loader, buf, &len))) {
552 		if (library_dir == ((zend_string*)-1)) {
553 			library_dir = loader->library;
554 		} else {
555 			name += (origin_len - len);
556 		}
557 	} else {
558 		if (!loader->glibrary) {
559 			library_dir = loader->library;
560 		} else {
561 			library_dir = loader->glibrary;
562 		}
563 	}
564 
565 	if (UNEXPECTED(yaf_loader_is_lowcase_path(loader))) {
566 		zend_str_tolower(name, len);
567 	}
568 	yaf_loader_sanitize_path(name, len);
569 
570 	if (EXPECTED(app) && UNEXPECTED(app->ext)) {
571 		ext = ZSTR_VAL(app->ext);
572 		ext_len = ZSTR_LEN(app->ext);
573 	} else {
574 		ext = YAF_DEFAULT_EXT;
575 		ext_len = sizeof(YAF_DEFAULT_EXT) - 1;
576 	}
577 
578 	ZEND_ASSERT(library_dir);
579 	if (UNEXPECTED(ZSTR_LEN(library_dir) + len + ext_len + 2 > MAXPATHLEN)) {
580 		return 0;
581 	}
582 
583 	memmove(buf + ZSTR_LEN(library_dir) + 1, name, len);
584 	memcpy(buf, ZSTR_VAL(library_dir), ZSTR_LEN(library_dir));
585 	buf[ZSTR_LEN(library_dir)] = DEFAULT_SLASH;
586 	buf[ZSTR_LEN(library_dir) + 1 + len] = '.';
587 	memcpy(buf + ZSTR_LEN(library_dir) + 1 + len + 1, ext, ext_len);
588 	buf[ZSTR_LEN(library_dir) + 1 + len + 1 + ext_len] = '\0';
589 	len = ZSTR_LEN(library_dir) + 1 + len + 1 + ext_len;
590 
591 	return yaf_loader_import(buf, len);
592 }
593 /* }}} */
594 
yaf_loader_load_mvc(yaf_loader_object * loader,char * buf,uint32_t len,int type)595 static zend_never_inline int yaf_loader_load_mvc(yaf_loader_object *loader, char *buf, uint32_t len, int type) /* {{{ */ {
596 	char *name;
597 	const char *folder, *ext;
598 	uint32_t folder_len, ext_len;
599 	zend_string *library_dir;
600 	yaf_application_object *app = yaf_application_instance();
601 
602 	if (UNEXPECTED(app == NULL)) {
603 		php_error_docref(NULL, E_WARNING, "Couldn't load a MVC class unless an %s is initialized", ZSTR_VAL(yaf_application_ce->name));
604 		*buf = '\0';
605 		return 0;
606 	}
607 
608 	switch (type) {
609 		case YAF_CLASS_NAME_MODEL:
610 			folder = YAF_MODEL_DIRECTORY_NAME;
611 			folder_len = sizeof(YAF_MODEL_DIRECTORY_NAME) - 1;
612 			break;
613 		case YAF_CLASS_NAME_PLUGIN:
614 			folder = YAF_PLUGIN_DIRECTORY_NAME;
615 			folder_len = sizeof(YAF_PLUGIN_DIRECTORY_NAME) - 1;
616 			break;
617 		case YAF_CLASS_NAME_CONTROLLER:
618 			folder = YAF_CONTROLLER_DIRECTORY_NAME;
619 			folder_len = sizeof(YAF_CONTROLLER_DIRECTORY_NAME) - 1;
620 			break;
621 		default:
622 			ZEND_ASSERT(0);
623 			break;
624 	}
625 
626 	len -= (folder_len - 1); /* models -> model etc*/
627 	if (EXPECTED(yaf_loader_is_name_suffix(loader))) {
628 		name = buf;
629 		if (UNEXPECTED(yaf_loader_has_name_separator(loader))) {
630 			len -= YAF_G(name_separator_len);
631 		}
632 	} else {
633 		name = buf + folder_len - 1;
634 		if (UNEXPECTED(yaf_loader_has_name_separator(loader))) {
635 			name += YAF_G(name_separator_len);
636 			len -= YAF_G(name_separator_len);
637 		}
638 	}
639 	if (UNEXPECTED(yaf_loader_is_lowcase_path(loader))) {
640 		zend_str_tolower(name, len);
641 	}
642 	yaf_loader_sanitize_path(name, len);
643 
644 	if (UNEXPECTED(app->ext)) {
645 		ext = ZSTR_VAL(app->ext);
646 		ext_len = ZSTR_LEN(app->ext);
647 	} else {
648 		ext = YAF_DEFAULT_EXT;
649 		ext_len = sizeof(YAF_DEFAULT_EXT) - 1;
650 	}
651 
652 	if (UNEXPECTED(ZSTR_LEN(app->directory) + 1 + folder_len + 1 + len + 1 + ext_len > MAXPATHLEN)) {
653 		php_error_docref(NULL, E_WARNING, "Path too long '%s'", ZSTR_VAL(app->directory));
654 		*buf = '\0';
655 		return 0;
656 	}
657 
658 	library_dir = app->directory;
659 	memmove(buf + ZSTR_LEN(library_dir) + 1 + folder_len + 1, name, len);
660 	memcpy(buf, ZSTR_VAL(library_dir), ZSTR_LEN(library_dir));
661 	buf[ZSTR_LEN(library_dir)] = DEFAULT_SLASH;
662 	memcpy(buf + ZSTR_LEN(library_dir) + 1, folder, folder_len);
663 	buf[ZSTR_LEN(library_dir) + 1 + folder_len] = DEFAULT_SLASH;
664 	buf[ZSTR_LEN(library_dir) + 1 + folder_len + 1 + len] = '.';
665 	memcpy(buf + ZSTR_LEN(library_dir) + 1 + folder_len + 1 + len + 1, ext, ext_len);
666 	buf[ZSTR_LEN(library_dir) + 1 + folder_len + 1 + len + 1 + ext_len] = '\0';
667 
668 	return yaf_loader_import(buf, len);
669 }
670 /* }}} */
671 
672 /** {{{ proto public Yaf_Loader::autoload($class_name)
673 */
PHP_METHOD(yaf_loader,autoload)674 PHP_METHOD(yaf_loader, autoload) {
675 	char directory[MAXPATHLEN];
676 	uint32_t class_type, status;
677 	zend_string *class_name;
678 	zend_string *unqualified = NULL;
679 	yaf_loader_object *loader = Z_YAFLOADEROBJ_P(getThis());
680 
681 	ZEND_PARSE_PARAMETERS_START(1, 1)
682 		Z_PARAM_STR(class_name)
683 	ZEND_PARSE_PARAMETERS_END();
684 
685 	if (UNEXPECTED(ZSTR_LEN(class_name) == 0 || ZSTR_LEN(class_name) > MAXPATHLEN)) {
686 		RETURN_FALSE;
687 	}
688 
689 	/*
690 	if (UNEXPECTED(ZSTR_LEN(class_name) >= sizeof(YAF_LOADER_RESERVERD) - 1 &&
691 		yaf_slip_equal(ZSTR_VAL(class_name), YAF_LOADER_RESERVERD, sizeof(YAF_LOADER_RESERVERD) - 1))) {
692 		php_error_docref(NULL, E_WARNING, "You should not use '%s' as class name prefix", YAF_LOADER_RESERVERD);
693 	}
694 	*/
695 	if (ZSTR_VAL(class_name)[0] == '\\') {
696 		unqualified = zend_string_init(ZSTR_VAL(class_name) + 1, ZSTR_LEN(class_name) - 1, 0);
697 		class_name = unqualified;
698 	}
699 	yaf_loader_sanitize_name(ZSTR_VAL(class_name), ZSTR_LEN(class_name), directory);
700 	if ((class_type = yaf_loader_identify_category(loader, class_name)) == YAF_CLASS_NAME_NORMAL) {
701 		status = yaf_loader_load_user(loader, directory, ZSTR_LEN(class_name));
702 	} else {
703 		status = yaf_loader_load_mvc(loader, directory, ZSTR_LEN(class_name), class_type);
704 	}
705 
706 	if (unqualified) {
707 		zend_string_release(unqualified);
708 	}
709 	if (EXPECTED(!yaf_loader_use_spl_autoload(loader))) {
710 		if (EXPECTED(status)) {
711 			zend_string *lc_name = zend_string_tolower(class_name);
712 			if (UNEXPECTED(!zend_hash_exists(EG(class_table), lc_name))) {
713 				php_error_docref(NULL, E_WARNING, "Could not find class %s in %s", ZSTR_VAL(class_name), directory);
714 			}
715 			zend_string_release(lc_name);
716 		} else if (*directory) {
717 			php_error_docref(NULL, E_WARNING, "Failed opening script %s: %s", directory, strerror(errno));
718 		}
719 		RETURN_TRUE;
720 	}
721 	RETURN_BOOL(status);
722 }
723 /* }}} */
724 
725 /** {{{ proto public static Yaf_Loader::import($file)
726 */
PHP_METHOD(yaf_loader,import)727 PHP_METHOD(yaf_loader, import) {
728 	zend_string *file;
729 	int need_free = 0;
730 
731 	ZEND_PARSE_PARAMETERS_START(1, 1)
732 		Z_PARAM_STR(file)
733 	ZEND_PARSE_PARAMETERS_END();
734 
735 	if (ZSTR_LEN(file) == 0) {
736 		RETURN_FALSE;
737 	} else {
738 		int retval;
739 
740 		if (!IS_ABSOLUTE_PATH(ZSTR_VAL(file), ZSTR_LEN(file))) {
741 			if (UNEXPECTED(Z_TYPE(YAF_G(loader)) != IS_OBJECT)) {
742 				php_error_docref(NULL, E_WARNING, "%s need to be initialize first", ZSTR_VAL(yaf_loader_ce->name));
743 				RETURN_FALSE;
744 			} else {
745 				yaf_loader_object *loader = Z_YAFLOADEROBJ(YAF_G(loader));
746 				zend_string *library = loader->library;
747 				file = strpprintf(0, "%s%c%s", ZSTR_VAL(library), DEFAULT_SLASH, ZSTR_VAL(file));
748 				need_free = 1;
749 			}
750 		}
751 
752 		retval = zend_hash_exists(&EG(included_files), file);
753 		if (retval) {
754 			if (need_free) {
755 				zend_string_release(file);
756 			}
757 			RETURN_TRUE;
758 		}
759 
760 		retval = yaf_loader_import(ZSTR_VAL(file), ZSTR_LEN(file));
761 		if (need_free) {
762 			zend_string_release(file);
763 		}
764 
765 		RETURN_BOOL(retval);
766 	}
767 }
768 /* }}} */
769 
770 /** {{{ proto public Yaf_Loader::registerLocalNamespace(mixed $namespace, string $path = NULL)
771 */
PHP_METHOD(yaf_loader,registerLocalNamespace)772 PHP_METHOD(yaf_loader, registerLocalNamespace) {
773 	zval *namespaces;
774 	zend_string *path = NULL;
775 
776 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|S", &namespaces, &path) == FAILURE) {
777 		return;
778 	}
779 
780 	if (IS_STRING == Z_TYPE_P(namespaces)) {
781 		if (yaf_loader_register_namespace(Z_YAFLOADEROBJ_P(getThis()), Z_STR_P(namespaces), path)) {
782 			RETURN_ZVAL(getThis(), 1, 0);
783 		}
784 	} else if (IS_ARRAY == Z_TYPE_P(namespaces)) {
785 		if (yaf_loader_register_namespace_multi(Z_YAFLOADEROBJ_P(getThis()), namespaces)) {
786 			RETURN_ZVAL(getThis(), 1, 0);
787 		}
788 	} else {
789 		php_error_docref(NULL, E_WARNING, "Invalid parameters provided, must be a string, or an array");
790 	}
791 
792 	RETURN_FALSE;
793 }
794 /* }}} */
795 
796 /** {{{ proto public Yaf_Loader::getLocalNamespace(void)
797 */
PHP_METHOD(yaf_loader,getLocalNamespace)798 PHP_METHOD(yaf_loader, getLocalNamespace) {
799 	yaf_loader_object *loader = Z_YAFLOADEROBJ_P(getThis());
800 
801 	if (zend_parse_parameters_none() == FAILURE) {
802 		return;
803 	}
804 
805 	ZVAL_ARR(return_value, yaf_loader_get_namespaces(loader));
806 }
807 /* }}} */
808 
809 /** {{{ proto public Yaf_Loader::clearLocalNamespace(void)
810 */
PHP_METHOD(yaf_loader,clearLocalNamespace)811 PHP_METHOD(yaf_loader, clearLocalNamespace) {
812 	yaf_loader_object *loader = Z_YAFLOADEROBJ_P(getThis());
813 
814 	if (zend_parse_parameters_none() == FAILURE) {
815 		return;
816 	}
817 
818 	zend_hash_clean(YAF_LOADER_NAMESPACES(loader));
819 
820 	RETURN_TRUE;
821 }
822 /* }}} */
823 
824 /** {{{ proto public Yaf_Loader::isLocalName(string $class_name)
825 */
PHP_METHOD(yaf_loader,isLocalName)826 PHP_METHOD(yaf_loader, isLocalName) {
827 	zend_string *name;
828 	int result;
829 	char *sanitized_name;
830 	uint32_t sanitized_len;
831 	yaf_loader_object *loader = Z_YAFLOADEROBJ_P(getThis());
832 	ALLOCA_FLAG(use_heap);
833 
834 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name) == FAILURE) {
835 		return;
836 	}
837 
838 	if (ZSTR_VAL(name)[0] == '\\') {
839 		sanitized_len = ZSTR_LEN(name) - 1;
840 		sanitized_name = do_alloca(sanitized_len, use_heap);
841 		yaf_loader_sanitize_name(ZSTR_VAL(name) + 1, sanitized_len, sanitized_name);
842 	} else {
843 		sanitized_len = ZSTR_LEN(name);
844 		sanitized_name = do_alloca(sanitized_len, use_heap);
845 		yaf_loader_sanitize_name(ZSTR_VAL(name), sanitized_len, sanitized_name);
846 	}
847 	result = YAF_LOADER_NAMESPACES(loader) && yaf_loader_resolve_namespace(loader, sanitized_name, &sanitized_len);
848 	free_alloca(sanitized_name, use_heap);
849 
850 	RETURN_BOOL(result);
851 }
852 /* }}} */
853 
854 /** {{{ proto public Yaf_Loader::getNamespacePath(string $class_name)
855 */
PHP_METHOD(yaf_loader,getNamespacePath)856 PHP_METHOD(yaf_loader, getNamespacePath) {
857 	zend_string *name;
858 	zend_string *path;
859 	char *sanitized_name;
860 	uint32_t sanitized_len;
861 	yaf_loader_object *loader = Z_YAFLOADEROBJ_P(getThis());
862 	ALLOCA_FLAG(use_heap);
863 
864 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name) == FAILURE) {
865 		return;
866 	}
867 
868 	if (ZSTR_VAL(name)[0] == '\\') {
869 		sanitized_len = ZSTR_LEN(name) - 1;
870 		sanitized_name = do_alloca(sanitized_len, use_heap);
871 		yaf_loader_sanitize_name(ZSTR_VAL(name) + 1, sanitized_len, sanitized_name);
872 	} else {
873 		sanitized_len = ZSTR_LEN(name);
874 		sanitized_name = do_alloca(sanitized_len, use_heap);
875 		yaf_loader_sanitize_name(ZSTR_VAL(name), sanitized_len, sanitized_name);
876 	}
877 	if ((path = yaf_loader_resolve_namespace(loader, sanitized_name, &sanitized_len))) {
878 		if (path == ((zend_string*)-1)) {
879 			RETVAL_STR_COPY(loader->library);
880 		} else {
881 			RETVAL_STR_COPY(path);
882 		}
883 	} else {
884 		if (loader->glibrary) {
885 			RETVAL_STR_COPY(loader->glibrary);
886 		} else {
887 			RETVAL_STR_COPY(loader->library);
888 		}
889 	}
890 	free_alloca(sanitized_name, use_heap);
891 
892 	return;
893 }
894 /* }}} */
895 
896 /** {{{ proto public Yaf_Loader::setLibraryPath(string $path, $global = FALSE)
897 */
PHP_METHOD(yaf_loader,setLibraryPath)898 PHP_METHOD(yaf_loader, setLibraryPath) {
899 	zend_string *library;
900 	zend_bool global = 0;
901 	yaf_loader_object *loader = Z_YAFLOADEROBJ_P(getThis());
902 
903 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|b", &library, &global) == FAILURE) {
904 		return;
905 	}
906 
907 	if (!global) {
908 		yaf_loader_set_library_path(loader, library);
909 	} else {
910 		yaf_loader_set_global_library_path(loader, library);
911 	}
912 
913 	RETURN_ZVAL(getThis(), 1, 0);
914 }
915 /* }}} */
916 
917 /** {{{ proto public Yaf_Loader::getLibraryPath($global = FALSE)
918 */
PHP_METHOD(yaf_loader,getLibraryPath)919 PHP_METHOD(yaf_loader, getLibraryPath) {
920 	zend_bool global = 0;
921 	yaf_loader_object *loader = Z_YAFLOADEROBJ_P(getThis());
922 
923 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &global) == FAILURE) {
924 		return;
925 	}
926 
927 	if (!global) {
928 		RETURN_STR_COPY(loader->library);
929 	} else if (loader->glibrary) {
930 		RETURN_STR_COPY(loader->glibrary);
931 	} else {
932 		RETURN_EMPTY_STRING();
933 	}
934 }
935 /* }}} */
936 
937 /** {{{ proto public Yaf_Loader::getInstance($library = NULL, $global_library = NULL)
938 */
PHP_METHOD(yaf_loader,getInstance)939 PHP_METHOD(yaf_loader, getInstance) {
940 	zend_string *library = NULL;
941 	zend_string *global = NULL;
942 	yaf_loader_t *loader;
943 
944 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S!S!", &library, &global) == FAILURE) {
945 		return;
946 	}
947 
948 	if ((loader = yaf_loader_instance(NULL))) {
949 		if (library) {
950 			yaf_loader_set_library_path(Z_YAFLOADEROBJ_P(loader), library);
951 		}
952 		if (global) {
953 			yaf_loader_set_global_library_path(Z_YAFLOADEROBJ_P(loader), global);
954 		}
955 		/* for back-compatible with changing of YAF_G(lowcase_path) ini_set */
956 		yaf_loader_reset(Z_YAFLOADEROBJ_P(loader));
957 		RETURN_ZVAL(loader, 1, 0);
958 	}
959 
960 	RETURN_FALSE;
961 }
962 /* }}} */
963 
964 /** {{{ proto private Yaf_Loader::__construct(void)
965 */
PHP_METHOD(yaf_loader,__construct)966 PHP_METHOD(yaf_loader, __construct) {
967 }
968 /* }}} */
969 
970 /** {{{ yaf_loader_methods
971 */
972 zend_function_entry yaf_loader_methods[] = {
973 	PHP_ME(yaf_loader, __construct, yaf_loader_void_arginfo, ZEND_ACC_PRIVATE|ZEND_ACC_CTOR)
974 	PHP_ME(yaf_loader, autoload, yaf_loader_autoloader_arginfo, ZEND_ACC_PUBLIC)
975 	PHP_ME(yaf_loader, getInstance, yaf_loader_getinstance_arginfo, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
976 	PHP_ME(yaf_loader, registerLocalNamespace, yaf_loader_regnamespace_arginfo, ZEND_ACC_PUBLIC)
977 	PHP_ME(yaf_loader, getLocalNamespace, yaf_loader_void_arginfo, ZEND_ACC_PUBLIC)
978 	PHP_ME(yaf_loader, clearLocalNamespace, yaf_loader_void_arginfo, ZEND_ACC_PUBLIC)
979 	PHP_ME(yaf_loader, isLocalName, yaf_loader_islocalname_arginfo, ZEND_ACC_PUBLIC)
980 	PHP_ME(yaf_loader, getNamespacePath, yaf_loader_islocalname_arginfo, ZEND_ACC_PUBLIC)
981 	PHP_ME(yaf_loader, import, yaf_loader_import_arginfo, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
982 	PHP_ME(yaf_loader, setLibraryPath, yaf_loader_setlib_arginfo, ZEND_ACC_PUBLIC)
983 	PHP_ME(yaf_loader, getLibraryPath, yaf_loader_getlib_arginfo, ZEND_ACC_PUBLIC)
984 	PHP_MALIAS(yaf_loader, registerNamespace, registerLocalNamespace, yaf_loader_regnamespace_arginfo, ZEND_ACC_PUBLIC)
985 	PHP_MALIAS(yaf_loader, getNamespaces, getLocalNamespace, yaf_loader_void_arginfo, ZEND_ACC_PUBLIC)
986 	{NULL, NULL, NULL}
987 };
988 /* }}} */
989 
990 /** {{{ YAF_STARTUP_FUNCTION
991 */
YAF_STARTUP_FUNCTION(loader)992 YAF_STARTUP_FUNCTION(loader) {
993 	zend_class_entry ce;
994 
995 	YAF_INIT_CLASS_ENTRY(ce, "Yaf_Loader",  "Yaf\\Loader", yaf_loader_methods);
996 	yaf_loader_ce = zend_register_internal_class_ex(&ce, NULL);
997 	yaf_loader_ce->ce_flags |= ZEND_ACC_FINAL;
998 	yaf_loader_ce->serialize = zend_class_serialize_deny;
999 	yaf_loader_ce->unserialize = zend_class_unserialize_deny;
1000 
1001 	memcpy(&yaf_loader_obj_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1002 	yaf_loader_obj_handlers.clone_obj = NULL;
1003 	yaf_loader_obj_handlers.get_gc = yaf_fake_get_gc;
1004 	yaf_loader_obj_handlers.free_obj = yaf_loader_obj_free;
1005 	yaf_loader_obj_handlers.get_properties = yaf_loader_get_properties;
1006 
1007 	return SUCCESS;
1008 }
1009 /* }}} */
1010 
1011 /*
1012  * Local variables:
1013  * tab-width: 4
1014  * c-basic-offset: 4
1015  * End:
1016  * vim600: noet sw=4 ts=4 fdm=marker
1017  * vim<600: noet sw=4 ts=4
1018  */
1019