@@ -89,6 +89,10 @@ PHP_MINIT_FUNCTION(user_streams)
8989struct _php_userstream_data {
9090 struct php_user_stream_wrapper * wrapper ;
9191 zend_object * object ;
92+ /* Caches for most frequently used methods */
93+ zend_function * fn_cache_read ;
94+ zend_function * fn_cache_eof ;
95+ zend_function * fn_cache_write ;
9296};
9397typedef struct _php_userstream_data php_userstream_data_t ;
9498
@@ -249,6 +253,38 @@ typedef struct _php_userstream_data php_userstream_data_t;
249253
250254 }}} **/
251255
256+ static zend_function * php_userstream_get_fn (php_userstream_data_t * us , zend_function * * cache , const char * name , size_t len )
257+ {
258+ if (cache && * cache ) {
259+ return * cache ;
260+ }
261+
262+ zend_function * fn = zend_hash_str_find_ptr (& us -> object -> ce -> function_table , name , len );
263+
264+ if (cache ) {
265+ * cache = fn ;
266+ }
267+
268+ return fn ;
269+ }
270+
271+ static zend_result php_userstream_call (php_userstream_data_t * us , zend_function * * cache , const char * name , size_t len , zval * retval , uint32_t param_count , zval * params )
272+ {
273+ zend_function * fn = php_userstream_get_fn (us , cache , name , len );
274+ if (UNEXPECTED (!fn || !(fn -> common .fn_flags & ZEND_ACC_PUBLIC ))) { // TODO: this path should be tested with magic methods via a .phpt file
275+ /* Slow fallback */
276+ zend_string * tmp ;
277+ ALLOCA_FLAG (use_heap );
278+ ZSTR_ALLOCA_INIT (tmp , name , len , use_heap );
279+ zend_result result = zend_call_method_if_exists (us -> object , tmp , retval , param_count , params );
280+ ZSTR_ALLOCA_FREE (tmp , use_heap );
281+ return result ;
282+ } else {
283+ zend_call_known_instance_method (fn , us -> object , retval , param_count , params );
284+ return SUCCESS ;
285+ }
286+ }
287+
252288static zend_object * user_stream_create_object (struct php_user_stream_wrapper * uwrap , php_stream_context * context )
253289{
254290 if (uwrap -> ce -> ce_flags & (ZEND_ACC_INTERFACE |ZEND_ACC_TRAIT |ZEND_ACC_IMPLICIT_ABSTRACT_CLASS |ZEND_ACC_EXPLICIT_ABSTRACT_CLASS )) {
@@ -309,9 +345,9 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *
309345 PG (in_user_include ) = 1 ;
310346 }
311347
312- us = emalloc ( sizeof (* us ));
348+ us = ecalloc ( 1 , sizeof (* us ));
313349 us -> wrapper = uwrap ;
314- /* zend_call_method_if_exists() may unregister the stream wrapper. Hold on to it. */
350+ /* method call may unregister the stream wrapper. Hold on to it. */
315351 GC_ADDREF (us -> wrapper -> resource );
316352
317353 us -> object = user_stream_create_object (uwrap , context );
@@ -325,9 +361,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *
325361 ZVAL_LONG (& args [2 ], options );
326362 ZVAL_NEW_REF (& args [3 ], & EG (uninitialized_zval ));
327363
328- zend_string * func_name = ZSTR_INIT_LITERAL (USERSTREAM_OPEN , false);
329- zend_result call_result = zend_call_method_if_exists (us -> object , func_name , & zretval , 4 , args );
330- zend_string_release_ex (func_name , false);
364+ zend_result call_result = php_userstream_call (us , NULL , ZEND_STRL (USERSTREAM_OPEN ), & zretval , 4 , args );
331365
332366 /* Keep arg3 alive if it has assigned the reference */
333367 zval_ptr_dtor (& args [1 ]);
@@ -401,9 +435,9 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char
401435 }
402436 FG (user_stream_current_filename ) = filename ;
403437
404- us = emalloc ( sizeof (* us ));
438+ us = ecalloc ( 1 , sizeof (* us ));
405439 us -> wrapper = uwrap ;
406- /* zend_call_method_if_exists() may unregister the stream wrapper. Hold on to it. */
440+ /* method call may unregister the stream wrapper. Hold on to it. */
407441 GC_ADDREF (us -> wrapper -> resource );
408442
409443 us -> object = user_stream_create_object (uwrap , context );
@@ -415,9 +449,7 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char
415449 ZVAL_STRING (& args [0 ], filename );
416450 ZVAL_LONG (& args [1 ], options );
417451
418- zend_string * func_name = ZSTR_INIT_LITERAL (USERSTREAM_DIR_OPEN , false);
419- zend_result call_result = zend_call_method_if_exists (us -> object , func_name , & zretval , 2 , args );
420- zend_string_release_ex (func_name , false);
452+ zend_result call_result = php_userstream_call (us , NULL , ZEND_STRL (USERSTREAM_DIR_OPEN ), & zretval , 2 , args );
421453 zval_ptr_dtor (& args [0 ]);
422454
423455 if (UNEXPECTED (call_result == FAILURE )) {
@@ -570,9 +602,7 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_
570602 uint32_t orig_no_fclose = stream -> flags & PHP_STREAM_FLAG_NO_FCLOSE ;
571603 stream -> flags |= PHP_STREAM_FLAG_NO_FCLOSE ;
572604
573- zend_string * func_name = ZSTR_INIT_LITERAL (USERSTREAM_WRITE , false);
574- zend_result call_result = zend_call_method_if_exists (us -> object , func_name , & retval , 1 , args );
575- zend_string_release_ex (func_name , false);
605+ zend_result call_result = php_userstream_call (us , & us -> fn_cache_write , ZEND_STRL (USERSTREAM_WRITE ), & retval , 1 , args );
576606 zval_ptr_dtor (& args [0 ]);
577607
578608 if (UNEXPECTED (call_result == FAILURE )) {
@@ -619,9 +649,7 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count
619649 stream -> flags |= PHP_STREAM_FLAG_NO_FCLOSE ;
620650
621651 ZVAL_LONG (& args [0 ], count );
622- zend_string * func_name = ZSTR_INIT_LITERAL (USERSTREAM_READ , false);
623- zend_result call_result = zend_call_method_if_exists (us -> object , func_name , & retval , 1 , args );
624- zend_string_release_ex (func_name , false);
652+ zend_result call_result = php_userstream_call (us , & us -> fn_cache_read , ZEND_STRL (USERSTREAM_READ ), & retval , 1 , args );
625653
626654 if (UNEXPECTED (Z_ISUNDEF (retval ))) {
627655 goto err ;
@@ -656,11 +684,7 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count
656684 ZVAL_UNDEF (& retval );
657685
658686 /* since the user stream has no way of setting the eof flag directly, we need to ask it if we hit eof */
659-
660- func_name = ZSTR_INIT_LITERAL (USERSTREAM_EOF , false);
661- call_result = zend_call_method_if_exists (us -> object , func_name , & retval , 0 , NULL );
662- zend_string_release_ex (func_name , false);
663-
687+ call_result = php_userstream_call (us , & us -> fn_cache_eof , ZEND_STRL (USERSTREAM_EOF ), & retval , 0 , NULL );
664688 if (UNEXPECTED (call_result == FAILURE )) {
665689 php_error_docref (NULL , E_WARNING ,
666690 "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF" ,
@@ -696,9 +720,7 @@ static int php_userstreamop_close(php_stream *stream, int close_handle)
696720
697721 ZEND_ASSERT (us != NULL );
698722
699- zend_string * func_name = ZSTR_INIT_LITERAL (USERSTREAM_CLOSE , false);
700- zend_call_method_if_exists (us -> object , func_name , & retval , 0 , NULL );
701- zend_string_release_ex (func_name , false);
723+ php_userstream_call (us , NULL , ZEND_STRL (USERSTREAM_CLOSE ), & retval , 0 , NULL );
702724
703725 zval_ptr_dtor (& retval );
704726
@@ -716,11 +738,9 @@ static int php_userstreamop_flush(php_stream *stream)
716738
717739 ZEND_ASSERT (us != NULL );
718740
719- zend_string * func_name = ZSTR_INIT_LITERAL (USERSTREAM_FLUSH , false);
720- zend_result call_result = zend_call_method_if_exists (us -> object , func_name , & retval , 0 , NULL );
721- zend_string_release_ex (func_name , false);
741+ php_userstream_call (us , NULL , ZEND_STRL (USERSTREAM_FLUSH ), & retval , 0 , NULL );
722742
723- int ret = call_result == SUCCESS && Z_TYPE (retval ) != IS_UNDEF && zend_is_true (& retval ) ? 0 : -1 ;
743+ int ret = Z_TYPE (retval ) != IS_UNDEF && zend_is_true (& retval ) ? 0 : -1 ;
724744
725745 zval_ptr_dtor (& retval );
726746
@@ -742,10 +762,7 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int when
742762 uint32_t orig_no_fclose = stream -> flags & PHP_STREAM_FLAG_NO_FCLOSE ;
743763 stream -> flags |= PHP_STREAM_FLAG_NO_FCLOSE ;
744764
745- zend_string * func_name = ZSTR_INIT_LITERAL (USERSTREAM_SEEK , false);
746- zend_result call_result = zend_call_method_if_exists (us -> object , func_name , & retval , 2 , args );
747- zend_string_release_ex (func_name , false);
748-
765+ zend_result call_result = php_userstream_call (us , NULL , ZEND_STRL (USERSTREAM_SEEK ), & retval , 2 , args );
749766 if (call_result == FAILURE ) {
750767 /* stream_seek is not implemented, so disable seeks for this stream */
751768 stream -> flags |= PHP_STREAM_FLAG_NO_SEEK ;
@@ -769,9 +786,7 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int when
769786 }
770787
771788 /* now determine where we are */
772- func_name = ZSTR_INIT_LITERAL (USERSTREAM_TELL , false);
773- call_result = zend_call_method_if_exists (us -> object , func_name , & retval , 0 , NULL );
774- zend_string_release_ex (func_name , false);
789+ call_result = php_userstream_call (us , NULL , ZEND_STRL (USERSTREAM_TELL ), & retval , 0 , NULL );
775790
776791 if (call_result == SUCCESS && Z_TYPE (retval ) == IS_LONG ) {
777792 * newoffs = Z_LVAL (retval );
@@ -836,10 +851,7 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb)
836851 php_userstream_data_t * us = (php_userstream_data_t * )stream -> abstract ;
837852 int ret = -1 ;
838853
839- zend_string * func_name = ZSTR_INIT_LITERAL (USERSTREAM_STAT , false);
840- zend_result call_result = zend_call_method_if_exists (us -> object , func_name , & retval , 0 , NULL );
841- zend_string_release_ex (func_name , false);
842-
854+ zend_result call_result = php_userstream_call (us , NULL , ZEND_STRL (USERSTREAM_STAT ), & retval , 0 , NULL );
843855 if (UNEXPECTED (call_result == FAILURE )) {
844856 php_error_docref (NULL , E_WARNING , "%s::" USERSTREAM_STAT " is not implemented!" ,
845857 ZSTR_VAL (us -> wrapper -> ce -> name ));
@@ -860,13 +872,11 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb)
860872 return ret ;
861873}
862874
863- static int user_stream_set_check_liveliness (const php_userstream_data_t * us )
875+ static int user_stream_set_check_liveliness (php_userstream_data_t * us )
864876{
865877 zval retval ;
866878
867- zend_string * func_name = ZSTR_INIT_LITERAL (USERSTREAM_EOF , false);
868- zend_result call_result = zend_call_method_if_exists (us -> object , func_name , & retval , 0 , NULL );
869- zend_string_release_ex (func_name , false);
879+ zend_result call_result = php_userstream_call (us , NULL , ZEND_STRL (USERSTREAM_EOF ), & retval , 0 , NULL );
870880
871881 if (UNEXPECTED (call_result == FAILURE )) {
872882 php_error_docref (NULL , E_WARNING ,
@@ -888,7 +898,7 @@ static int user_stream_set_check_liveliness(const php_userstream_data_t *us)
888898 }
889899}
890900
891- static int user_stream_set_locking (const php_userstream_data_t * us , int value )
901+ static int user_stream_set_locking (php_userstream_data_t * us , int value )
892902{
893903 zval retval ;
894904 zval zlock ;
@@ -914,9 +924,7 @@ static int user_stream_set_locking(const php_userstream_data_t *us, int value)
914924 ZVAL_LONG (& zlock , lock );
915925
916926 /* TODO wouldblock */
917- zend_string * func_name = ZSTR_INIT_LITERAL (USERSTREAM_LOCK , false);
918- zend_result call_result = zend_call_method_if_exists (us -> object , func_name , & retval , 1 , & zlock );
919- zend_string_release_ex (func_name , false);
927+ zend_result call_result = php_userstream_call (us , NULL , ZEND_STRL (USERSTREAM_LOCK ), & retval , 1 , & zlock );
920928
921929 if (UNEXPECTED (call_result == FAILURE )) {
922930 if (value == 0 ) {
@@ -991,7 +999,7 @@ static int user_stream_set_truncation(const php_userstream_data_t *us, int value
991999 }
9921000}
9931001
994- static int user_stream_set_option (const php_userstream_data_t * us , int option , int value , void * ptrparam )
1002+ static int user_stream_set_option (php_userstream_data_t * us , int option , int value , void * ptrparam )
9951003{
9961004 zval args [3 ];
9971005 ZVAL_LONG (& args [0 ], option );
@@ -1011,9 +1019,7 @@ static int user_stream_set_option(const php_userstream_data_t *us, int option, i
10111019 }
10121020
10131021 zval retval ;
1014- zend_string * func_name = ZSTR_INIT_LITERAL (USERSTREAM_SET_OPTION , false);
1015- zend_result call_result = zend_call_method_if_exists (us -> object , func_name , & retval , 3 , args );
1016- zend_string_release_ex (func_name , false);
1022+ zend_result call_result = php_userstream_call (us , NULL , ZEND_STRL (USERSTREAM_SET_OPTION ), & retval , 3 , args );
10171023
10181024 if (UNEXPECTED (call_result == FAILURE )) {
10191025 php_error_docref (NULL , E_WARNING ,
@@ -1324,9 +1330,7 @@ static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t co
13241330 return -1 ;
13251331 }
13261332
1327- zend_string * func_name = ZSTR_INIT_LITERAL (USERSTREAM_DIR_READ , false);
1328- zend_result call_result = zend_call_method_if_exists (us -> object , func_name , & retval , 0 , NULL );
1329- zend_string_release_ex (func_name , false);
1333+ zend_result call_result = php_userstream_call (us , NULL , ZEND_STRL (USERSTREAM_DIR_READ ), & retval , 0 , NULL );
13301334
13311335 if (UNEXPECTED (call_result == FAILURE )) {
13321336 php_error_docref (NULL , E_WARNING , "%s::" USERSTREAM_DIR_READ " is not implemented!" ,
@@ -1360,9 +1364,7 @@ static int php_userstreamop_closedir(php_stream *stream, int close_handle)
13601364
13611365 ZEND_ASSERT (us != NULL );
13621366
1363- zend_string * func_name = ZSTR_INIT_LITERAL (USERSTREAM_DIR_CLOSE , false);
1364- zend_call_method_if_exists (us -> object , func_name , & retval , 0 , NULL );
1365- zend_string_release_ex (func_name , false);
1367+ php_userstream_call (us , NULL , ZEND_STRL (USERSTREAM_DIR_CLOSE ), & retval , 0 , NULL );
13661368
13671369 zval_ptr_dtor (& retval );
13681370 OBJ_RELEASE (us -> object );
@@ -1376,9 +1378,7 @@ static int php_userstreamop_rewinddir(php_stream *stream, zend_off_t offset, int
13761378 zval retval ;
13771379 php_userstream_data_t * us = (php_userstream_data_t * )stream -> abstract ;
13781380
1379- zend_string * func_name = ZSTR_INIT_LITERAL (USERSTREAM_DIR_REWIND , false);
1380- zend_call_method_if_exists (us -> object , func_name , & retval , 0 , NULL );
1381- zend_string_release_ex (func_name , false);
1381+ php_userstream_call (us , NULL , ZEND_STRL (USERSTREAM_DIR_REWIND ), & retval , 0 , NULL );
13821382
13831383 zval_ptr_dtor (& retval );
13841384
@@ -1408,9 +1408,7 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr)
14081408 uint32_t orig_no_fclose = stream -> flags & PHP_STREAM_FLAG_NO_FCLOSE ;
14091409 stream -> flags |= PHP_STREAM_FLAG_NO_FCLOSE ;
14101410
1411- zend_string * func_name = ZSTR_INIT_LITERAL (USERSTREAM_CAST , false);
1412- zend_result call_result = zend_call_method_if_exists (us -> object , func_name , & retval , 1 , args );
1413- zend_string_release_ex (func_name , false);
1411+ zend_result call_result = php_userstream_call (us , NULL , ZEND_STRL (USERSTREAM_CAST ), & retval , 1 , args );
14141412
14151413 if (UNEXPECTED (call_result == FAILURE )) {
14161414 if (report_errors ) {
0 commit comments