Back to index

php5  5.3.10
user_filters.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:                                                             |
00016    | Wez Furlong (wez@thebrainroom.com)                                   |
00017    | Sara Golemon (pollita@php.net)                                       |
00018    +----------------------------------------------------------------------+
00019 */
00020 
00021 /* $Id: user_filters.c 321634 2012-01-01 13:15:04Z felipe $ */
00022 
00023 #include "php.h"
00024 #include "php_globals.h"
00025 #include "ext/standard/basic_functions.h"
00026 #include "ext/standard/file.h"
00027 
00028 #define PHP_STREAM_BRIGADE_RES_NAME       "userfilter.bucket brigade"
00029 #define PHP_STREAM_BUCKET_RES_NAME "userfilter.bucket"
00030 #define PHP_STREAM_FILTER_RES_NAME "userfilter.filter"
00031 
00032 struct php_user_filter_data {
00033        zend_class_entry *ce;
00034        /* variable length; this *must* be last in the structure */
00035        char classname[1];
00036 };
00037 
00038 /* to provide context for calling into the next filter from user-space */
00039 static int le_userfilters;
00040 static int le_bucket_brigade;
00041 static int le_bucket;
00042 
00043 #define GET_FILTER_FROM_OBJ()      { \
00044        zval **tmp; \
00045        if (FAILURE == zend_hash_index_find(Z_OBJPROP_P(this_ptr), 0, (void**)&tmp)) { \
00046               php_error_docref(NULL TSRMLS_CC, E_WARNING, "filter property vanished"); \
00047               RETURN_FALSE; \
00048        } \
00049        ZEND_FETCH_RESOURCE(filter, php_stream_filter*, tmp, -1, "filter", le_userfilters); \
00050 }
00051 
00052 /* define the base filter class */
00053 
00054 PHP_FUNCTION(user_filter_nop)
00055 {
00056 }
00057 ZEND_BEGIN_ARG_INFO(arginfo_php_user_filter_filter, 0)
00058        ZEND_ARG_INFO(0, in)
00059        ZEND_ARG_INFO(0, out)
00060        ZEND_ARG_INFO(1, consumed)
00061        ZEND_ARG_INFO(0, closing)
00062 ZEND_END_ARG_INFO()
00063 
00064 ZEND_BEGIN_ARG_INFO(arginfo_php_user_filter_onCreate, 0)
00065 ZEND_END_ARG_INFO()
00066 
00067 ZEND_BEGIN_ARG_INFO(arginfo_php_user_filter_onClose, 0)
00068 ZEND_END_ARG_INFO()
00069 
00070 static const zend_function_entry user_filter_class_funcs[] = {
00071        PHP_NAMED_FE(filter, PHP_FN(user_filter_nop),           arginfo_php_user_filter_filter)
00072        PHP_NAMED_FE(onCreate,      PHP_FN(user_filter_nop),           arginfo_php_user_filter_onCreate)
00073        PHP_NAMED_FE(onClose,       PHP_FN(user_filter_nop),           arginfo_php_user_filter_onClose)
00074        PHP_FE_END
00075 };
00076 
00077 static zend_class_entry user_filter_class_entry;
00078 
00079 static ZEND_RSRC_DTOR_FUNC(php_bucket_dtor)
00080 {
00081        php_stream_bucket *bucket = (php_stream_bucket *)rsrc->ptr;
00082        if (bucket) {
00083               php_stream_bucket_delref(bucket TSRMLS_CC);
00084               bucket = NULL;
00085        }
00086 }
00087 
00088 PHP_MINIT_FUNCTION(user_filters)
00089 {
00090        zend_class_entry *php_user_filter;
00091        /* init the filter class ancestor */
00092        INIT_CLASS_ENTRY(user_filter_class_entry, "php_user_filter", user_filter_class_funcs);
00093        if ((php_user_filter = zend_register_internal_class(&user_filter_class_entry TSRMLS_CC)) == NULL) {
00094               return FAILURE;
00095        }
00096        zend_declare_property_string(php_user_filter, "filtername", sizeof("filtername")-1, "", ZEND_ACC_PUBLIC TSRMLS_CC);
00097        zend_declare_property_string(php_user_filter, "params", sizeof("params")-1, "", ZEND_ACC_PUBLIC TSRMLS_CC);
00098 
00099        /* init the filter resource; it has no dtor, as streams will always clean it up
00100         * at the correct time */
00101        le_userfilters = zend_register_list_destructors_ex(NULL, NULL, PHP_STREAM_FILTER_RES_NAME, 0);
00102 
00103        if (le_userfilters == FAILURE) {
00104               return FAILURE;
00105        }
00106 
00107        /* Filters will dispose of their brigades */
00108        le_bucket_brigade = zend_register_list_destructors_ex(NULL, NULL, PHP_STREAM_BRIGADE_RES_NAME, module_number);
00109        /* Brigades will dispose of their buckets */
00110        le_bucket = zend_register_list_destructors_ex(php_bucket_dtor, NULL, PHP_STREAM_BUCKET_RES_NAME, module_number);
00111        
00112        if (le_bucket_brigade == FAILURE) {
00113               return FAILURE;
00114        }
00115 
00116        REGISTER_LONG_CONSTANT("PSFS_PASS_ON",                  PSFS_PASS_ON,               CONST_CS | CONST_PERSISTENT);
00117        REGISTER_LONG_CONSTANT("PSFS_FEED_ME",                  PSFS_FEED_ME,               CONST_CS | CONST_PERSISTENT);
00118        REGISTER_LONG_CONSTANT("PSFS_ERR_FATAL",         PSFS_ERR_FATAL,                    CONST_CS | CONST_PERSISTENT);
00119 
00120        REGISTER_LONG_CONSTANT("PSFS_FLAG_NORMAL",              PSFS_FLAG_NORMAL,           CONST_CS | CONST_PERSISTENT);
00121        REGISTER_LONG_CONSTANT("PSFS_FLAG_FLUSH_INC",    PSFS_FLAG_FLUSH_INC, CONST_CS | CONST_PERSISTENT);
00122        REGISTER_LONG_CONSTANT("PSFS_FLAG_FLUSH_CLOSE",  PSFS_FLAG_FLUSH_CLOSE,      CONST_CS | CONST_PERSISTENT);
00123        
00124        return SUCCESS;
00125 }
00126 
00127 PHP_RSHUTDOWN_FUNCTION(user_filters)
00128 {
00129        if (BG(user_filter_map)) {
00130               zend_hash_destroy(BG(user_filter_map));
00131               efree(BG(user_filter_map));
00132               BG(user_filter_map) = NULL;
00133        }
00134 
00135        return SUCCESS;
00136 }
00137 
00138 static void userfilter_dtor(php_stream_filter *thisfilter TSRMLS_DC)
00139 {
00140        zval *obj = (zval*)thisfilter->abstract;
00141        zval func_name;
00142        zval *retval = NULL;
00143 
00144        if (obj == NULL) {
00145               /* If there's no object associated then there's nothing to dispose of */
00146               return;
00147        }
00148 
00149        ZVAL_STRINGL(&func_name, "onclose", sizeof("onclose")-1, 0);
00150 
00151        call_user_function_ex(NULL,
00152                      &obj,
00153                      &func_name,
00154                      &retval,
00155                      0, NULL,
00156                      0, NULL TSRMLS_CC);
00157 
00158        if (retval)
00159               zval_ptr_dtor(&retval);
00160 
00161        /* kill the object */
00162        zval_ptr_dtor(&obj);
00163 }
00164 
00165 php_stream_filter_status_t userfilter_filter(
00166                      php_stream *stream,
00167                      php_stream_filter *thisfilter,
00168                      php_stream_bucket_brigade *buckets_in,
00169                      php_stream_bucket_brigade *buckets_out,
00170                      size_t *bytes_consumed,
00171                      int flags
00172                      TSRMLS_DC)
00173 {
00174        int ret = PSFS_ERR_FATAL;
00175        zval *obj = (zval*)thisfilter->abstract;
00176        zval func_name;
00177        zval *retval = NULL;
00178        zval **args[4];
00179        zval *zclosing, *zconsumed, *zin, *zout, *zstream;
00180        zval zpropname;
00181        int call_result;
00182 
00183        if (FAILURE == zend_hash_find(Z_OBJPROP_P(obj), "stream", sizeof("stream"), (void**)&zstream)) {
00184               /* Give the userfilter class a hook back to the stream */
00185               ALLOC_INIT_ZVAL(zstream);
00186               php_stream_to_zval(stream, zstream);
00187               zval_copy_ctor(zstream);
00188               add_property_zval(obj, "stream", zstream);
00189               /* add_property_zval increments the refcount which is unwanted here */
00190               zval_ptr_dtor(&zstream);
00191        }
00192 
00193        ZVAL_STRINGL(&func_name, "filter", sizeof("filter")-1, 0);
00194 
00195        /* Setup calling arguments */
00196        ALLOC_INIT_ZVAL(zin);
00197        ZEND_REGISTER_RESOURCE(zin, buckets_in, le_bucket_brigade);
00198        args[0] = &zin;
00199 
00200        ALLOC_INIT_ZVAL(zout);
00201        ZEND_REGISTER_RESOURCE(zout, buckets_out, le_bucket_brigade);
00202        args[1] = &zout;
00203 
00204        ALLOC_INIT_ZVAL(zconsumed);
00205        if (bytes_consumed) {
00206               ZVAL_LONG(zconsumed, *bytes_consumed);
00207        } else {
00208               ZVAL_NULL(zconsumed);
00209        }
00210        args[2] = &zconsumed;
00211 
00212        ALLOC_INIT_ZVAL(zclosing);
00213        ZVAL_BOOL(zclosing, flags & PSFS_FLAG_FLUSH_CLOSE);
00214        args[3] = &zclosing;
00215 
00216        call_result = call_user_function_ex(NULL,
00217                      &obj,
00218                      &func_name,
00219                      &retval,
00220                      4, args,
00221                      0, NULL TSRMLS_CC);
00222 
00223        if (call_result == SUCCESS && retval != NULL) {
00224               convert_to_long(retval);
00225               ret = Z_LVAL_P(retval);
00226        } else if (call_result == FAILURE) {
00227               php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to call filter function");
00228        }
00229 
00230        if (bytes_consumed) {
00231               *bytes_consumed = Z_LVAL_P(zconsumed);
00232        }
00233 
00234        if (retval) {
00235               zval_ptr_dtor(&retval);
00236        }
00237 
00238        if (buckets_in->head) {
00239               php_stream_bucket *bucket = buckets_in->head;
00240 
00241               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unprocessed filter buckets remaining on input brigade");
00242               while ((bucket = buckets_in->head)) {
00243                      /* Remove unconsumed buckets from the brigade */
00244                      php_stream_bucket_unlink(bucket TSRMLS_CC);
00245                      php_stream_bucket_delref(bucket TSRMLS_CC);
00246               }
00247        }
00248        if (ret != PSFS_PASS_ON) {
00249               php_stream_bucket *bucket = buckets_out->head;
00250               while (bucket != NULL) {
00251                      php_stream_bucket_unlink(bucket TSRMLS_CC);
00252                      php_stream_bucket_delref(bucket TSRMLS_CC);
00253                      bucket = buckets_out->head;
00254               }
00255        }
00256 
00257        /* filter resources are cleaned up by the stream destructor,
00258         * keeping a reference to the stream resource here would prevent it
00259         * from being destroyed properly */
00260        INIT_ZVAL(zpropname);
00261        ZVAL_STRINGL(&zpropname, "stream", sizeof("stream")-1, 0);
00262        Z_OBJ_HANDLER_P(obj, unset_property)(obj, &zpropname TSRMLS_CC);
00263 
00264        zval_ptr_dtor(&zclosing);
00265        zval_ptr_dtor(&zconsumed);
00266        zval_ptr_dtor(&zout);
00267        zval_ptr_dtor(&zin);
00268 
00269        return ret;
00270 }
00271 
00272 static php_stream_filter_ops userfilter_ops = {
00273        userfilter_filter,
00274        userfilter_dtor,
00275        "user-filter"
00276 };
00277 
00278 static php_stream_filter *user_filter_factory_create(const char *filtername,
00279               zval *filterparams, int persistent TSRMLS_DC)
00280 {
00281        struct php_user_filter_data *fdat = NULL;
00282        php_stream_filter *filter;
00283        zval *obj, *zfilter;
00284        zval func_name;
00285        zval *retval = NULL;
00286        int len;
00287        
00288        /* some sanity checks */
00289        if (persistent) {
00290               php_error_docref(NULL TSRMLS_CC, E_WARNING,
00291                             "cannot use a user-space filter with a persistent stream");
00292               return NULL;
00293        }
00294 
00295        len = strlen(filtername);
00296 
00297        /* determine the classname/class entry */
00298        if (FAILURE == zend_hash_find(BG(user_filter_map), (char*)filtername, len + 1, (void**)&fdat)) {
00299               char *period;
00300 
00301               /* Userspace Filters using ambiguous wildcards could cause problems.
00302            i.e.: myfilter.foo.bar will always call into myfilter.foo.*
00303                  never seeing myfilter.* 
00304            TODO: Allow failed userfilter creations to continue
00305                  scanning through the list */
00306               if ((period = strrchr(filtername, '.'))) {
00307                      char *wildcard = emalloc(len + 3);
00308 
00309                      /* Search for wildcard matches instead */
00310                      memcpy(wildcard, filtername, len + 1); /* copy \0 */
00311                      period = wildcard + (period - filtername);
00312                      while (period) {
00313                             *period = '\0';
00314                             strncat(wildcard, ".*", 2);
00315                             if (SUCCESS == zend_hash_find(BG(user_filter_map), wildcard, strlen(wildcard) + 1, (void**)&fdat)) {
00316                                    period = NULL;
00317                             } else {
00318                                    *period = '\0';
00319                                    period = strrchr(wildcard, '.');
00320                             }
00321                      }
00322                      efree(wildcard);
00323               }
00324               if (fdat == NULL) {
00325                      php_error_docref(NULL TSRMLS_CC, E_WARNING,
00326                                    "Err, filter \"%s\" is not in the user-filter map, but somehow the user-filter-factory was invoked for it!?", filtername);
00327                      return NULL;
00328               }
00329        }
00330 
00331        /* bind the classname to the actual class */
00332        if (fdat->ce == NULL) {
00333               if (FAILURE == zend_lookup_class(fdat->classname, strlen(fdat->classname),
00334                                    (zend_class_entry ***)&fdat->ce TSRMLS_CC)) {
00335                      php_error_docref(NULL TSRMLS_CC, E_WARNING,
00336                                    "user-filter \"%s\" requires class \"%s\", but that class is not defined",
00337                                    filtername, fdat->classname);
00338                      return NULL;
00339               }
00340               fdat->ce = *(zend_class_entry**)fdat->ce;
00341 
00342        }
00343 
00344        filter = php_stream_filter_alloc(&userfilter_ops, NULL, 0);
00345        if (filter == NULL) {
00346               return NULL;
00347        }
00348 
00349        /* create the object */
00350        ALLOC_ZVAL(obj);
00351        object_init_ex(obj, fdat->ce);
00352        Z_SET_REFCOUNT_P(obj, 1);
00353        Z_SET_ISREF_P(obj);
00354 
00355        /* filtername */
00356        add_property_string(obj, "filtername", (char*)filtername, 1);
00357        
00358        /* and the parameters, if any */
00359        if (filterparams) {
00360               add_property_zval(obj, "params", filterparams);
00361        } else {
00362               add_property_null(obj, "params");
00363        }
00364 
00365        /* invoke the constructor */
00366        ZVAL_STRINGL(&func_name, "oncreate", sizeof("oncreate")-1, 0);
00367 
00368        call_user_function_ex(NULL,
00369                      &obj,
00370                      &func_name,
00371                      &retval,
00372                      0, NULL,
00373                      0, NULL TSRMLS_CC);
00374 
00375        if (retval) {
00376               if (Z_TYPE_P(retval) == IS_BOOL && Z_LVAL_P(retval) == 0) {
00377                      /* User reported filter creation error "return false;" */
00378                      zval_ptr_dtor(&retval);
00379 
00380                      /* Kill the filter (safely) */
00381                      filter->abstract = NULL;
00382                      php_stream_filter_free(filter TSRMLS_CC);
00383 
00384                      /* Kill the object */
00385                      zval_ptr_dtor(&obj);
00386 
00387                      /* Report failure to filter_alloc */
00388                      return NULL;
00389               }                    
00390               zval_ptr_dtor(&retval);
00391        }
00392 
00393        /* set the filter property, this will be used during cleanup */
00394        ALLOC_INIT_ZVAL(zfilter);
00395        ZEND_REGISTER_RESOURCE(zfilter, filter, le_userfilters);
00396        filter->abstract = obj;
00397        add_property_zval(obj, "filter", zfilter);
00398        /* add_property_zval increments the refcount which is unwanted here */
00399        zval_ptr_dtor(&zfilter);
00400 
00401        return filter;
00402 }
00403 
00404 static php_stream_filter_factory user_filter_factory = {
00405        user_filter_factory_create
00406 };
00407 
00408 static void filter_item_dtor(struct php_user_filter_data *fdat)
00409 {
00410 }
00411 
00412 /* {{{ proto object stream_bucket_make_writeable(resource brigade)
00413    Return a bucket object from the brigade for operating on */
00414 PHP_FUNCTION(stream_bucket_make_writeable)
00415 {
00416        zval *zbrigade, *zbucket;
00417        php_stream_bucket_brigade *brigade;
00418        php_stream_bucket *bucket;
00419 
00420        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zbrigade) == FAILURE) {
00421               RETURN_FALSE;
00422        }
00423 
00424        ZEND_FETCH_RESOURCE(brigade, php_stream_bucket_brigade *, &zbrigade, -1, PHP_STREAM_BRIGADE_RES_NAME, le_bucket_brigade); 
00425 
00426        ZVAL_NULL(return_value);
00427 
00428        if (brigade->head && (bucket = php_stream_bucket_make_writeable(brigade->head TSRMLS_CC))) {
00429               ALLOC_INIT_ZVAL(zbucket);
00430               ZEND_REGISTER_RESOURCE(zbucket, bucket, le_bucket);
00431               object_init(return_value);
00432               add_property_zval(return_value, "bucket", zbucket);
00433               /* add_property_zval increments the refcount which is unwanted here */
00434               zval_ptr_dtor(&zbucket);
00435               add_property_stringl(return_value, "data", bucket->buf, bucket->buflen, 1);
00436               add_property_long(return_value, "datalen", bucket->buflen);
00437        }
00438 }
00439 /* }}} */
00440 
00441 /* {{{ php_stream_bucket_attach */
00442 static void php_stream_bucket_attach(int append, INTERNAL_FUNCTION_PARAMETERS)
00443 {
00444        zval *zbrigade, *zobject;
00445        zval **pzbucket, **pzdata;
00446        php_stream_bucket_brigade *brigade;
00447        php_stream_bucket *bucket;
00448 
00449        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zo", &zbrigade, &zobject) == FAILURE) {
00450               RETURN_FALSE;
00451        }
00452 
00453        if (FAILURE == zend_hash_find(Z_OBJPROP_P(zobject), "bucket", 7, (void**)&pzbucket)) {
00454               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Object has no bucket property");
00455               RETURN_FALSE;
00456        }
00457 
00458        ZEND_FETCH_RESOURCE(brigade, php_stream_bucket_brigade *, &zbrigade, -1, PHP_STREAM_BRIGADE_RES_NAME, le_bucket_brigade);
00459        ZEND_FETCH_RESOURCE(bucket, php_stream_bucket *, pzbucket, -1, PHP_STREAM_BUCKET_RES_NAME, le_bucket);
00460 
00461        if (SUCCESS == zend_hash_find(Z_OBJPROP_P(zobject), "data", 5, (void**)&pzdata) && (*pzdata)->type == IS_STRING) {
00462               if (!bucket->own_buf) {
00463                      bucket = php_stream_bucket_make_writeable(bucket TSRMLS_CC);
00464               }
00465               if ((int)bucket->buflen != Z_STRLEN_PP(pzdata)) {
00466                      bucket->buf = perealloc(bucket->buf, Z_STRLEN_PP(pzdata), bucket->is_persistent);
00467                      bucket->buflen = Z_STRLEN_PP(pzdata);
00468               }
00469               memcpy(bucket->buf, Z_STRVAL_PP(pzdata), bucket->buflen);
00470        }
00471 
00472        if (append) {
00473               php_stream_bucket_append(brigade, bucket TSRMLS_CC);
00474        } else {
00475               php_stream_bucket_prepend(brigade, bucket TSRMLS_CC);
00476        }
00477        /* This is a hack necessary to accomodate situations where bucket is appended to the stream
00478         * multiple times. See bug35916.phpt for reference.
00479         */
00480        if (bucket->refcount == 1) {
00481               bucket->refcount++;
00482        }
00483 }
00484 /* }}} */
00485 
00486 /* {{{ proto void stream_bucket_prepend(resource brigade, resource bucket)
00487    Prepend bucket to brigade */
00488 PHP_FUNCTION(stream_bucket_prepend)
00489 {
00490        php_stream_bucket_attach(0, INTERNAL_FUNCTION_PARAM_PASSTHRU);
00491 }
00492 /* }}} */
00493 
00494 /* {{{ proto void stream_bucket_append(resource brigade, resource bucket)
00495    Append bucket to brigade */
00496 PHP_FUNCTION(stream_bucket_append)
00497 {
00498        php_stream_bucket_attach(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
00499 }
00500 /* }}} */
00501 
00502 /* {{{ proto resource stream_bucket_new(resource stream, string buffer)
00503    Create a new bucket for use on the current stream */
00504 PHP_FUNCTION(stream_bucket_new)
00505 {
00506        zval *zstream, *zbucket;
00507        php_stream *stream;
00508        char *buffer;
00509        char *pbuffer;
00510        int buffer_len;
00511        php_stream_bucket *bucket;
00512 
00513        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs", &zstream, &buffer, &buffer_len) == FAILURE) {
00514               RETURN_FALSE;
00515        }
00516 
00517        php_stream_from_zval(stream, &zstream);
00518 
00519        if (!(pbuffer = pemalloc(buffer_len, php_stream_is_persistent(stream)))) {
00520               RETURN_FALSE;
00521        }
00522 
00523        memcpy(pbuffer, buffer, buffer_len);
00524 
00525        bucket = php_stream_bucket_new(stream, pbuffer, buffer_len, 1, php_stream_is_persistent(stream) TSRMLS_CC);
00526        
00527        if (bucket == NULL) {
00528               RETURN_FALSE;
00529        }
00530 
00531        ALLOC_INIT_ZVAL(zbucket);
00532        ZEND_REGISTER_RESOURCE(zbucket, bucket, le_bucket);
00533        object_init(return_value);
00534        add_property_zval(return_value, "bucket", zbucket);
00535        /* add_property_zval increments the refcount which is unwanted here */
00536        zval_ptr_dtor(&zbucket);
00537        add_property_stringl(return_value, "data", bucket->buf, bucket->buflen, 1);
00538        add_property_long(return_value, "datalen", bucket->buflen);
00539 }
00540 /* }}} */
00541 
00542 /* {{{ proto array stream_get_filters(void)
00543    Returns a list of registered filters */
00544 PHP_FUNCTION(stream_get_filters)
00545 {
00546        char *filter_name;
00547        int key_flags, filter_name_len = 0;
00548        HashTable *filters_hash;
00549        ulong num_key;
00550 
00551        if (zend_parse_parameters_none() == FAILURE) {
00552               return;
00553        }
00554 
00555        array_init(return_value);
00556 
00557        filters_hash = php_get_stream_filters_hash();
00558 
00559        if (filters_hash) {
00560               for(zend_hash_internal_pointer_reset(filters_hash);
00561                      (key_flags = zend_hash_get_current_key_ex(filters_hash, &filter_name, &filter_name_len, &num_key, 0, NULL)) != HASH_KEY_NON_EXISTANT;
00562                      zend_hash_move_forward(filters_hash))
00563                             if (key_flags == HASH_KEY_IS_STRING) {
00564                                    add_next_index_stringl(return_value, filter_name, filter_name_len - 1, 1);
00565                             }
00566        }
00567        /* It's okay to return an empty array if no filters are registered */
00568 }
00569 /* }}} */     
00570 
00571 /* {{{ proto bool stream_filter_register(string filtername, string classname)
00572    Registers a custom filter handler class */
00573 PHP_FUNCTION(stream_filter_register)
00574 {
00575        char *filtername, *classname;
00576        int filtername_len, classname_len;
00577        struct php_user_filter_data *fdat;
00578        
00579        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &filtername, &filtername_len,
00580                             &classname, &classname_len) == FAILURE) {
00581               RETURN_FALSE;
00582        }
00583 
00584        RETVAL_FALSE;
00585 
00586        if (!filtername_len) {
00587               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter name cannot be empty");
00588               return;
00589        }
00590 
00591        if (!classname_len) {
00592               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Class name cannot be empty");
00593               return;
00594        }
00595 
00596        if (!BG(user_filter_map)) {
00597               BG(user_filter_map) = (HashTable*) emalloc(sizeof(HashTable));
00598               zend_hash_init(BG(user_filter_map), 5, NULL, (dtor_func_t) filter_item_dtor, 0);
00599        }
00600 
00601        fdat = ecalloc(1, sizeof(struct php_user_filter_data) + classname_len);
00602        memcpy(fdat->classname, classname, classname_len);
00603 
00604        if (zend_hash_add(BG(user_filter_map), filtername, filtername_len + 1, (void*)fdat,
00605                             sizeof(*fdat) + classname_len, NULL) == SUCCESS &&
00606                      php_stream_filter_register_factory_volatile(filtername, &user_filter_factory TSRMLS_CC) == SUCCESS) {
00607               RETVAL_TRUE;
00608        }
00609 
00610        efree(fdat);
00611 }
00612 /* }}} */
00613 
00614 
00615 /*
00616  * Local variables:
00617  * tab-width: 4
00618  * c-basic-offset: 4
00619  * End:
00620  * vim600: sw=4 ts=4 fdm=marker
00621  * vim<600: sw=4 ts=4
00622  */