1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 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/* $Id$ */ 20 21#ifdef HAVE_CONFIG_H 22#include "config.h" 23#endif 24 25#include "php.h" 26#include "php_ini.h" 27#include "php_main.h" 28#include "ext/standard/info.h" 29#include "php_spl.h" 30#include "spl_functions.h" 31#include "spl_engine.h" 32#include "spl_array.h" 33#include "spl_directory.h" 34#include "spl_iterators.h" 35#include "spl_exceptions.h" 36#include "spl_observer.h" 37#include "spl_dllist.h" 38#include "spl_fixedarray.h" 39#include "spl_heap.h" 40#include "zend_exceptions.h" 41#include "zend_interfaces.h" 42#include "ext/standard/php_rand.h" 43#include "ext/standard/php_lcg.h" 44#include "main/snprintf.h" 45 46#ifdef COMPILE_DL_SPL 47ZEND_GET_MODULE(spl) 48#endif 49 50ZEND_DECLARE_MODULE_GLOBALS(spl) 51 52#define SPL_DEFAULT_FILE_EXTENSIONS ".inc,.php" 53 54/* {{{ PHP_GINIT_FUNCTION 55 */ 56static PHP_GINIT_FUNCTION(spl) 57{ 58 spl_globals->autoload_extensions = NULL; 59 spl_globals->autoload_extensions_len = 0; 60 spl_globals->autoload_functions = NULL; 61 spl_globals->autoload_running = 0; 62} 63/* }}} */ 64 65static zend_class_entry * spl_find_ce_by_name(char *name, int len, zend_bool autoload TSRMLS_DC) 66{ 67 zend_class_entry **ce; 68 int found; 69 70 if (!autoload) { 71 char *lc_name; 72 ALLOCA_FLAG(use_heap) 73 74 lc_name = do_alloca(len + 1, use_heap); 75 zend_str_tolower_copy(lc_name, name, len); 76 77 found = zend_hash_find(EG(class_table), lc_name, len +1, (void **) &ce); 78 free_alloca(lc_name, use_heap); 79 } else { 80 found = zend_lookup_class(name, len, &ce TSRMLS_CC); 81 } 82 if (found != SUCCESS) { 83 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Class %s does not exist%s", name, autoload ? " and could not be loaded" : ""); 84 return NULL; 85 } 86 87 return *ce; 88} 89 90/* {{{ proto array class_parents(object instance [, boolean autoload = true]) 91 Return an array containing the names of all parent classes */ 92PHP_FUNCTION(class_parents) 93{ 94 zval *obj; 95 zend_class_entry *parent_class, *ce; 96 zend_bool autoload = 1; 97 98 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|b", &obj, &autoload) == FAILURE) { 99 RETURN_FALSE; 100 } 101 102 if (Z_TYPE_P(obj) != IS_OBJECT && Z_TYPE_P(obj) != IS_STRING) { 103 php_error_docref(NULL TSRMLS_CC, E_WARNING, "object or string expected"); 104 RETURN_FALSE; 105 } 106 107 if (Z_TYPE_P(obj) == IS_STRING) { 108 if (NULL == (ce = spl_find_ce_by_name(Z_STRVAL_P(obj), Z_STRLEN_P(obj), autoload TSRMLS_CC))) { 109 RETURN_FALSE; 110 } 111 } else { 112 ce = Z_OBJCE_P(obj); 113 } 114 115 array_init(return_value); 116 parent_class = ce->parent; 117 while (parent_class) { 118 spl_add_class_name(return_value, parent_class, 0, 0 TSRMLS_CC); 119 parent_class = parent_class->parent; 120 } 121} 122/* }}} */ 123 124/* {{{ proto array class_implements(mixed what [, bool autoload ]) 125 Return all classes and interfaces implemented by SPL */ 126PHP_FUNCTION(class_implements) 127{ 128 zval *obj; 129 zend_bool autoload = 1; 130 zend_class_entry *ce; 131 132 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|b", &obj, &autoload) == FAILURE) { 133 RETURN_FALSE; 134 } 135 if (Z_TYPE_P(obj) != IS_OBJECT && Z_TYPE_P(obj) != IS_STRING) { 136 php_error_docref(NULL TSRMLS_CC, E_WARNING, "object or string expected"); 137 RETURN_FALSE; 138 } 139 140 if (Z_TYPE_P(obj) == IS_STRING) { 141 if (NULL == (ce = spl_find_ce_by_name(Z_STRVAL_P(obj), Z_STRLEN_P(obj), autoload TSRMLS_CC))) { 142 RETURN_FALSE; 143 } 144 } else { 145 ce = Z_OBJCE_P(obj); 146 } 147 148 array_init(return_value); 149 spl_add_interfaces(return_value, ce, 1, ZEND_ACC_INTERFACE TSRMLS_CC); 150} 151/* }}} */ 152 153#define SPL_ADD_CLASS(class_name, z_list, sub, allow, ce_flags) \ 154 spl_add_classes(spl_ce_ ## class_name, z_list, sub, allow, ce_flags TSRMLS_CC) 155 156#define SPL_LIST_CLASSES(z_list, sub, allow, ce_flags) \ 157 SPL_ADD_CLASS(AppendIterator, z_list, sub, allow, ce_flags); \ 158 SPL_ADD_CLASS(ArrayIterator, z_list, sub, allow, ce_flags); \ 159 SPL_ADD_CLASS(ArrayObject, z_list, sub, allow, ce_flags); \ 160 SPL_ADD_CLASS(BadFunctionCallException, z_list, sub, allow, ce_flags); \ 161 SPL_ADD_CLASS(BadMethodCallException, z_list, sub, allow, ce_flags); \ 162 SPL_ADD_CLASS(CachingIterator, z_list, sub, allow, ce_flags); \ 163 SPL_ADD_CLASS(Countable, z_list, sub, allow, ce_flags); \ 164 SPL_ADD_CLASS(DirectoryIterator, z_list, sub, allow, ce_flags); \ 165 SPL_ADD_CLASS(DomainException, z_list, sub, allow, ce_flags); \ 166 SPL_ADD_CLASS(EmptyIterator, z_list, sub, allow, ce_flags); \ 167 SPL_ADD_CLASS(FilesystemIterator, z_list, sub, allow, ce_flags); \ 168 SPL_ADD_CLASS(FilterIterator, z_list, sub, allow, ce_flags); \ 169 SPL_ADD_CLASS(GlobIterator, z_list, sub, allow, ce_flags); \ 170 SPL_ADD_CLASS(InfiniteIterator, z_list, sub, allow, ce_flags); \ 171 SPL_ADD_CLASS(InvalidArgumentException, z_list, sub, allow, ce_flags); \ 172 SPL_ADD_CLASS(IteratorIterator, z_list, sub, allow, ce_flags); \ 173 SPL_ADD_CLASS(LengthException, z_list, sub, allow, ce_flags); \ 174 SPL_ADD_CLASS(LimitIterator, z_list, sub, allow, ce_flags); \ 175 SPL_ADD_CLASS(LogicException, z_list, sub, allow, ce_flags); \ 176 SPL_ADD_CLASS(MultipleIterator, z_list, sub, allow, ce_flags); \ 177 SPL_ADD_CLASS(NoRewindIterator, z_list, sub, allow, ce_flags); \ 178 SPL_ADD_CLASS(OuterIterator, z_list, sub, allow, ce_flags); \ 179 SPL_ADD_CLASS(OutOfBoundsException, z_list, sub, allow, ce_flags); \ 180 SPL_ADD_CLASS(OutOfRangeException, z_list, sub, allow, ce_flags); \ 181 SPL_ADD_CLASS(OverflowException, z_list, sub, allow, ce_flags); \ 182 SPL_ADD_CLASS(ParentIterator, z_list, sub, allow, ce_flags); \ 183 SPL_ADD_CLASS(RangeException, z_list, sub, allow, ce_flags); \ 184 SPL_ADD_CLASS(RecursiveArrayIterator, z_list, sub, allow, ce_flags); \ 185 SPL_ADD_CLASS(RecursiveCachingIterator, z_list, sub, allow, ce_flags); \ 186 SPL_ADD_CLASS(RecursiveDirectoryIterator, z_list, sub, allow, ce_flags); \ 187 SPL_ADD_CLASS(RecursiveFilterIterator, z_list, sub, allow, ce_flags); \ 188 SPL_ADD_CLASS(RecursiveIterator, z_list, sub, allow, ce_flags); \ 189 SPL_ADD_CLASS(RecursiveIteratorIterator, z_list, sub, allow, ce_flags); \ 190 SPL_ADD_CLASS(RecursiveRegexIterator, z_list, sub, allow, ce_flags); \ 191 SPL_ADD_CLASS(RecursiveTreeIterator, z_list, sub, allow, ce_flags); \ 192 SPL_ADD_CLASS(RegexIterator, z_list, sub, allow, ce_flags); \ 193 SPL_ADD_CLASS(RuntimeException, z_list, sub, allow, ce_flags); \ 194 SPL_ADD_CLASS(SeekableIterator, z_list, sub, allow, ce_flags); \ 195 SPL_ADD_CLASS(SplDoublyLinkedList, z_list, sub, allow, ce_flags); \ 196 SPL_ADD_CLASS(SplFileInfo, z_list, sub, allow, ce_flags); \ 197 SPL_ADD_CLASS(SplFileObject, z_list, sub, allow, ce_flags); \ 198 SPL_ADD_CLASS(SplFixedArray, z_list, sub, allow, ce_flags); \ 199 SPL_ADD_CLASS(SplHeap, z_list, sub, allow, ce_flags); \ 200 SPL_ADD_CLASS(SplMinHeap, z_list, sub, allow, ce_flags); \ 201 SPL_ADD_CLASS(SplMaxHeap, z_list, sub, allow, ce_flags); \ 202 SPL_ADD_CLASS(SplObjectStorage, z_list, sub, allow, ce_flags); \ 203 SPL_ADD_CLASS(SplObserver, z_list, sub, allow, ce_flags); \ 204 SPL_ADD_CLASS(SplPriorityQueue, z_list, sub, allow, ce_flags); \ 205 SPL_ADD_CLASS(SplQueue, z_list, sub, allow, ce_flags); \ 206 SPL_ADD_CLASS(SplStack, z_list, sub, allow, ce_flags); \ 207 SPL_ADD_CLASS(SplSubject, z_list, sub, allow, ce_flags); \ 208 SPL_ADD_CLASS(SplTempFileObject, z_list, sub, allow, ce_flags); \ 209 SPL_ADD_CLASS(UnderflowException, z_list, sub, allow, ce_flags); \ 210 SPL_ADD_CLASS(UnexpectedValueException, z_list, sub, allow, ce_flags); \ 211 212/* {{{ proto array spl_classes() 213 Return an array containing the names of all clsses and interfaces defined in SPL */ 214PHP_FUNCTION(spl_classes) 215{ 216 array_init(return_value); 217 218 SPL_LIST_CLASSES(return_value, 0, 0, 0) 219} 220/* }}} */ 221 222static int spl_autoload(const char *class_name, const char * lc_name, int class_name_len, const char * file_extension TSRMLS_DC) /* {{{ */ 223{ 224 char *class_file; 225 int class_file_len; 226 int dummy = 1; 227 zend_file_handle file_handle; 228 zend_op_array *new_op_array; 229 zval *result = NULL; 230 int ret; 231 232 class_file_len = spprintf(&class_file, 0, "%s%s", lc_name, file_extension); 233 234#if DEFAULT_SLASH != '\\' 235 { 236 char *ptr = class_file; 237 char *end = ptr + class_file_len; 238 239 while ((ptr = memchr(ptr, '\\', (end - ptr))) != NULL) { 240 *ptr = DEFAULT_SLASH; 241 } 242 } 243#endif 244 245 ret = php_stream_open_for_zend_ex(class_file, &file_handle, ENFORCE_SAFE_MODE|USE_PATH|STREAM_OPEN_FOR_INCLUDE TSRMLS_CC); 246 247 if (ret == SUCCESS) { 248 if (!file_handle.opened_path) { 249 file_handle.opened_path = estrndup(class_file, class_file_len); 250 } 251 if (zend_hash_add(&EG(included_files), file_handle.opened_path, strlen(file_handle.opened_path)+1, (void *)&dummy, sizeof(int), NULL)==SUCCESS) { 252 new_op_array = zend_compile_file(&file_handle, ZEND_REQUIRE TSRMLS_CC); 253 zend_destroy_file_handle(&file_handle TSRMLS_CC); 254 } else { 255 new_op_array = NULL; 256 zend_file_handle_dtor(&file_handle TSRMLS_CC); 257 } 258 if (new_op_array) { 259 EG(return_value_ptr_ptr) = &result; 260 EG(active_op_array) = new_op_array; 261 if (!EG(active_symbol_table)) { 262 zend_rebuild_symbol_table(TSRMLS_C); 263 } 264 265 zend_execute(new_op_array TSRMLS_CC); 266 267 destroy_op_array(new_op_array TSRMLS_CC); 268 efree(new_op_array); 269 if (!EG(exception)) { 270 if (EG(return_value_ptr_ptr)) { 271 zval_ptr_dtor(EG(return_value_ptr_ptr)); 272 } 273 } 274 275 efree(class_file); 276 return zend_hash_exists(EG(class_table), (char*)lc_name, class_name_len+1); 277 } 278 } 279 efree(class_file); 280 return 0; 281} /* }}} */ 282 283/* {{{ proto void spl_autoload(string class_name [, string file_extensions]) 284 Default implementation for __autoload() */ 285PHP_FUNCTION(spl_autoload) 286{ 287 char *class_name, *lc_name, *file_exts = SPL_G(autoload_extensions); 288 int class_name_len, file_exts_len = SPL_G(autoload_extensions_len), found = 0; 289 char *copy, *pos1, *pos2; 290 zval **original_return_value = EG(return_value_ptr_ptr); 291 zend_op **original_opline_ptr = EG(opline_ptr); 292 zend_op_array *original_active_op_array = EG(active_op_array); 293 294 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &class_name, &class_name_len, &file_exts, &file_exts_len) == FAILURE) { 295 RETURN_FALSE; 296 } 297 298 if (file_exts == NULL) { /* autoload_extensions is not intialzed, set to defaults */ 299 copy = pos1 = estrndup(SPL_DEFAULT_FILE_EXTENSIONS, sizeof(SPL_DEFAULT_FILE_EXTENSIONS)-1); 300 } else { 301 copy = pos1 = estrndup(file_exts, file_exts_len); 302 } 303 lc_name = zend_str_tolower_dup(class_name, class_name_len); 304 while(pos1 && *pos1 && !EG(exception)) { 305 EG(return_value_ptr_ptr) = original_return_value; 306 EG(opline_ptr) = original_opline_ptr; 307 EG(active_op_array) = original_active_op_array; 308 pos2 = strchr(pos1, ','); 309 if (pos2) *pos2 = '\0'; 310 if (spl_autoload(class_name, lc_name, class_name_len, pos1 TSRMLS_CC)) { 311 found = 1; 312 break; /* loaded */ 313 } 314 pos1 = pos2 ? pos2 + 1 : NULL; 315 } 316 efree(lc_name); 317 if (copy) { 318 efree(copy); 319 } 320 321 EG(return_value_ptr_ptr) = original_return_value; 322 EG(opline_ptr) = original_opline_ptr; 323 EG(active_op_array) = original_active_op_array; 324 325 if (!found && !SPL_G(autoload_running)) { 326 /* For internal errors, we generate E_ERROR, for direct calls an exception is thrown. 327 * The "scope" is determined by an opcode, if it is ZEND_FETCH_CLASS we know function was called indirectly by 328 * the Zend engine. 329 */ 330 if (active_opline->opcode != ZEND_FETCH_CLASS) { 331 zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Class %s could not be loaded", class_name); 332 } else { 333 php_error_docref(NULL TSRMLS_CC, E_ERROR, "Class %s could not be loaded", class_name); 334 } 335 } 336} /* }}} */ 337 338/* {{{ proto string spl_autoload_extensions([string file_extensions]) 339 Register and return default file extensions for spl_autoload */ 340PHP_FUNCTION(spl_autoload_extensions) 341{ 342 char *file_exts = NULL; 343 int file_exts_len; 344 345 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &file_exts, &file_exts_len) == FAILURE) { 346 return; 347 } 348 if (file_exts) { 349 if (SPL_G(autoload_extensions)) { 350 efree(SPL_G(autoload_extensions)); 351 } 352 SPL_G(autoload_extensions) = estrndup(file_exts, file_exts_len); 353 SPL_G(autoload_extensions_len) = file_exts_len; 354 } 355 356 if (SPL_G(autoload_extensions) == NULL) { 357 RETURN_STRINGL(SPL_DEFAULT_FILE_EXTENSIONS, sizeof(SPL_DEFAULT_FILE_EXTENSIONS) - 1, 1); 358 } else { 359 RETURN_STRINGL(SPL_G(autoload_extensions), SPL_G(autoload_extensions_len), 1); 360 } 361} /* }}} */ 362 363typedef struct { 364 zend_function *func_ptr; 365 zval *obj; 366 zval *closure; 367 zend_class_entry *ce; 368} autoload_func_info; 369 370static void autoload_func_info_dtor(autoload_func_info *alfi) 371{ 372 if (alfi->obj) { 373 zval_ptr_dtor(&alfi->obj); 374 } 375 if (alfi->closure) { 376 zval_ptr_dtor(&alfi->closure); 377 } 378} 379 380/* {{{ proto void spl_autoload_call(string class_name) 381 Try all registerd autoload function to load the requested class */ 382PHP_FUNCTION(spl_autoload_call) 383{ 384 zval *class_name, *retval = NULL; 385 int class_name_len; 386 char *func_name, *lc_name; 387 uint func_name_len; 388 ulong dummy; 389 HashPosition function_pos; 390 autoload_func_info *alfi; 391 392 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &class_name) == FAILURE || Z_TYPE_P(class_name) != IS_STRING) { 393 return; 394 } 395 396 if (SPL_G(autoload_functions)) { 397 int l_autoload_running = SPL_G(autoload_running); 398 SPL_G(autoload_running) = 1; 399 class_name_len = Z_STRLEN_P(class_name); 400 lc_name = zend_str_tolower_dup(Z_STRVAL_P(class_name), class_name_len); 401 zend_hash_internal_pointer_reset_ex(SPL_G(autoload_functions), &function_pos); 402 while(zend_hash_has_more_elements_ex(SPL_G(autoload_functions), &function_pos) == SUCCESS) { 403 zend_hash_get_current_key_ex(SPL_G(autoload_functions), &func_name, &func_name_len, &dummy, 0, &function_pos); 404 zend_hash_get_current_data_ex(SPL_G(autoload_functions), (void **) &alfi, &function_pos); 405 zend_call_method(alfi->obj ? &alfi->obj : NULL, alfi->ce, &alfi->func_ptr, func_name, func_name_len, &retval, 1, class_name, NULL TSRMLS_CC); 406 zend_exception_save(TSRMLS_C); 407 if (retval) { 408 zval_ptr_dtor(&retval); 409 retval = NULL; 410 } 411 if (zend_hash_exists(EG(class_table), lc_name, class_name_len + 1)) { 412 break; 413 } 414 zend_hash_move_forward_ex(SPL_G(autoload_functions), &function_pos); 415 } 416 zend_exception_restore(TSRMLS_C); 417 efree(lc_name); 418 SPL_G(autoload_running) = l_autoload_running; 419 } else { 420 /* do not use or overwrite &EG(autoload_func) here */ 421 zend_call_method_with_1_params(NULL, NULL, NULL, "spl_autoload", NULL, class_name); 422 } 423} /* }}} */ 424 425#define HT_MOVE_TAIL_TO_HEAD(ht) \ 426 (ht)->pListTail->pListNext = (ht)->pListHead; \ 427 (ht)->pListHead = (ht)->pListTail; \ 428 (ht)->pListTail = (ht)->pListHead->pListLast; \ 429 (ht)->pListHead->pListNext->pListLast = (ht)->pListHead;\ 430 (ht)->pListTail->pListNext = NULL; \ 431 (ht)->pListHead->pListLast = NULL; 432 433/* {{{ proto bool spl_autoload_register([mixed autoload_function = "spl_autoload" [, throw = true [, prepend]]]) 434 Register given function as __autoload() implementation */ 435PHP_FUNCTION(spl_autoload_register) 436{ 437 char *func_name, *error = NULL; 438 int func_name_len; 439 char *lc_name = NULL; 440 zval *zcallable = NULL; 441 zend_bool do_throw = 1; 442 zend_bool prepend = 0; 443 zend_function *spl_func_ptr; 444 autoload_func_info alfi; 445 zval *obj_ptr; 446 zend_fcall_info_cache fcc; 447 448 if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "|zbb", &zcallable, &do_throw, &prepend) == FAILURE) { 449 return; 450 } 451 452 if (ZEND_NUM_ARGS()) { 453 if (Z_TYPE_P(zcallable) == IS_STRING) { 454 if (Z_STRLEN_P(zcallable) == sizeof("spl_autoload_call") - 1) { 455 if (!zend_binary_strcasecmp(Z_STRVAL_P(zcallable), sizeof("spl_autoload_call"), "spl_autoload_call", sizeof("spl_autoload_call"))) { 456 if (do_throw) { 457 zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Function spl_autoload_call() cannot be registered"); 458 } 459 RETURN_FALSE; 460 } 461 } 462 } 463 464 if (!zend_is_callable_ex(zcallable, NULL, IS_CALLABLE_STRICT, &func_name, &func_name_len, &fcc, &error TSRMLS_CC)) { 465 alfi.ce = fcc.calling_scope; 466 alfi.func_ptr = fcc.function_handler; 467 obj_ptr = fcc.object_ptr; 468 if (Z_TYPE_P(zcallable) == IS_ARRAY) { 469 if (!obj_ptr && alfi.func_ptr && !(alfi.func_ptr->common.fn_flags & ZEND_ACC_STATIC)) { 470 if (do_throw) { 471 zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Passed array specifies a non static method but no object (%s)", error); 472 } 473 if (error) { 474 efree(error); 475 } 476 efree(func_name); 477 RETURN_FALSE; 478 } 479 else if (do_throw) { 480 zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Passed array does not specify %s %smethod (%s)", alfi.func_ptr ? "a callable" : "an existing", !obj_ptr ? "static " : "", error); 481 } 482 if (error) { 483 efree(error); 484 } 485 efree(func_name); 486 RETURN_FALSE; 487 } else if (Z_TYPE_P(zcallable) == IS_STRING) { 488 if (do_throw) { 489 zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Function '%s' not %s (%s)", func_name, alfi.func_ptr ? "callable" : "found", error); 490 } 491 if (error) { 492 efree(error); 493 } 494 efree(func_name); 495 RETURN_FALSE; 496 } else { 497 if (do_throw) { 498 zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Illegal value passed (%s)", error); 499 } 500 if (error) { 501 efree(error); 502 } 503 efree(func_name); 504 RETURN_FALSE; 505 } 506 } 507 alfi.closure = NULL; 508 alfi.ce = fcc.calling_scope; 509 alfi.func_ptr = fcc.function_handler; 510 obj_ptr = fcc.object_ptr; 511 if (error) { 512 efree(error); 513 } 514 515 lc_name = safe_emalloc(func_name_len, 1, sizeof(long) + 1); 516 zend_str_tolower_copy(lc_name, func_name, func_name_len); 517 efree(func_name); 518 519 if (Z_TYPE_P(zcallable) == IS_OBJECT) { 520 alfi.closure = zcallable; 521 Z_ADDREF_P(zcallable); 522 523 lc_name = erealloc(lc_name, func_name_len + 2 + sizeof(zend_object_handle)); 524 memcpy(lc_name + func_name_len, &Z_OBJ_HANDLE_P(zcallable), 525 sizeof(zend_object_handle)); 526 func_name_len += sizeof(zend_object_handle); 527 lc_name[func_name_len] = '\0'; 528 } 529 530 if (SPL_G(autoload_functions) && zend_hash_exists(SPL_G(autoload_functions), (char*)lc_name, func_name_len+1)) { 531 if (alfi.closure) { 532 Z_DELREF_P(zcallable); 533 } 534 goto skip; 535 } 536 537 if (obj_ptr && !(alfi.func_ptr->common.fn_flags & ZEND_ACC_STATIC)) { 538 /* add object id to the hash to ensure uniqueness, for more reference look at bug #40091 */ 539 lc_name = erealloc(lc_name, func_name_len + 2 + sizeof(zend_object_handle)); 540 memcpy(lc_name + func_name_len, &Z_OBJ_HANDLE_P(obj_ptr), sizeof(zend_object_handle)); 541 func_name_len += sizeof(zend_object_handle); 542 lc_name[func_name_len] = '\0'; 543 alfi.obj = obj_ptr; 544 Z_ADDREF_P(alfi.obj); 545 } else { 546 alfi.obj = NULL; 547 } 548 549 if (!SPL_G(autoload_functions)) { 550 ALLOC_HASHTABLE(SPL_G(autoload_functions)); 551 zend_hash_init(SPL_G(autoload_functions), 1, NULL, (dtor_func_t) autoload_func_info_dtor, 0); 552 } 553 554 zend_hash_find(EG(function_table), "spl_autoload", sizeof("spl_autoload"), (void **) &spl_func_ptr); 555 556 if (EG(autoload_func) == spl_func_ptr) { /* registered already, so we insert that first */ 557 autoload_func_info spl_alfi; 558 559 spl_alfi.func_ptr = spl_func_ptr; 560 spl_alfi.obj = NULL; 561 spl_alfi.ce = NULL; 562 spl_alfi.closure = NULL; 563 zend_hash_add(SPL_G(autoload_functions), "spl_autoload", sizeof("spl_autoload"), &spl_alfi, sizeof(autoload_func_info), NULL); 564 if (prepend && SPL_G(autoload_functions)->nNumOfElements > 1) { 565 /* Move the newly created element to the head of the hashtable */ 566 HT_MOVE_TAIL_TO_HEAD(SPL_G(autoload_functions)); 567 } 568 } 569 570 zend_hash_add(SPL_G(autoload_functions), lc_name, func_name_len+1, &alfi.func_ptr, sizeof(autoload_func_info), NULL); 571 if (prepend && SPL_G(autoload_functions)->nNumOfElements > 1) { 572 /* Move the newly created element to the head of the hashtable */ 573 HT_MOVE_TAIL_TO_HEAD(SPL_G(autoload_functions)); 574 } 575skip: 576 efree(lc_name); 577 } 578 579 if (SPL_G(autoload_functions)) { 580 zend_hash_find(EG(function_table), "spl_autoload_call", sizeof("spl_autoload_call"), (void **) &EG(autoload_func)); 581 } else { 582 zend_hash_find(EG(function_table), "spl_autoload", sizeof("spl_autoload"), (void **) &EG(autoload_func)); 583 } 584 RETURN_TRUE; 585} /* }}} */ 586 587/* {{{ proto bool spl_autoload_unregister(mixed autoload_function) 588 Unregister given function as __autoload() implementation */ 589PHP_FUNCTION(spl_autoload_unregister) 590{ 591 char *func_name, *error = NULL; 592 int func_name_len; 593 char *lc_name = NULL; 594 zval *zcallable; 595 int success = FAILURE; 596 zend_function *spl_func_ptr; 597 zval *obj_ptr; 598 zend_fcall_info_cache fcc; 599 600 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zcallable) == FAILURE) { 601 return; 602 } 603 604 if (!zend_is_callable_ex(zcallable, NULL, IS_CALLABLE_CHECK_SYNTAX_ONLY, &func_name, &func_name_len, &fcc, &error TSRMLS_CC)) { 605 zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Unable to unregister invalid function (%s)", error); 606 if (error) { 607 efree(error); 608 } 609 if (func_name) { 610 efree(func_name); 611 } 612 RETURN_FALSE; 613 } 614 obj_ptr = fcc.object_ptr; 615 if (error) { 616 efree(error); 617 } 618 619 lc_name = safe_emalloc(func_name_len, 1, sizeof(long) + 1); 620 zend_str_tolower_copy(lc_name, func_name, func_name_len); 621 efree(func_name); 622 623 if (Z_TYPE_P(zcallable) == IS_OBJECT) { 624 lc_name = erealloc(lc_name, func_name_len + 2 + sizeof(zend_object_handle)); 625 memcpy(lc_name + func_name_len, &Z_OBJ_HANDLE_P(zcallable), 626 sizeof(zend_object_handle)); 627 func_name_len += sizeof(zend_object_handle); 628 lc_name[func_name_len] = '\0'; 629 } 630 631 if (SPL_G(autoload_functions)) { 632 if (func_name_len == sizeof("spl_autoload_call")-1 && !strcmp(lc_name, "spl_autoload_call")) { 633 /* remove all */ 634 zend_hash_destroy(SPL_G(autoload_functions)); 635 FREE_HASHTABLE(SPL_G(autoload_functions)); 636 SPL_G(autoload_functions) = NULL; 637 EG(autoload_func) = NULL; 638 success = SUCCESS; 639 } else { 640 /* remove specific */ 641 success = zend_hash_del(SPL_G(autoload_functions), lc_name, func_name_len+1); 642 if (success != SUCCESS && obj_ptr) { 643 lc_name = erealloc(lc_name, func_name_len + 2 + sizeof(zend_object_handle)); 644 memcpy(lc_name + func_name_len, &Z_OBJ_HANDLE_P(obj_ptr), sizeof(zend_object_handle)); 645 func_name_len += sizeof(zend_object_handle); 646 lc_name[func_name_len] = '\0'; 647 success = zend_hash_del(SPL_G(autoload_functions), lc_name, func_name_len+1); 648 } 649 } 650 } else if (func_name_len == sizeof("spl_autoload")-1 && !strcmp(lc_name, "spl_autoload")) { 651 /* register single spl_autoload() */ 652 zend_hash_find(EG(function_table), "spl_autoload", sizeof("spl_autoload"), (void **) &spl_func_ptr); 653 654 if (EG(autoload_func) == spl_func_ptr) { 655 success = SUCCESS; 656 EG(autoload_func) = NULL; 657 } 658 } 659 660 efree(lc_name); 661 RETURN_BOOL(success == SUCCESS); 662} /* }}} */ 663 664/* {{{ proto false|array spl_autoload_functions() 665 Return all registered __autoload() functionns */ 666PHP_FUNCTION(spl_autoload_functions) 667{ 668 zend_function *fptr; 669 HashPosition function_pos; 670 autoload_func_info *alfi; 671 672 if (zend_parse_parameters_none() == FAILURE) { 673 return; 674 } 675 676 if (!EG(autoload_func)) { 677 if (zend_hash_find(EG(function_table), ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME), (void **) &fptr) == SUCCESS) { 678 array_init(return_value); 679 add_next_index_stringl(return_value, ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME)-1, 1); 680 return; 681 } 682 RETURN_FALSE; 683 } 684 685 zend_hash_find(EG(function_table), "spl_autoload_call", sizeof("spl_autoload_call"), (void **) &fptr); 686 687 if (EG(autoload_func) == fptr) { 688 array_init(return_value); 689 zend_hash_internal_pointer_reset_ex(SPL_G(autoload_functions), &function_pos); 690 while(zend_hash_has_more_elements_ex(SPL_G(autoload_functions), &function_pos) == SUCCESS) { 691 zend_hash_get_current_data_ex(SPL_G(autoload_functions), (void **) &alfi, &function_pos); 692 if (alfi->closure) { 693 Z_ADDREF_P(alfi->closure); 694 add_next_index_zval(return_value, alfi->closure); 695 } else if (alfi->func_ptr->common.scope) { 696 zval *tmp; 697 MAKE_STD_ZVAL(tmp); 698 array_init(tmp); 699 700 if (alfi->obj) { 701 Z_ADDREF_P(alfi->obj); 702 add_next_index_zval(tmp, alfi->obj); 703 } else { 704 add_next_index_string(tmp, alfi->ce->name, 1); 705 } 706 add_next_index_string(tmp, alfi->func_ptr->common.function_name, 1); 707 add_next_index_zval(return_value, tmp); 708 } else 709 add_next_index_string(return_value, alfi->func_ptr->common.function_name, 1); 710 711 zend_hash_move_forward_ex(SPL_G(autoload_functions), &function_pos); 712 } 713 return; 714 } 715 716 array_init(return_value); 717 add_next_index_string(return_value, EG(autoload_func)->common.function_name, 1); 718} /* }}} */ 719 720/* {{{ proto string spl_object_hash(object obj) 721 Return hash id for given object */ 722PHP_FUNCTION(spl_object_hash) 723{ 724 zval *obj; 725 char* hash; 726 727 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj) == FAILURE) { 728 return; 729 } 730 731 hash = emalloc(33); 732 php_spl_object_hash(obj, hash TSRMLS_CC); 733 734 RETVAL_STRING(hash, 0); 735} 736/* }}} */ 737 738PHPAPI void php_spl_object_hash(zval *obj, char *result TSRMLS_DC) /* {{{*/ 739{ 740 intptr_t hash_handle, hash_handlers; 741 char *hex; 742 743 if (!SPL_G(hash_mask_init)) { 744 if (!BG(mt_rand_is_seeded)) { 745 php_mt_srand(GENERATE_SEED() TSRMLS_CC); 746 } 747 748 SPL_G(hash_mask_handle) = (intptr_t)(php_mt_rand(TSRMLS_C) >> 1); 749 SPL_G(hash_mask_handlers) = (intptr_t)(php_mt_rand(TSRMLS_C) >> 1); 750 SPL_G(hash_mask_init) = 1; 751 } 752 753 hash_handle = SPL_G(hash_mask_handle)^(intptr_t)Z_OBJ_HANDLE_P(obj); 754 hash_handlers = SPL_G(hash_mask_handlers)^(intptr_t)Z_OBJ_HT_P(obj); 755 756 spprintf(&hex, 32, "%016x%016x", hash_handle, hash_handlers); 757 758 strlcpy(result, hex, 33); 759 efree(hex); 760} 761/* }}} */ 762 763int spl_build_class_list_string(zval **entry, char **list TSRMLS_DC) /* {{{ */ 764{ 765 char *res; 766 767 spprintf(&res, 0, "%s, %s", *list, Z_STRVAL_PP(entry)); 768 efree(*list); 769 *list = res; 770 return ZEND_HASH_APPLY_KEEP; 771} /* }}} */ 772 773/* {{{ PHP_MINFO(spl) 774 */ 775PHP_MINFO_FUNCTION(spl) 776{ 777 zval list; 778 char *strg; 779 780 php_info_print_table_start(); 781 php_info_print_table_header(2, "SPL support", "enabled"); 782 783 INIT_PZVAL(&list); 784 array_init(&list); 785 SPL_LIST_CLASSES(&list, 0, 1, ZEND_ACC_INTERFACE) 786 strg = estrdup(""); 787 zend_hash_apply_with_argument(Z_ARRVAL_P(&list), (apply_func_arg_t)spl_build_class_list_string, &strg TSRMLS_CC); 788 zval_dtor(&list); 789 php_info_print_table_row(2, "Interfaces", strg + 2); 790 efree(strg); 791 792 INIT_PZVAL(&list); 793 array_init(&list); 794 SPL_LIST_CLASSES(&list, 0, -1, ZEND_ACC_INTERFACE) 795 strg = estrdup(""); 796 zend_hash_apply_with_argument(Z_ARRVAL_P(&list), (apply_func_arg_t)spl_build_class_list_string, &strg TSRMLS_CC); 797 zval_dtor(&list); 798 php_info_print_table_row(2, "Classes", strg + 2); 799 efree(strg); 800 801 php_info_print_table_end(); 802} 803/* }}} */ 804 805/* {{{ arginfo */ 806ZEND_BEGIN_ARG_INFO_EX(arginfo_iterator_to_array, 0, 0, 1) 807 ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0) 808 ZEND_ARG_INFO(0, use_keys) 809ZEND_END_ARG_INFO(); 810 811ZEND_BEGIN_ARG_INFO(arginfo_iterator, 0) 812 ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0) 813ZEND_END_ARG_INFO(); 814 815ZEND_BEGIN_ARG_INFO_EX(arginfo_iterator_apply, 0, 0, 2) 816 ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0) 817 ZEND_ARG_INFO(0, function) 818 ZEND_ARG_ARRAY_INFO(0, args, 1) 819ZEND_END_ARG_INFO(); 820 821ZEND_BEGIN_ARG_INFO_EX(arginfo_class_parents, 0, 0, 1) 822 ZEND_ARG_INFO(0, instance) 823 ZEND_ARG_INFO(0, autoload) 824ZEND_END_ARG_INFO() 825 826ZEND_BEGIN_ARG_INFO_EX(arginfo_class_implements, 0, 0, 1) 827 ZEND_ARG_INFO(0, what) 828 ZEND_ARG_INFO(0, autoload) 829ZEND_END_ARG_INFO() 830 831ZEND_BEGIN_ARG_INFO(arginfo_spl_classes, 0) 832ZEND_END_ARG_INFO() 833 834ZEND_BEGIN_ARG_INFO(arginfo_spl_autoload_functions, 0) 835ZEND_END_ARG_INFO() 836 837ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload, 0, 0, 1) 838 ZEND_ARG_INFO(0, class_name) 839 ZEND_ARG_INFO(0, file_extensions) 840ZEND_END_ARG_INFO() 841 842ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_extensions, 0, 0, 0) 843 ZEND_ARG_INFO(0, file_extensions) 844ZEND_END_ARG_INFO() 845 846ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_call, 0, 0, 1) 847 ZEND_ARG_INFO(0, class_name) 848ZEND_END_ARG_INFO() 849 850ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_register, 0, 0, 0) 851 ZEND_ARG_INFO(0, autoload_function) 852ZEND_END_ARG_INFO() 853 854ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_unregister, 0, 0, 1) 855 ZEND_ARG_INFO(0, autoload_function) 856ZEND_END_ARG_INFO() 857 858ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_object_hash, 0, 0, 1) 859 ZEND_ARG_INFO(0, obj) 860ZEND_END_ARG_INFO() 861/* }}} */ 862 863/* {{{ spl_functions 864 */ 865const zend_function_entry spl_functions[] = { 866 PHP_FE(spl_classes, arginfo_spl_classes) 867 PHP_FE(spl_autoload, arginfo_spl_autoload) 868 PHP_FE(spl_autoload_extensions, arginfo_spl_autoload_extensions) 869 PHP_FE(spl_autoload_register, arginfo_spl_autoload_register) 870 PHP_FE(spl_autoload_unregister, arginfo_spl_autoload_unregister) 871 PHP_FE(spl_autoload_functions, arginfo_spl_autoload_functions) 872 PHP_FE(spl_autoload_call, arginfo_spl_autoload_call) 873 PHP_FE(class_parents, arginfo_class_parents) 874 PHP_FE(class_implements, arginfo_class_implements) 875 PHP_FE(spl_object_hash, arginfo_spl_object_hash) 876#ifdef SPL_ITERATORS_H 877 PHP_FE(iterator_to_array, arginfo_iterator_to_array) 878 PHP_FE(iterator_count, arginfo_iterator) 879 PHP_FE(iterator_apply, arginfo_iterator_apply) 880#endif /* SPL_ITERATORS_H */ 881 PHP_FE_END 882}; 883/* }}} */ 884 885/* {{{ PHP_MINIT_FUNCTION(spl) 886 */ 887PHP_MINIT_FUNCTION(spl) 888{ 889 PHP_MINIT(spl_exceptions)(INIT_FUNC_ARGS_PASSTHRU); 890 PHP_MINIT(spl_iterators)(INIT_FUNC_ARGS_PASSTHRU); 891 PHP_MINIT(spl_array)(INIT_FUNC_ARGS_PASSTHRU); 892 PHP_MINIT(spl_directory)(INIT_FUNC_ARGS_PASSTHRU); 893 PHP_MINIT(spl_dllist)(INIT_FUNC_ARGS_PASSTHRU); 894 PHP_MINIT(spl_heap)(INIT_FUNC_ARGS_PASSTHRU); 895 PHP_MINIT(spl_fixedarray)(INIT_FUNC_ARGS_PASSTHRU); 896 PHP_MINIT(spl_observer)(INIT_FUNC_ARGS_PASSTHRU); 897 898 return SUCCESS; 899} 900/* }}} */ 901 902PHP_RINIT_FUNCTION(spl) /* {{{ */ 903{ 904 SPL_G(autoload_extensions) = NULL; 905 SPL_G(autoload_extensions_len) = 0; 906 SPL_G(autoload_functions) = NULL; 907 SPL_G(hash_mask_init) = 0; 908 return SUCCESS; 909} /* }}} */ 910 911PHP_RSHUTDOWN_FUNCTION(spl) /* {{{ */ 912{ 913 if (SPL_G(autoload_extensions)) { 914 efree(SPL_G(autoload_extensions)); 915 SPL_G(autoload_extensions) = NULL; 916 SPL_G(autoload_extensions_len) = 0; 917 } 918 if (SPL_G(autoload_functions)) { 919 zend_hash_destroy(SPL_G(autoload_functions)); 920 FREE_HASHTABLE(SPL_G(autoload_functions)); 921 SPL_G(autoload_functions) = NULL; 922 } 923 if (SPL_G(hash_mask_init)) { 924 SPL_G(hash_mask_init) = 0; 925 } 926 return SUCCESS; 927} /* }}} */ 928 929/* {{{ spl_module_entry 930 */ 931zend_module_entry spl_module_entry = { 932 STANDARD_MODULE_HEADER, 933 "SPL", 934 spl_functions, 935 PHP_MINIT(spl), 936 NULL, 937 PHP_RINIT(spl), 938 PHP_RSHUTDOWN(spl), 939 PHP_MINFO(spl), 940 "0.2", 941 PHP_MODULE_GLOBALS(spl), 942 PHP_GINIT(spl), 943 NULL, 944 NULL, 945 STANDARD_MODULE_PROPERTIES_EX 946}; 947/* }}} */ 948 949/* 950 * Local variables: 951 * tab-width: 4 952 * c-basic-offset: 4 953 * End: 954 * vim600: fdm=marker 955 * vim: noet sw=4 ts=4 956 */ 957