From 833a2295d143c67295dd94e20a56883b4f2d0787 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 17 Jul 2008 09:53:42 +0000 Subject: [PATCH] Support for closures --- ext/curl/tests/curl_006.phpt | 35 +++++++ ext/filter/tests/callback_closure.phpt | 14 +++ ext/mysqli/mysqli_api.c | 3 +- ...qli_set_local_infile_handler_closures.phpt | 65 +++++++++++++ ext/pcntl/tests/signal_closure_handler.phpt | 25 +++++ ext/session/tests/save_handler_closures.inc | 9 ++ .../session_set_save_handler_closures.phpt | 95 +++++++++++++++++++ ext/sqlite/tests/sqlite_closures_001.phpt | 54 +++++++++++ ext/sqlite/tests/sqlite_closures_002.phpt | 52 ++++++++++ ext/standard/basic_functions.c | 6 +- .../tests/assert/assert_closures.phpt | 16 ++++ .../tests/general_functions/closures_001.phpt | 11 +++ .../tests/general_functions/closures_002.phpt | 25 +++++ .../general_functions/ob_start_closures.phpt | 39 ++++++++ ext/xml/tests/xml_closures_001.phpt | 47 +++++++++ ext/xml/xml.c | 2 +- main/output.c | 13 ++- 17 files changed, 506 insertions(+), 5 deletions(-) create mode 100755 ext/curl/tests/curl_006.phpt create mode 100755 ext/filter/tests/callback_closure.phpt create mode 100755 ext/mysqli/tests/mysqli_set_local_infile_handler_closures.phpt create mode 100755 ext/pcntl/tests/signal_closure_handler.phpt create mode 100755 ext/session/tests/save_handler_closures.inc create mode 100755 ext/session/tests/session_set_save_handler_closures.phpt create mode 100755 ext/sqlite/tests/sqlite_closures_001.phpt create mode 100755 ext/sqlite/tests/sqlite_closures_002.phpt create mode 100755 ext/standard/tests/assert/assert_closures.phpt create mode 100755 ext/standard/tests/general_functions/closures_001.phpt create mode 100755 ext/standard/tests/general_functions/closures_002.phpt create mode 100755 ext/standard/tests/general_functions/ob_start_closures.phpt create mode 100755 ext/xml/tests/xml_closures_001.phpt diff --git a/ext/curl/tests/curl_006.phpt b/ext/curl/tests/curl_006.phpt new file mode 100755 index 0000000000..c4a6faa413 --- /dev/null +++ b/ext/curl/tests/curl_006.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test curl_opt() function with CURLOPT_WRITEFUNCTION paremter set to a closure +--SKIPIF-- + +--FILE-- +); ***' . "\n"; + + $url = "{$host}/get.php?test=get"; + $ch = curl_init(); + + ob_start(); // start output buffering + curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use + curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($ch, $data) { + echo 'Data: '.$data; + return strlen ($data); + }); + + curl_exec($ch); + curl_close($ch); +?> +===DONE=== +--EXPECTF-- +*** Testing curl_setopt($ch, CURLOPT_WRITEFUNCTION, ); *** +Data: Hello World! +Hello World!===DONE=== diff --git a/ext/filter/tests/callback_closure.phpt b/ext/filter/tests/callback_closure.phpt new file mode 100755 index 0000000000..e27a31b37e --- /dev/null +++ b/ext/filter/tests/callback_closure.phpt @@ -0,0 +1,14 @@ +--TEST-- +callback function is a closure +--SKIPIF-- + +--FILE-- + $callback))); +?> +--EXPECT-- +string(4) "test" diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index a8c376cf46..b95584b554 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -1420,6 +1420,7 @@ PHP_FUNCTION(mysqli_set_local_infile_handler) efree(callback_name); RETURN_FALSE; } + efree(callback_name); /* save callback function */ if (!mysql->li_read) { @@ -1427,7 +1428,7 @@ PHP_FUNCTION(mysqli_set_local_infile_handler) } else { zval_dtor(mysql->li_read); } - ZVAL_STRING(mysql->li_read, callback_name, 0); + ZVAL_ZVAL(mysql->li_read, callback_func, 1, 0); RETURN_TRUE; } diff --git a/ext/mysqli/tests/mysqli_set_local_infile_handler_closures.phpt b/ext/mysqli/tests/mysqli_set_local_infile_handler_closures.phpt new file mode 100755 index 0000000000..3bdf6bb78b --- /dev/null +++ b/ext/mysqli/tests/mysqli_set_local_infile_handler_closures.phpt @@ -0,0 +1,65 @@ +--TEST-- +mysqli_set_local_infile_handler() - use closures as handler +--SKIPIF-- + +--INI-- +mysqli.allow_local_infile=1 +--FILE-- + 10) + return 0; + + return strlen($buffer); + }; + + $file = create_standard_csv(1); + if (!try_handler(20, $link, $file, $callback_replace_buffer, null)) + printf("[008] Failure\n"); + + mysqli_close($link); + print "done!"; +?> +--EXPECTF-- +Callback set to 'Closure object' +Callback: 0 +Callback: 1 +done! \ No newline at end of file diff --git a/ext/pcntl/tests/signal_closure_handler.phpt b/ext/pcntl/tests/signal_closure_handler.phpt new file mode 100755 index 0000000000..ba9b8f5106 --- /dev/null +++ b/ext/pcntl/tests/signal_closure_handler.phpt @@ -0,0 +1,25 @@ +--TEST-- +Closures as a signal handler +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Start! +Signal handler called! +Done! diff --git a/ext/session/tests/save_handler_closures.inc b/ext/session/tests/save_handler_closures.inc new file mode 100755 index 0000000000..50f33c1098 --- /dev/null +++ b/ext/session/tests/save_handler_closures.inc @@ -0,0 +1,9 @@ + diff --git a/ext/session/tests/session_set_save_handler_closures.phpt b/ext/session/tests/session_set_save_handler_closures.phpt new file mode 100755 index 0000000000..21b2c68737 --- /dev/null +++ b/ext/session/tests/session_set_save_handler_closures.phpt @@ -0,0 +1,95 @@ +--TEST-- +Test session_set_save_handler() function : using closures as callbacks +--INI-- +session.save_path= +session.name=PHPSESSID +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +*** Testing session_set_save_handler() : using closures as callbacks *** + +string(%d) "%s" + +Warning: session_module_name(): Cannot find named PHP session module () in %s on line %d +bool(false) + +Warning: session_module_name(): Cannot find named PHP session module (blah) in %s on line %d +bool(false) + +Warning: session_module_name(): Cannot find named PHP session module (foo) in %s on line %d +bool(false) +Open [%s,PHPSESSID] +Read [%s,%s] +array(3) { + ["Blah"]=> + string(12) "Hello World!" + ["Foo"]=> + bool(false) + ["Guff"]=> + int(1234567890) +} +Write [%s,%s,Blah|s:12:"Hello World!";Foo|b:0;Guff|i:1234567890;] +Close [%s,PHPSESSID] +array(3) { + ["Blah"]=> + string(12) "Hello World!" + ["Foo"]=> + bool(false) + ["Guff"]=> + int(1234567890) +} +Starting session again..! +Open [%s,PHPSESSID] +Read [%s,%s] +array(3) { + ["Blah"]=> + string(12) "Hello World!" + ["Foo"]=> + bool(false) + ["Guff"]=> + int(1234567890) +} +Write [%s,%s,Blah|s:12:"Hello World!";Foo|b:0;Guff|i:1234567890;] +Close [%s,PHPSESSID] diff --git a/ext/sqlite/tests/sqlite_closures_001.phpt b/ext/sqlite/tests/sqlite_closures_001.phpt new file mode 100755 index 0000000000..8a90d1e5e0 --- /dev/null +++ b/ext/sqlite/tests/sqlite_closures_001.phpt @@ -0,0 +1,54 @@ +--TEST-- +sqlite: aggregate functions with closures +--INI-- +sqlite.assoc_case=0 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + string(11) "onetwothree" +} +DONE! diff --git a/ext/sqlite/tests/sqlite_closures_002.phpt b/ext/sqlite/tests/sqlite_closures_002.phpt new file mode 100755 index 0000000000..e880bb1733 --- /dev/null +++ b/ext/sqlite/tests/sqlite_closures_002.phpt @@ -0,0 +1,52 @@ +--TEST-- +sqlite: regular functions with closures +--INI-- +sqlite.assoc_case=0 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + string(7) "one-uno" +} +array(1) { + [0]=> + string(7) "two-dos" +} +array(1) { + [0]=> + string(10) "three-tres" +} +DONE! diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index fdddcb1b63..afa41e7209 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -5392,6 +5392,10 @@ static int user_tick_function_compare(user_tick_function_entry * tick_fe1, user_ zval result; zend_compare_arrays(&result, func1, func2 TSRMLS_CC); ret = (Z_LVAL(result) == 0); + } else if (Z_TYPE_P(func1) == IS_OBJECT && Z_TYPE_P(func2) == IS_OBJECT) { + zval result; + zend_compare_objects(&result, func1, func2 TSRMLS_CC); + ret = (Z_LVAL(result) == 0); } else { ret = 0; } @@ -6044,7 +6048,7 @@ PHP_FUNCTION(register_tick_function) efree(function_name); } - if (Z_TYPE_P(tick_fe.arguments[0]) != IS_ARRAY) { + if (Z_TYPE_P(tick_fe.arguments[0]) != IS_ARRAY && Z_TYPE_P(tick_fe.arguments[0]) != IS_OBJECT) { convert_to_string_ex(&tick_fe.arguments[0]); } diff --git a/ext/standard/tests/assert/assert_closures.phpt b/ext/standard/tests/assert/assert_closures.phpt new file mode 100755 index 0000000000..e01c11ace9 --- /dev/null +++ b/ext/standard/tests/assert/assert_closures.phpt @@ -0,0 +1,16 @@ +--TEST-- +assert() - basic - accept closures as callback. +--INI-- +assert.active = 1 +assert.warning = 1 +assert.bail = 0 +assert.quiet_eval = 0 +--FILE-- + +--EXPECTF-- +Hello World! + +Warning: assert(): Assertion failed in %s on line %d diff --git a/ext/standard/tests/general_functions/closures_001.phpt b/ext/standard/tests/general_functions/closures_001.phpt new file mode 100755 index 0000000000..b4fc898fd2 --- /dev/null +++ b/ext/standard/tests/general_functions/closures_001.phpt @@ -0,0 +1,11 @@ +--TEST-- +register_shutdown_function() & closure +--FILE-- + +--EXPECTF-- +Done +Hello World! diff --git a/ext/standard/tests/general_functions/closures_002.phpt b/ext/standard/tests/general_functions/closures_002.phpt new file mode 100755 index 0000000000..6df389bbb1 --- /dev/null +++ b/ext/standard/tests/general_functions/closures_002.phpt @@ -0,0 +1,25 @@ +--TEST-- +register_tick_function() & closure +--FILE-- + +--EXPECTF-- +Test +%d +%d +bool(true) +%d +Done diff --git a/ext/standard/tests/general_functions/ob_start_closures.phpt b/ext/standard/tests/general_functions/ob_start_closures.phpt new file mode 100755 index 0000000000..ba730961bd --- /dev/null +++ b/ext/standard/tests/general_functions/ob_start_closures.phpt @@ -0,0 +1,39 @@ +--TEST-- +Test ob_start() function : closures as output handlers +--INI-- +output_buffering=0 +--FILE-- + +===DONE=== +--EXPECT-- +*** Testing ob_start() : closures as output handlers *** +Output (1): Output (2): Test +With newlines +Test +With newlines +Output (2): Test +With newlines +===DONE=== diff --git a/ext/xml/tests/xml_closures_001.phpt b/ext/xml/tests/xml_closures_001.phpt new file mode 100755 index 0000000000..5439a2a918 --- /dev/null +++ b/ext/xml/tests/xml_closures_001.phpt @@ -0,0 +1,47 @@ +--TEST-- +XML parser test using closures as callbacks +--SKIPIF-- + +--INI-- +magic_quotes_runtime=0 +--FILE-- +\n"; +}; + +$end_element = function ($xp, $elem) +{ + print "\n"; +}; + +$xp = xml_parser_create(); +xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false); +xml_set_element_handler($xp, $start_element, $end_element); +$fp = fopen("xmltest.xml", "r"); +while ($data = fread($fp, 4096)) { + xml_parse($xp, $data, feof($fp)); +} +xml_parser_free($xp); + +?> +--EXPECT-- + + + + + + + + + + diff --git a/ext/xml/xml.c b/ext/xml/xml.c index b276f8d892..6f8ff34210 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -508,7 +508,7 @@ static void xml_set_handler(zval **handler, zval **data) } /* IS_ARRAY might indicate that we're using array($obj, 'method') syntax */ - if (Z_TYPE_PP(data) != IS_ARRAY) { + if (Z_TYPE_PP(data) != IS_ARRAY && Z_TYPE_PP(data) != IS_OBJECT) { convert_to_string_ex(data); if (Z_STRLEN_PP(data) == 0) { diff --git a/main/output.c b/main/output.c index 295dcfae01..92145afdc8 100644 --- a/main/output.c +++ b/main/output.c @@ -526,8 +526,17 @@ static int php_ob_init(uint initial_size, uint block_size, zval *output_handler, } } } else if (output_handler && output_handler->type == IS_OBJECT) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "No method name given: use ob_start(array($object,'method')) to specify instance $object and the name of a method of class %s to use as output handler", Z_OBJCE_P(output_handler)->name); - result = FAILURE; + /* do we have callable object */ + if (zend_is_callable(output_handler, 0, &handler_name)) { + SEPARATE_ZVAL(&output_handler); + Z_ADDREF_P(output_handler); + result = php_ob_init_named(initial_size, block_size, handler_name, output_handler, chunk_size, erase TSRMLS_CC); + efree(handler_name); + } else { + efree(handler_name); + php_error_docref(NULL TSRMLS_CC, E_ERROR, "No method name given: use ob_start(array($object,'method')) to specify instance $object and the name of a method of class %s to use as output handler", Z_OBJCE_P(output_handler)->name); + result = FAILURE; + } } else { result = php_ob_init_named(initial_size, block_size, OB_DEFAULT_HANDLER_NAME, NULL, chunk_size, erase TSRMLS_CC); } -- 2.40.0