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