Back to index

php5  5.3.10
php_spl.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Marcus Boerger <helly@php.net>                              |
00016    +----------------------------------------------------------------------+
00017  */
00018 
00019 /* $Id: php_spl.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include "config.h"
00023 #endif
00024 
00025 #include "php.h"
00026 #include "php_ini.h"
00027 #include "php_main.h"
00028 #include "ext/standard/info.h"
00029 #include "php_spl.h"
00030 #include "spl_functions.h"
00031 #include "spl_engine.h"
00032 #include "spl_array.h"
00033 #include "spl_directory.h"
00034 #include "spl_iterators.h"
00035 #include "spl_exceptions.h"
00036 #include "spl_observer.h"
00037 #include "spl_dllist.h"
00038 #include "spl_fixedarray.h"
00039 #include "spl_heap.h"
00040 #include "zend_exceptions.h"
00041 #include "zend_interfaces.h"
00042 #include "ext/standard/php_rand.h"
00043 #include "ext/standard/php_lcg.h"
00044 #include "main/snprintf.h"
00045 
00046 #ifdef COMPILE_DL_SPL
00047 ZEND_GET_MODULE(spl)
00048 #endif
00049 
00050 ZEND_DECLARE_MODULE_GLOBALS(spl)
00051 
00052 #define SPL_DEFAULT_FILE_EXTENSIONS ".inc,.php"
00053 
00054 /* {{{ PHP_GINIT_FUNCTION
00055  */
00056 static PHP_GINIT_FUNCTION(spl)
00057 {
00058        spl_globals->autoload_extensions     = NULL;
00059        spl_globals->autoload_extensions_len = 0;
00060        spl_globals->autoload_functions      = NULL;
00061        spl_globals->autoload_running        = 0;
00062 }
00063 /* }}} */
00064 
00065 static zend_class_entry * spl_find_ce_by_name(char *name, int len, zend_bool autoload TSRMLS_DC)
00066 {
00067        zend_class_entry **ce;
00068        int found;
00069 
00070        if (!autoload) {
00071               char *lc_name;
00072               ALLOCA_FLAG(use_heap)
00073 
00074               lc_name = do_alloca(len + 1, use_heap);
00075               zend_str_tolower_copy(lc_name, name, len);
00076 
00077               found = zend_hash_find(EG(class_table), lc_name, len +1, (void **) &ce);
00078               free_alloca(lc_name, use_heap);
00079        } else {
00080               found = zend_lookup_class(name, len, &ce TSRMLS_CC);
00081        }
00082        if (found != SUCCESS) {
00083               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Class %s does not exist%s", name, autoload ? " and could not be loaded" : "");
00084               return NULL;
00085        }
00086        
00087        return *ce;
00088 }
00089 
00090 /* {{{ proto array class_parents(object instance [, boolean autoload = true])
00091  Return an array containing the names of all parent classes */
00092 PHP_FUNCTION(class_parents)
00093 {
00094        zval *obj;
00095        zend_class_entry *parent_class, *ce;
00096        zend_bool autoload = 1;
00097 
00098        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|b", &obj, &autoload) == FAILURE) {
00099               RETURN_FALSE;
00100        }
00101        
00102        if (Z_TYPE_P(obj) != IS_OBJECT && Z_TYPE_P(obj) != IS_STRING) {
00103               php_error_docref(NULL TSRMLS_CC, E_WARNING, "object or string expected");
00104               RETURN_FALSE;
00105        }
00106        
00107        if (Z_TYPE_P(obj) == IS_STRING) {
00108               if (NULL == (ce = spl_find_ce_by_name(Z_STRVAL_P(obj), Z_STRLEN_P(obj), autoload TSRMLS_CC))) {
00109                      RETURN_FALSE;
00110               }
00111        } else {
00112               ce = Z_OBJCE_P(obj);
00113        }
00114        
00115        array_init(return_value);
00116        parent_class = ce->parent;
00117        while (parent_class) {
00118               spl_add_class_name(return_value, parent_class, 0, 0 TSRMLS_CC);
00119               parent_class = parent_class->parent;
00120        }
00121 }
00122 /* }}} */
00123 
00124 /* {{{ proto array class_implements(mixed what [, bool autoload ])
00125  Return all classes and interfaces implemented by SPL */
00126 PHP_FUNCTION(class_implements)
00127 {
00128        zval *obj;
00129        zend_bool autoload = 1;
00130        zend_class_entry *ce;
00131        
00132        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|b", &obj, &autoload) == FAILURE) {
00133               RETURN_FALSE;
00134        }
00135        if (Z_TYPE_P(obj) != IS_OBJECT && Z_TYPE_P(obj) != IS_STRING) {
00136               php_error_docref(NULL TSRMLS_CC, E_WARNING, "object or string expected");
00137               RETURN_FALSE;
00138        }
00139        
00140        if (Z_TYPE_P(obj) == IS_STRING) {
00141               if (NULL == (ce = spl_find_ce_by_name(Z_STRVAL_P(obj), Z_STRLEN_P(obj), autoload TSRMLS_CC))) {
00142                      RETURN_FALSE;
00143               }
00144        } else {
00145               ce = Z_OBJCE_P(obj);
00146        }
00147        
00148        array_init(return_value);
00149        spl_add_interfaces(return_value, ce, 1, ZEND_ACC_INTERFACE TSRMLS_CC);
00150 }
00151 /* }}} */
00152 
00153 #define SPL_ADD_CLASS(class_name, z_list, sub, allow, ce_flags) \
00154        spl_add_classes(spl_ce_ ## class_name, z_list, sub, allow, ce_flags TSRMLS_CC)
00155 
00156 #define SPL_LIST_CLASSES(z_list, sub, allow, ce_flags) \
00157        SPL_ADD_CLASS(AppendIterator, z_list, sub, allow, ce_flags); \
00158        SPL_ADD_CLASS(ArrayIterator, z_list, sub, allow, ce_flags); \
00159        SPL_ADD_CLASS(ArrayObject, z_list, sub, allow, ce_flags); \
00160        SPL_ADD_CLASS(BadFunctionCallException, z_list, sub, allow, ce_flags); \
00161        SPL_ADD_CLASS(BadMethodCallException, z_list, sub, allow, ce_flags); \
00162        SPL_ADD_CLASS(CachingIterator, z_list, sub, allow, ce_flags); \
00163        SPL_ADD_CLASS(Countable, z_list, sub, allow, ce_flags); \
00164        SPL_ADD_CLASS(DirectoryIterator, z_list, sub, allow, ce_flags); \
00165        SPL_ADD_CLASS(DomainException, z_list, sub, allow, ce_flags); \
00166        SPL_ADD_CLASS(EmptyIterator, z_list, sub, allow, ce_flags); \
00167        SPL_ADD_CLASS(FilesystemIterator, z_list, sub, allow, ce_flags); \
00168        SPL_ADD_CLASS(FilterIterator, z_list, sub, allow, ce_flags); \
00169        SPL_ADD_CLASS(GlobIterator, z_list, sub, allow, ce_flags); \
00170        SPL_ADD_CLASS(InfiniteIterator, z_list, sub, allow, ce_flags); \
00171        SPL_ADD_CLASS(InvalidArgumentException, z_list, sub, allow, ce_flags); \
00172        SPL_ADD_CLASS(IteratorIterator, z_list, sub, allow, ce_flags); \
00173        SPL_ADD_CLASS(LengthException, z_list, sub, allow, ce_flags); \
00174        SPL_ADD_CLASS(LimitIterator, z_list, sub, allow, ce_flags); \
00175        SPL_ADD_CLASS(LogicException, z_list, sub, allow, ce_flags); \
00176        SPL_ADD_CLASS(MultipleIterator, z_list, sub, allow, ce_flags); \
00177        SPL_ADD_CLASS(NoRewindIterator, z_list, sub, allow, ce_flags); \
00178        SPL_ADD_CLASS(OuterIterator, z_list, sub, allow, ce_flags); \
00179        SPL_ADD_CLASS(OutOfBoundsException, z_list, sub, allow, ce_flags); \
00180        SPL_ADD_CLASS(OutOfRangeException, z_list, sub, allow, ce_flags); \
00181        SPL_ADD_CLASS(OverflowException, z_list, sub, allow, ce_flags); \
00182        SPL_ADD_CLASS(ParentIterator, z_list, sub, allow, ce_flags); \
00183        SPL_ADD_CLASS(RangeException, z_list, sub, allow, ce_flags); \
00184        SPL_ADD_CLASS(RecursiveArrayIterator, z_list, sub, allow, ce_flags); \
00185        SPL_ADD_CLASS(RecursiveCachingIterator, z_list, sub, allow, ce_flags); \
00186        SPL_ADD_CLASS(RecursiveDirectoryIterator, z_list, sub, allow, ce_flags); \
00187        SPL_ADD_CLASS(RecursiveFilterIterator, z_list, sub, allow, ce_flags); \
00188        SPL_ADD_CLASS(RecursiveIterator, z_list, sub, allow, ce_flags); \
00189        SPL_ADD_CLASS(RecursiveIteratorIterator, z_list, sub, allow, ce_flags); \
00190        SPL_ADD_CLASS(RecursiveRegexIterator, z_list, sub, allow, ce_flags); \
00191        SPL_ADD_CLASS(RecursiveTreeIterator, z_list, sub, allow, ce_flags); \
00192        SPL_ADD_CLASS(RegexIterator, z_list, sub, allow, ce_flags); \
00193        SPL_ADD_CLASS(RuntimeException, z_list, sub, allow, ce_flags); \
00194        SPL_ADD_CLASS(SeekableIterator, z_list, sub, allow, ce_flags); \
00195        SPL_ADD_CLASS(SplDoublyLinkedList, z_list, sub, allow, ce_flags); \
00196        SPL_ADD_CLASS(SplFileInfo, z_list, sub, allow, ce_flags); \
00197        SPL_ADD_CLASS(SplFileObject, z_list, sub, allow, ce_flags); \
00198        SPL_ADD_CLASS(SplFixedArray, z_list, sub, allow, ce_flags); \
00199        SPL_ADD_CLASS(SplHeap, z_list, sub, allow, ce_flags); \
00200        SPL_ADD_CLASS(SplMinHeap, z_list, sub, allow, ce_flags); \
00201        SPL_ADD_CLASS(SplMaxHeap, z_list, sub, allow, ce_flags); \
00202        SPL_ADD_CLASS(SplObjectStorage, z_list, sub, allow, ce_flags); \
00203        SPL_ADD_CLASS(SplObserver, z_list, sub, allow, ce_flags); \
00204        SPL_ADD_CLASS(SplPriorityQueue, z_list, sub, allow, ce_flags); \
00205        SPL_ADD_CLASS(SplQueue, z_list, sub, allow, ce_flags); \
00206        SPL_ADD_CLASS(SplStack, z_list, sub, allow, ce_flags); \
00207        SPL_ADD_CLASS(SplSubject, z_list, sub, allow, ce_flags); \
00208        SPL_ADD_CLASS(SplTempFileObject, z_list, sub, allow, ce_flags); \
00209        SPL_ADD_CLASS(UnderflowException, z_list, sub, allow, ce_flags); \
00210        SPL_ADD_CLASS(UnexpectedValueException, z_list, sub, allow, ce_flags); \
00211 
00212 /* {{{ proto array spl_classes()
00213  Return an array containing the names of all clsses and interfaces defined in SPL */
00214 PHP_FUNCTION(spl_classes)
00215 {
00216        array_init(return_value);
00217        
00218        SPL_LIST_CLASSES(return_value, 0, 0, 0)
00219 }
00220 /* }}} */
00221 
00222 static int spl_autoload(const char *class_name, const char * lc_name, int class_name_len, const char * file_extension TSRMLS_DC) /* {{{ */
00223 {
00224        char *class_file;
00225        int class_file_len;
00226        int dummy = 1;
00227        zend_file_handle file_handle;
00228        zend_op_array *new_op_array;
00229        zval *result = NULL;
00230        int ret;
00231 
00232        class_file_len = spprintf(&class_file, 0, "%s%s", lc_name, file_extension);
00233 
00234 #if DEFAULT_SLASH != '\\'
00235        {
00236               char *ptr = class_file;
00237               char *end = ptr + class_file_len;
00238               
00239               while ((ptr = memchr(ptr, '\\', (end - ptr))) != NULL) {
00240                      *ptr = DEFAULT_SLASH;
00241               }
00242        }
00243 #endif
00244 
00245        ret = php_stream_open_for_zend_ex(class_file, &file_handle, ENFORCE_SAFE_MODE|USE_PATH|STREAM_OPEN_FOR_INCLUDE TSRMLS_CC);
00246 
00247        if (ret == SUCCESS) {
00248               if (!file_handle.opened_path) {
00249                      file_handle.opened_path = estrndup(class_file, class_file_len);
00250               }
00251               if (zend_hash_add(&EG(included_files), file_handle.opened_path, strlen(file_handle.opened_path)+1, (void *)&dummy, sizeof(int), NULL)==SUCCESS) {
00252                      new_op_array = zend_compile_file(&file_handle, ZEND_REQUIRE TSRMLS_CC);
00253                      zend_destroy_file_handle(&file_handle TSRMLS_CC);
00254               } else {
00255                      new_op_array = NULL;
00256                      zend_file_handle_dtor(&file_handle TSRMLS_CC);
00257               }
00258               if (new_op_array) {
00259                      EG(return_value_ptr_ptr) = &result;
00260                      EG(active_op_array) = new_op_array;
00261                      if (!EG(active_symbol_table)) {
00262                             zend_rebuild_symbol_table(TSRMLS_C);
00263                      }
00264 
00265                      zend_execute(new_op_array TSRMLS_CC);
00266        
00267                      destroy_op_array(new_op_array TSRMLS_CC);
00268                      efree(new_op_array);
00269                      if (!EG(exception)) {
00270                             if (EG(return_value_ptr_ptr)) {
00271                                    zval_ptr_dtor(EG(return_value_ptr_ptr));
00272                             }
00273                      }
00274 
00275                      efree(class_file);
00276                      return zend_hash_exists(EG(class_table), (char*)lc_name, class_name_len+1);
00277               }
00278        }
00279        efree(class_file);
00280        return 0;
00281 } /* }}} */
00282 
00283 /* {{{ proto void spl_autoload(string class_name [, string file_extensions])
00284  Default implementation for __autoload() */
00285 PHP_FUNCTION(spl_autoload)
00286 {
00287        char *class_name, *lc_name, *file_exts = SPL_G(autoload_extensions);
00288        int class_name_len, file_exts_len = SPL_G(autoload_extensions_len), found = 0;
00289        char *copy, *pos1, *pos2;
00290        zval **original_return_value = EG(return_value_ptr_ptr);
00291        zend_op **original_opline_ptr = EG(opline_ptr);
00292        zend_op_array *original_active_op_array = EG(active_op_array);
00293        
00294        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &class_name, &class_name_len, &file_exts, &file_exts_len) == FAILURE) {
00295               RETURN_FALSE;
00296        }
00297 
00298        if (file_exts == NULL) { /* autoload_extensions is not intialzed, set to defaults */
00299               copy = pos1 = estrndup(SPL_DEFAULT_FILE_EXTENSIONS, sizeof(SPL_DEFAULT_FILE_EXTENSIONS)-1);
00300        } else {
00301               copy = pos1 = estrndup(file_exts, file_exts_len);
00302        }
00303        lc_name = zend_str_tolower_dup(class_name, class_name_len);
00304        while(pos1 && *pos1 && !EG(exception)) {
00305               EG(return_value_ptr_ptr) = original_return_value;
00306               EG(opline_ptr) = original_opline_ptr;
00307               EG(active_op_array) = original_active_op_array;
00308               pos2 = strchr(pos1, ',');
00309               if (pos2) *pos2 = '\0';
00310               if (spl_autoload(class_name, lc_name, class_name_len, pos1 TSRMLS_CC)) {
00311                      found = 1;
00312                      break; /* loaded */
00313               }
00314               pos1 = pos2 ? pos2 + 1 : NULL;
00315        }
00316        efree(lc_name);
00317        if (copy) {
00318               efree(copy);
00319        }
00320 
00321        EG(return_value_ptr_ptr) = original_return_value;
00322        EG(opline_ptr) = original_opline_ptr;
00323        EG(active_op_array) = original_active_op_array;
00324 
00325        if (!found && !SPL_G(autoload_running)) {
00326               /* For internal errors, we generate E_ERROR, for direct calls an exception is thrown.
00327                * The "scope" is determined by an opcode, if it is ZEND_FETCH_CLASS we know function was called indirectly by
00328                * the Zend engine.
00329                */
00330               if (active_opline->opcode != ZEND_FETCH_CLASS) {
00331                      zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Class %s could not be loaded", class_name);
00332               } else {
00333                      php_error_docref(NULL TSRMLS_CC, E_ERROR, "Class %s could not be loaded", class_name);
00334               }
00335        }
00336 } /* }}} */
00337 
00338 /* {{{ proto string spl_autoload_extensions([string file_extensions])
00339  Register and return default file extensions for spl_autoload */
00340 PHP_FUNCTION(spl_autoload_extensions)
00341 {
00342        char *file_exts = NULL;
00343        int file_exts_len;
00344 
00345        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &file_exts, &file_exts_len) == FAILURE) {
00346               return;
00347        }
00348        if (file_exts) {
00349               if (SPL_G(autoload_extensions)) {
00350                      efree(SPL_G(autoload_extensions));
00351               }
00352               SPL_G(autoload_extensions) = estrndup(file_exts, file_exts_len);
00353               SPL_G(autoload_extensions_len) = file_exts_len;
00354        }
00355 
00356        if (SPL_G(autoload_extensions) == NULL) {
00357               RETURN_STRINGL(SPL_DEFAULT_FILE_EXTENSIONS, sizeof(SPL_DEFAULT_FILE_EXTENSIONS) - 1, 1);
00358        } else {
00359               RETURN_STRINGL(SPL_G(autoload_extensions), SPL_G(autoload_extensions_len), 1);
00360        }
00361 } /* }}} */
00362 
00363 typedef struct {
00364        zend_function *func_ptr;
00365        zval *obj;
00366        zval *closure;
00367        zend_class_entry *ce;
00368 } autoload_func_info;
00369 
00370 static void autoload_func_info_dtor(autoload_func_info *alfi)
00371 {
00372        if (alfi->obj) {
00373               zval_ptr_dtor(&alfi->obj);
00374        }
00375        if (alfi->closure) {
00376               zval_ptr_dtor(&alfi->closure);
00377        }
00378 }
00379 
00380 /* {{{ proto void spl_autoload_call(string class_name)
00381  Try all registerd autoload function to load the requested class */
00382 PHP_FUNCTION(spl_autoload_call)
00383 {
00384        zval *class_name, *retval = NULL;
00385        int class_name_len;
00386        char *func_name, *lc_name;
00387        uint func_name_len;
00388        ulong dummy;
00389        HashPosition function_pos;
00390        autoload_func_info *alfi;
00391 
00392        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &class_name) == FAILURE || Z_TYPE_P(class_name) != IS_STRING) {
00393               return;
00394        }
00395 
00396        if (SPL_G(autoload_functions)) {
00397               int l_autoload_running = SPL_G(autoload_running);
00398               SPL_G(autoload_running) = 1;
00399               class_name_len = Z_STRLEN_P(class_name);
00400               lc_name = zend_str_tolower_dup(Z_STRVAL_P(class_name), class_name_len);
00401               zend_hash_internal_pointer_reset_ex(SPL_G(autoload_functions), &function_pos);
00402               while(zend_hash_has_more_elements_ex(SPL_G(autoload_functions), &function_pos) == SUCCESS) {
00403                      zend_hash_get_current_key_ex(SPL_G(autoload_functions), &func_name, &func_name_len, &dummy, 0, &function_pos);
00404                      zend_hash_get_current_data_ex(SPL_G(autoload_functions), (void **) &alfi, &function_pos);
00405                      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);
00406                      zend_exception_save(TSRMLS_C);
00407                      if (retval) {
00408                             zval_ptr_dtor(&retval);
00409                             retval = NULL;
00410                      }
00411                      if (zend_hash_exists(EG(class_table), lc_name, class_name_len + 1)) {
00412                             break;
00413                      }
00414                      zend_hash_move_forward_ex(SPL_G(autoload_functions), &function_pos);
00415               }
00416               zend_exception_restore(TSRMLS_C);
00417               efree(lc_name);
00418               SPL_G(autoload_running) = l_autoload_running;
00419        } else {
00420               /* do not use or overwrite &EG(autoload_func) here */
00421               zend_call_method_with_1_params(NULL, NULL, NULL, "spl_autoload", NULL, class_name);
00422        }
00423 } /* }}} */
00424 
00425 #define HT_MOVE_TAIL_TO_HEAD(ht)                                             \
00426        (ht)->pListTail->pListNext = (ht)->pListHead;                  \
00427        (ht)->pListHead = (ht)->pListTail;                                    \
00428        (ht)->pListTail = (ht)->pListHead->pListLast;                  \
00429        (ht)->pListHead->pListNext->pListLast = (ht)->pListHead;\
00430        (ht)->pListTail->pListNext = NULL;                                    \
00431        (ht)->pListHead->pListLast = NULL;
00432 
00433 /* {{{ proto bool spl_autoload_register([mixed autoload_function = "spl_autoload" [, throw = true [, prepend]]])
00434  Register given function as __autoload() implementation */
00435 PHP_FUNCTION(spl_autoload_register)
00436 {
00437        char *func_name, *error = NULL;
00438        int  func_name_len;
00439        char *lc_name = NULL;
00440        zval *zcallable = NULL;
00441        zend_bool do_throw = 1;
00442        zend_bool prepend  = 0;
00443        zend_function *spl_func_ptr;
00444        autoload_func_info alfi;
00445        zval *obj_ptr;
00446        zend_fcall_info_cache fcc;
00447 
00448        if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "|zbb", &zcallable, &do_throw, &prepend) == FAILURE) {
00449               return;
00450        }
00451 
00452        if (ZEND_NUM_ARGS()) {
00453               if (Z_TYPE_P(zcallable) == IS_STRING) {
00454                      if (Z_STRLEN_P(zcallable) == sizeof("spl_autoload_call") - 1) {
00455                             if (!zend_binary_strcasecmp(Z_STRVAL_P(zcallable), sizeof("spl_autoload_call"), "spl_autoload_call", sizeof("spl_autoload_call"))) {
00456                                    if (do_throw) {
00457                                           zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Function spl_autoload_call() cannot be registered");
00458                                    }
00459                                    RETURN_FALSE;
00460                             }
00461                      }
00462               }
00463        
00464               if (!zend_is_callable_ex(zcallable, NULL, IS_CALLABLE_STRICT, &func_name, &func_name_len, &fcc, &error TSRMLS_CC)) {
00465                      alfi.ce = fcc.calling_scope;
00466                      alfi.func_ptr = fcc.function_handler;
00467                      obj_ptr = fcc.object_ptr;
00468                      if (Z_TYPE_P(zcallable) == IS_ARRAY) {
00469                             if (!obj_ptr && alfi.func_ptr && !(alfi.func_ptr->common.fn_flags & ZEND_ACC_STATIC)) {
00470                                    if (do_throw) {
00471                                           zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Passed array specifies a non static method but no object (%s)", error);
00472                                    }
00473                                    if (error) {
00474                                           efree(error);
00475                                    }
00476                                    efree(func_name);
00477                                    RETURN_FALSE;
00478                             }
00479                             else if (do_throw) {
00480                                    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);
00481                             }
00482                             if (error) {
00483                                    efree(error);
00484                             }
00485                             efree(func_name);
00486                             RETURN_FALSE;
00487                      } else if (Z_TYPE_P(zcallable) == IS_STRING) {
00488                             if (do_throw) {
00489                                    zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Function '%s' not %s (%s)", func_name, alfi.func_ptr ? "callable" : "found", error);
00490                             }
00491                             if (error) {
00492                                    efree(error);
00493                             }
00494                             efree(func_name);
00495                             RETURN_FALSE;
00496                      } else {
00497                             if (do_throw) {
00498                                    zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Illegal value passed (%s)", error);
00499                             }
00500                             if (error) {
00501                                    efree(error);
00502                             }
00503                             efree(func_name);
00504                             RETURN_FALSE;
00505                      }
00506               }
00507               alfi.closure = NULL;
00508               alfi.ce = fcc.calling_scope;
00509               alfi.func_ptr = fcc.function_handler;
00510               obj_ptr = fcc.object_ptr;
00511               if (error) {
00512                      efree(error);
00513               }
00514        
00515               lc_name = safe_emalloc(func_name_len, 1, sizeof(long) + 1);
00516               zend_str_tolower_copy(lc_name, func_name, func_name_len);
00517               efree(func_name);
00518 
00519               if (Z_TYPE_P(zcallable) == IS_OBJECT) {
00520                      alfi.closure = zcallable;
00521                      Z_ADDREF_P(zcallable);
00522 
00523                      lc_name = erealloc(lc_name, func_name_len + 2 + sizeof(zend_object_handle));
00524                      memcpy(lc_name + func_name_len, &Z_OBJ_HANDLE_P(zcallable),
00525                             sizeof(zend_object_handle));
00526                      func_name_len += sizeof(zend_object_handle);
00527                      lc_name[func_name_len] = '\0';
00528               }
00529 
00530               if (SPL_G(autoload_functions) && zend_hash_exists(SPL_G(autoload_functions), (char*)lc_name, func_name_len+1)) {
00531                      if (alfi.closure) {
00532                             Z_DELREF_P(zcallable);
00533                      }
00534                      goto skip;
00535               }
00536 
00537               if (obj_ptr && !(alfi.func_ptr->common.fn_flags & ZEND_ACC_STATIC)) {
00538                      /* add object id to the hash to ensure uniqueness, for more reference look at bug #40091 */
00539                      lc_name = erealloc(lc_name, func_name_len + 2 + sizeof(zend_object_handle));
00540                      memcpy(lc_name + func_name_len, &Z_OBJ_HANDLE_P(obj_ptr), sizeof(zend_object_handle));
00541                      func_name_len += sizeof(zend_object_handle);
00542                      lc_name[func_name_len] = '\0';
00543                      alfi.obj = obj_ptr;
00544                      Z_ADDREF_P(alfi.obj);
00545               } else {
00546                      alfi.obj = NULL;
00547               }
00548 
00549               if (!SPL_G(autoload_functions)) {
00550                      ALLOC_HASHTABLE(SPL_G(autoload_functions));
00551                      zend_hash_init(SPL_G(autoload_functions), 1, NULL, (dtor_func_t) autoload_func_info_dtor, 0);
00552               }
00553 
00554               zend_hash_find(EG(function_table), "spl_autoload", sizeof("spl_autoload"), (void **) &spl_func_ptr);
00555 
00556               if (EG(autoload_func) == spl_func_ptr) { /* registered already, so we insert that first */
00557                      autoload_func_info spl_alfi;
00558 
00559                      spl_alfi.func_ptr = spl_func_ptr;
00560                      spl_alfi.obj = NULL;
00561                      spl_alfi.ce = NULL;
00562                      spl_alfi.closure = NULL;
00563                      zend_hash_add(SPL_G(autoload_functions), "spl_autoload", sizeof("spl_autoload"), &spl_alfi, sizeof(autoload_func_info), NULL);
00564                      if (prepend && SPL_G(autoload_functions)->nNumOfElements > 1) {
00565                             /* Move the newly created element to the head of the hashtable */
00566                             HT_MOVE_TAIL_TO_HEAD(SPL_G(autoload_functions));
00567                      }
00568               }
00569 
00570               zend_hash_add(SPL_G(autoload_functions), lc_name, func_name_len+1, &alfi.func_ptr, sizeof(autoload_func_info), NULL);
00571               if (prepend && SPL_G(autoload_functions)->nNumOfElements > 1) {
00572                      /* Move the newly created element to the head of the hashtable */
00573                      HT_MOVE_TAIL_TO_HEAD(SPL_G(autoload_functions));
00574               }
00575 skip:
00576               efree(lc_name);
00577        }
00578 
00579        if (SPL_G(autoload_functions)) {
00580               zend_hash_find(EG(function_table), "spl_autoload_call", sizeof("spl_autoload_call"), (void **) &EG(autoload_func));
00581        } else {
00582               zend_hash_find(EG(function_table), "spl_autoload", sizeof("spl_autoload"), (void **) &EG(autoload_func));
00583        }
00584        RETURN_TRUE;
00585 } /* }}} */
00586 
00587 /* {{{ proto bool spl_autoload_unregister(mixed autoload_function)
00588  Unregister given function as __autoload() implementation */
00589 PHP_FUNCTION(spl_autoload_unregister)
00590 {
00591        char *func_name, *error = NULL;
00592        int func_name_len;
00593        char *lc_name = NULL;
00594        zval *zcallable;
00595        int success = FAILURE;
00596        zend_function *spl_func_ptr;
00597        zval *obj_ptr;
00598        zend_fcall_info_cache fcc;
00599 
00600        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zcallable) == FAILURE) {
00601               return;
00602        }
00603 
00604        if (!zend_is_callable_ex(zcallable, NULL, IS_CALLABLE_CHECK_SYNTAX_ONLY, &func_name, &func_name_len, &fcc, &error TSRMLS_CC)) {
00605               zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Unable to unregister invalid function (%s)", error);
00606               if (error) {
00607                      efree(error);
00608               }
00609               if (func_name) {
00610                      efree(func_name);
00611               }
00612               RETURN_FALSE;
00613        }
00614        obj_ptr = fcc.object_ptr;
00615        if (error) {
00616               efree(error);
00617        }
00618 
00619        lc_name = safe_emalloc(func_name_len, 1, sizeof(long) + 1);
00620        zend_str_tolower_copy(lc_name, func_name, func_name_len);
00621        efree(func_name);
00622 
00623        if (Z_TYPE_P(zcallable) == IS_OBJECT) {
00624               lc_name = erealloc(lc_name, func_name_len + 2 + sizeof(zend_object_handle));
00625               memcpy(lc_name + func_name_len, &Z_OBJ_HANDLE_P(zcallable),
00626                      sizeof(zend_object_handle));
00627               func_name_len += sizeof(zend_object_handle);
00628               lc_name[func_name_len] = '\0';
00629        }
00630 
00631        if (SPL_G(autoload_functions)) {
00632               if (func_name_len == sizeof("spl_autoload_call")-1 && !strcmp(lc_name, "spl_autoload_call")) {
00633                      /* remove all */
00634                      zend_hash_destroy(SPL_G(autoload_functions));
00635                      FREE_HASHTABLE(SPL_G(autoload_functions));
00636                      SPL_G(autoload_functions) = NULL;
00637                      EG(autoload_func) = NULL;
00638                      success = SUCCESS;
00639               } else {
00640                      /* remove specific */
00641                      success = zend_hash_del(SPL_G(autoload_functions), lc_name, func_name_len+1);
00642                      if (success != SUCCESS && obj_ptr) {
00643                             lc_name = erealloc(lc_name, func_name_len + 2 + sizeof(zend_object_handle));
00644                             memcpy(lc_name + func_name_len, &Z_OBJ_HANDLE_P(obj_ptr), sizeof(zend_object_handle));
00645                             func_name_len += sizeof(zend_object_handle);
00646                             lc_name[func_name_len] = '\0';
00647                             success = zend_hash_del(SPL_G(autoload_functions), lc_name, func_name_len+1);
00648                      }
00649               }
00650        } else if (func_name_len == sizeof("spl_autoload")-1 && !strcmp(lc_name, "spl_autoload")) {
00651               /* register single spl_autoload() */
00652               zend_hash_find(EG(function_table), "spl_autoload", sizeof("spl_autoload"), (void **) &spl_func_ptr);
00653 
00654               if (EG(autoload_func) == spl_func_ptr) {
00655                      success = SUCCESS;
00656                      EG(autoload_func) = NULL;
00657               }
00658        }
00659 
00660        efree(lc_name);
00661        RETURN_BOOL(success == SUCCESS);
00662 } /* }}} */
00663 
00664 /* {{{ proto false|array spl_autoload_functions()
00665  Return all registered __autoload() functionns */
00666 PHP_FUNCTION(spl_autoload_functions)
00667 {
00668        zend_function *fptr;
00669        HashPosition function_pos;
00670        autoload_func_info *alfi;
00671 
00672        if (zend_parse_parameters_none() == FAILURE) {
00673               return;
00674        }
00675        
00676        if (!EG(autoload_func)) {
00677               if (zend_hash_find(EG(function_table), ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME), (void **) &fptr) == SUCCESS) {
00678                      array_init(return_value);
00679                      add_next_index_stringl(return_value, ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME)-1, 1);
00680                      return;
00681               }
00682               RETURN_FALSE;
00683        }
00684 
00685        zend_hash_find(EG(function_table), "spl_autoload_call", sizeof("spl_autoload_call"), (void **) &fptr);
00686 
00687        if (EG(autoload_func) == fptr) {
00688               array_init(return_value);
00689               zend_hash_internal_pointer_reset_ex(SPL_G(autoload_functions), &function_pos);
00690               while(zend_hash_has_more_elements_ex(SPL_G(autoload_functions), &function_pos) == SUCCESS) {
00691                      zend_hash_get_current_data_ex(SPL_G(autoload_functions), (void **) &alfi, &function_pos);
00692                      if (alfi->closure) {
00693                             Z_ADDREF_P(alfi->closure);
00694                             add_next_index_zval(return_value, alfi->closure);
00695                      } else if (alfi->func_ptr->common.scope) {
00696                             zval *tmp;
00697                             MAKE_STD_ZVAL(tmp);
00698                             array_init(tmp);
00699 
00700                             if (alfi->obj) {
00701                                    Z_ADDREF_P(alfi->obj);
00702                                    add_next_index_zval(tmp, alfi->obj);
00703                             } else {
00704                                    add_next_index_string(tmp, alfi->ce->name, 1);
00705                             }
00706                             add_next_index_string(tmp, alfi->func_ptr->common.function_name, 1);
00707                             add_next_index_zval(return_value, tmp);
00708                      } else
00709                             add_next_index_string(return_value, alfi->func_ptr->common.function_name, 1);
00710 
00711                      zend_hash_move_forward_ex(SPL_G(autoload_functions), &function_pos);
00712               }
00713               return;
00714        }
00715 
00716        array_init(return_value);
00717        add_next_index_string(return_value, EG(autoload_func)->common.function_name, 1);
00718 } /* }}} */
00719 
00720 /* {{{ proto string spl_object_hash(object obj)
00721  Return hash id for given object */
00722 PHP_FUNCTION(spl_object_hash)
00723 {
00724        zval *obj;
00725        char* hash;
00726 
00727        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj) == FAILURE) {
00728               return;
00729        }
00730        
00731        hash = emalloc(33);
00732        php_spl_object_hash(obj, hash TSRMLS_CC);
00733        
00734        RETVAL_STRING(hash, 0);
00735 }
00736 /* }}} */
00737 
00738 PHPAPI void php_spl_object_hash(zval *obj, char *result TSRMLS_DC) /* {{{*/
00739 {
00740        intptr_t hash_handle, hash_handlers;
00741        char *hex;
00742 
00743        if (!SPL_G(hash_mask_init)) {
00744               if (!BG(mt_rand_is_seeded)) {
00745                      php_mt_srand(GENERATE_SEED() TSRMLS_CC);
00746               }
00747 
00748               SPL_G(hash_mask_handle)   = (intptr_t)(php_mt_rand(TSRMLS_C) >> 1);
00749               SPL_G(hash_mask_handlers) = (intptr_t)(php_mt_rand(TSRMLS_C) >> 1);
00750               SPL_G(hash_mask_init) = 1;
00751        }
00752 
00753        hash_handle   = SPL_G(hash_mask_handle)^(intptr_t)Z_OBJ_HANDLE_P(obj);
00754        hash_handlers = SPL_G(hash_mask_handlers)^(intptr_t)Z_OBJ_HT_P(obj);
00755 
00756        spprintf(&hex, 32, "%016x%016x", hash_handle, hash_handlers);
00757 
00758        strlcpy(result, hex, 33);
00759        efree(hex);
00760 }
00761 /* }}} */
00762 
00763 int spl_build_class_list_string(zval **entry, char **list TSRMLS_DC) /* {{{ */
00764 {
00765        char *res;
00766        
00767        spprintf(&res, 0, "%s, %s", *list, Z_STRVAL_PP(entry));
00768        efree(*list);
00769        *list = res;
00770        return ZEND_HASH_APPLY_KEEP;
00771 } /* }}} */
00772 
00773 /* {{{ PHP_MINFO(spl)
00774  */
00775 PHP_MINFO_FUNCTION(spl)
00776 {
00777        zval list;
00778        char *strg;
00779 
00780        php_info_print_table_start();
00781        php_info_print_table_header(2, "SPL support",        "enabled");
00782 
00783        INIT_PZVAL(&list);
00784        array_init(&list);
00785        SPL_LIST_CLASSES(&list, 0, 1, ZEND_ACC_INTERFACE)
00786        strg = estrdup("");
00787        zend_hash_apply_with_argument(Z_ARRVAL_P(&list), (apply_func_arg_t)spl_build_class_list_string, &strg TSRMLS_CC);
00788        zval_dtor(&list);
00789        php_info_print_table_row(2, "Interfaces", strg + 2);
00790        efree(strg);
00791 
00792        INIT_PZVAL(&list);
00793        array_init(&list);
00794        SPL_LIST_CLASSES(&list, 0, -1, ZEND_ACC_INTERFACE)
00795        strg = estrdup("");
00796        zend_hash_apply_with_argument(Z_ARRVAL_P(&list), (apply_func_arg_t)spl_build_class_list_string, &strg TSRMLS_CC);
00797        zval_dtor(&list);
00798        php_info_print_table_row(2, "Classes", strg + 2);
00799        efree(strg);
00800 
00801        php_info_print_table_end();
00802 }
00803 /* }}} */
00804 
00805 /* {{{ arginfo */
00806 ZEND_BEGIN_ARG_INFO_EX(arginfo_iterator_to_array, 0, 0, 1)
00807        ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0)
00808        ZEND_ARG_INFO(0, use_keys)
00809 ZEND_END_ARG_INFO();
00810 
00811 ZEND_BEGIN_ARG_INFO(arginfo_iterator, 0)
00812        ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0)
00813 ZEND_END_ARG_INFO();
00814 
00815 ZEND_BEGIN_ARG_INFO_EX(arginfo_iterator_apply, 0, 0, 2)
00816        ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0)
00817        ZEND_ARG_INFO(0, function)
00818        ZEND_ARG_ARRAY_INFO(0, args, 1)
00819 ZEND_END_ARG_INFO();
00820 
00821 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_parents, 0, 0, 1)
00822        ZEND_ARG_INFO(0, instance)
00823        ZEND_ARG_INFO(0, autoload)
00824 ZEND_END_ARG_INFO()
00825 
00826 ZEND_BEGIN_ARG_INFO_EX(arginfo_class_implements, 0, 0, 1)
00827        ZEND_ARG_INFO(0, what)
00828        ZEND_ARG_INFO(0, autoload)
00829 ZEND_END_ARG_INFO()
00830 
00831 ZEND_BEGIN_ARG_INFO(arginfo_spl_classes, 0)
00832 ZEND_END_ARG_INFO()
00833 
00834 ZEND_BEGIN_ARG_INFO(arginfo_spl_autoload_functions, 0)
00835 ZEND_END_ARG_INFO()
00836 
00837 ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload, 0, 0, 1)
00838        ZEND_ARG_INFO(0, class_name)
00839        ZEND_ARG_INFO(0, file_extensions)
00840 ZEND_END_ARG_INFO()
00841 
00842 ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_extensions, 0, 0, 0)
00843        ZEND_ARG_INFO(0, file_extensions)
00844 ZEND_END_ARG_INFO()
00845 
00846 ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_call, 0, 0, 1)
00847        ZEND_ARG_INFO(0, class_name)
00848 ZEND_END_ARG_INFO()
00849 
00850 ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_register, 0, 0, 0)
00851        ZEND_ARG_INFO(0, autoload_function)
00852 ZEND_END_ARG_INFO()
00853 
00854 ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_autoload_unregister, 0, 0, 1)
00855        ZEND_ARG_INFO(0, autoload_function)
00856 ZEND_END_ARG_INFO()
00857 
00858 ZEND_BEGIN_ARG_INFO_EX(arginfo_spl_object_hash, 0, 0, 1)
00859        ZEND_ARG_INFO(0, obj)
00860 ZEND_END_ARG_INFO()
00861 /* }}} */
00862 
00863 /* {{{ spl_functions
00864  */
00865 const zend_function_entry spl_functions[] = {
00866        PHP_FE(spl_classes,             arginfo_spl_classes)
00867        PHP_FE(spl_autoload,            arginfo_spl_autoload)
00868        PHP_FE(spl_autoload_extensions, arginfo_spl_autoload_extensions)
00869        PHP_FE(spl_autoload_register,   arginfo_spl_autoload_register)
00870        PHP_FE(spl_autoload_unregister, arginfo_spl_autoload_unregister)
00871        PHP_FE(spl_autoload_functions,  arginfo_spl_autoload_functions)
00872        PHP_FE(spl_autoload_call,       arginfo_spl_autoload_call)
00873        PHP_FE(class_parents,           arginfo_class_parents)
00874        PHP_FE(class_implements,        arginfo_class_implements)
00875        PHP_FE(spl_object_hash,         arginfo_spl_object_hash)
00876 #ifdef SPL_ITERATORS_H
00877        PHP_FE(iterator_to_array,       arginfo_iterator_to_array)
00878        PHP_FE(iterator_count,          arginfo_iterator)
00879        PHP_FE(iterator_apply,          arginfo_iterator_apply)
00880 #endif /* SPL_ITERATORS_H */
00881        PHP_FE_END
00882 };
00883 /* }}} */
00884 
00885 /* {{{ PHP_MINIT_FUNCTION(spl)
00886  */
00887 PHP_MINIT_FUNCTION(spl)
00888 {
00889        PHP_MINIT(spl_exceptions)(INIT_FUNC_ARGS_PASSTHRU);
00890        PHP_MINIT(spl_iterators)(INIT_FUNC_ARGS_PASSTHRU);
00891        PHP_MINIT(spl_array)(INIT_FUNC_ARGS_PASSTHRU);
00892        PHP_MINIT(spl_directory)(INIT_FUNC_ARGS_PASSTHRU);
00893        PHP_MINIT(spl_dllist)(INIT_FUNC_ARGS_PASSTHRU);
00894        PHP_MINIT(spl_heap)(INIT_FUNC_ARGS_PASSTHRU);
00895        PHP_MINIT(spl_fixedarray)(INIT_FUNC_ARGS_PASSTHRU);
00896        PHP_MINIT(spl_observer)(INIT_FUNC_ARGS_PASSTHRU);
00897 
00898        return SUCCESS;
00899 }
00900 /* }}} */
00901 
00902 PHP_RINIT_FUNCTION(spl) /* {{{ */
00903 {
00904        SPL_G(autoload_extensions) = NULL;
00905        SPL_G(autoload_extensions_len) = 0;
00906        SPL_G(autoload_functions) = NULL;
00907        SPL_G(hash_mask_init) = 0;
00908        return SUCCESS;
00909 } /* }}} */
00910 
00911 PHP_RSHUTDOWN_FUNCTION(spl) /* {{{ */
00912 {
00913        if (SPL_G(autoload_extensions)) {
00914               efree(SPL_G(autoload_extensions));
00915               SPL_G(autoload_extensions) = NULL;
00916               SPL_G(autoload_extensions_len) = 0;
00917        }
00918        if (SPL_G(autoload_functions)) {
00919               zend_hash_destroy(SPL_G(autoload_functions));
00920               FREE_HASHTABLE(SPL_G(autoload_functions));
00921               SPL_G(autoload_functions) = NULL;
00922        }
00923        if (SPL_G(hash_mask_init)) {
00924               SPL_G(hash_mask_init) = 0;
00925        }
00926        return SUCCESS;
00927 } /* }}} */
00928 
00929 /* {{{ spl_module_entry
00930  */
00931 zend_module_entry spl_module_entry = {
00932        STANDARD_MODULE_HEADER,
00933        "SPL",
00934        spl_functions,
00935        PHP_MINIT(spl),
00936        NULL,
00937        PHP_RINIT(spl),
00938        PHP_RSHUTDOWN(spl),
00939        PHP_MINFO(spl),
00940        "0.2",
00941        PHP_MODULE_GLOBALS(spl),
00942        PHP_GINIT(spl),
00943        NULL,
00944        NULL,
00945        STANDARD_MODULE_PROPERTIES_EX
00946 };
00947 /* }}} */
00948 
00949 /*
00950  * Local variables:
00951  * tab-width: 4
00952  * c-basic-offset: 4
00953  * End:
00954  * vim600: fdm=marker
00955  * vim: noet sw=4 ts=4
00956  */