From f907a009f9d4c6a72ad756f57b9c3ebf0eeb8666 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Fri, 11 Aug 2023 20:39:24 +0100 Subject: [PATCH 01/47] Align highlight_string|file with HTML standard and modern browsers Closes GH-11913 --- UPGRADING | 4 ++++ Zend/tests/bug35655.phpt | 11 ++++++---- Zend/tests/bug42767.phpt | 5 +---- Zend/tests/bug71086.phpt | 6 ++--- Zend/tests/nowdoc_013.phpt | 10 +++++---- Zend/tests/nowdoc_014.phpt | 9 ++++---- Zend/zend_highlight.c | 16 ++++---------- .../tests/cache_list/frontcontroller15.phpt | 5 +---- .../tests/cache_list/frontcontroller3.phpt | 5 +---- .../tests/cache_list/frontcontroller9.phpt | 5 +---- ext/phar/tests/frontcontroller15.phpt | 5 +---- ext/phar/tests/frontcontroller3.phpt | 5 +---- ext/phar/tests/frontcontroller9.phpt | 5 +---- .../tests/tar/frontcontroller15.phar.phpt | 5 +---- ext/phar/tests/tar/frontcontroller3.phar.phpt | 5 +---- ext/phar/tests/tar/frontcontroller9.phar.phpt | 5 +---- .../tests/zip/frontcontroller15.phar.phpt | 5 +---- ext/phar/tests/zip/frontcontroller3.phar.phpt | 5 +---- ext/phar/tests/zip/frontcontroller9.phar.phpt | 5 +---- .../general_functions/highlight_heredoc.phpt | 12 +++++++--- .../tests/strings/highlight_file.phpt | 22 +++++++++---------- .../tests/strings/show_source_basic.phpt | 20 +++++++++++++---- .../tests/strings/show_source_variation1.phpt | 20 +++++++++++++---- .../tests/strings/show_source_variation2.phpt | 20 +++++++++++++---- sapi/cgi/tests/008.phpt | 20 +++++++++++++---- sapi/cli/tests/014.phpt | 20 +++++++++++++---- tests/strings/004.phpt | 8 ++----- tests/strings/bug26703.phpt | 5 +---- 28 files changed, 143 insertions(+), 125 deletions(-) diff --git a/UPGRADING b/UPGRADING index 1aa82831d5240..75536b00f4a54 100644 --- a/UPGRADING +++ b/UPGRADING @@ -221,6 +221,10 @@ PHP 8.3 UPGRADE NOTES only paths starting with `..` were disallowed. This could easily be circumvented by prepending `./` to the path. . User exception handlers now catch exceptions during shutdown. + . The resultant HTML of highlight_string and highlight_file has changed. + Whitespace between outer HTML tags is removed. Newlines and spaces + are no longer converted to HTML entities. The whole HTML is now wrapped in +
 tag. The outer  has been merged with .
 
 - Calendar
   . easter_date() now supports years from 1970 to 2,000,000,000 on 64-bit systems,
diff --git a/Zend/tests/bug35655.phpt b/Zend/tests/bug35655.phpt
index 47e7a7bd7e2e8..2caa3e372e7d2 100644
--- a/Zend/tests/bug35655.phpt
+++ b/Zend/tests/bug35655.phpt
@@ -19,7 +19,10 @@ EOT
 highlight_string($code);
 ?>
 --EXPECT--
-
-
<?php
  $x 
= <<<EOT
some string    
EOT
  
$y 2;
?> -
-
+

+<?php
+  $x = <<<EOT
+some string    
+EOT
+  $y = 2;
+?>
diff --git a/Zend/tests/bug42767.phpt b/Zend/tests/bug42767.phpt index b57177e4b5e58..0f0c3a4961722 100644 --- a/Zend/tests/bug42767.phpt +++ b/Zend/tests/bug42767.phpt @@ -11,7 +11,4 @@ highlight.html = #000000 highlight_string(' --EXPECT-- - -<?php /*some comment.. - - +
<?php /*some comment..
diff --git a/Zend/tests/bug71086.phpt b/Zend/tests/bug71086.phpt index 3e64f9c13a518..8e0b7befe8f07 100644 --- a/Zend/tests/bug71086.phpt +++ b/Zend/tests/bug71086.phpt @@ -8,7 +8,5 @@ var_dump($highlightedString); ?> --EXPECT-- -string(169) " -<?php 
 09 09 09
; -
-
" +string(139) "
<?php 
+ 09 09 09;
" diff --git a/Zend/tests/nowdoc_013.phpt b/Zend/tests/nowdoc_013.phpt index 61543f74e3b16..e2b9c98015eee 100644 --- a/Zend/tests/nowdoc_013.phpt +++ b/Zend/tests/nowdoc_013.phpt @@ -20,7 +20,9 @@ EOF; highlight_string($code); ?> --EXPECT-- - -<?php
  $x 
= <<<'EOT'
some string    
EOT
  
$y 2;
?> -
-
+
<?php
+  $x = <<<'EOT'
+some string    
+EOT
+  $y = 2;
+?>
diff --git a/Zend/tests/nowdoc_014.phpt b/Zend/tests/nowdoc_014.phpt index 694490b17d584..b4cd3ac07fca6 100644 --- a/Zend/tests/nowdoc_014.phpt +++ b/Zend/tests/nowdoc_014.phpt @@ -18,7 +18,8 @@ EOF; highlight_string($code); ?> --EXPECT-- - -<?php
  $x 
= <<<'EOT'
EOT
  
$y 2;
?> -
-
+
<?php
+  $x = <<<'EOT'
+EOT
+  $y = 2;
+?>
diff --git a/Zend/zend_highlight.c b/Zend/zend_highlight.c index 501eed5757133..167ee27192ed4 100644 --- a/Zend/zend_highlight.c +++ b/Zend/zend_highlight.c @@ -28,9 +28,6 @@ ZEND_API void zend_html_putc(char c) { switch (c) { - case '\n': - ZEND_PUTS("
"); - break; case '<': ZEND_PUTS("<"); break; @@ -40,11 +37,8 @@ ZEND_API void zend_html_putc(char c) case '&': ZEND_PUTS("&"); break; - case ' ': - ZEND_PUTS(" "); - break; case '\t': - ZEND_PUTS("    "); + ZEND_PUTS(" "); break; default: ZEND_PUTC(c); @@ -88,8 +82,7 @@ ZEND_API void zend_highlight(zend_syntax_highlighter_ini *syntax_highlighter_ini char *last_color = syntax_highlighter_ini->highlight_html; char *next_color; - zend_printf(""); - zend_printf("\n", last_color); + zend_printf("
", last_color);
 	/* highlight stuff coming back from zendlex() */
 	while ((token_type=lex_scan(&token, NULL))) {
 		switch (token_type) {
@@ -162,10 +155,9 @@ ZEND_API void zend_highlight(zend_syntax_highlighter_ini *syntax_highlighter_ini
 	}
 
 	if (last_color != syntax_highlighter_ini->highlight_html) {
-		zend_printf("\n");
+		zend_printf("");
 	}
-	zend_printf("\n");
-	zend_printf("");
+	zend_printf("
"); /* Discard parse errors thrown during tokenization */ zend_clear_exception(); diff --git a/ext/phar/tests/cache_list/frontcontroller15.phpt b/ext/phar/tests/cache_list/frontcontroller15.phpt index 1a9490be9c91c..4c7c1dddec905 100644 --- a/ext/phar/tests/cache_list/frontcontroller15.phpt +++ b/ext/phar/tests/cache_list/frontcontroller15.phpt @@ -14,7 +14,4 @@ files/frontcontroller8.phar --EXPECTHEADERS-- Content-type: text/html; charset=UTF-8 --EXPECT-- - -<?php function hio(){} - - +
<?php function hio(){}
diff --git a/ext/phar/tests/cache_list/frontcontroller3.phpt b/ext/phar/tests/cache_list/frontcontroller3.phpt index 04dad8a7ca306..0d01acb6d1c25 100644 --- a/ext/phar/tests/cache_list/frontcontroller3.phpt +++ b/ext/phar/tests/cache_list/frontcontroller3.phpt @@ -14,7 +14,4 @@ files/frontcontroller.phar --EXPECTHEADERS-- Content-type: text/html; charset=UTF-8 --EXPECT-- - -<?php function hio(){} - - +
<?php function hio(){}
diff --git a/ext/phar/tests/cache_list/frontcontroller9.phpt b/ext/phar/tests/cache_list/frontcontroller9.phpt index ddc129912164b..a894b192678e0 100644 --- a/ext/phar/tests/cache_list/frontcontroller9.phpt +++ b/ext/phar/tests/cache_list/frontcontroller9.phpt @@ -14,7 +14,4 @@ files/frontcontroller3.phar --EXPECTHEADERS-- Content-type: text/html; charset=UTF-8 --EXPECT-- - -<?php function hio(){} - - +
<?php function hio(){}
diff --git a/ext/phar/tests/frontcontroller15.phpt b/ext/phar/tests/frontcontroller15.phpt index 99d2476490b9a..f821e66bbbde0 100644 --- a/ext/phar/tests/frontcontroller15.phpt +++ b/ext/phar/tests/frontcontroller15.phpt @@ -13,7 +13,4 @@ files/frontcontroller8.phar --EXPECTHEADERS-- Content-type: text/html; charset=UTF-8 --EXPECT-- - -<?php function hio(){} - - +
<?php function hio(){}
diff --git a/ext/phar/tests/frontcontroller3.phpt b/ext/phar/tests/frontcontroller3.phpt index 045aca26c1d0b..55b42a6f5e29c 100644 --- a/ext/phar/tests/frontcontroller3.phpt +++ b/ext/phar/tests/frontcontroller3.phpt @@ -13,7 +13,4 @@ files/frontcontroller.phar --EXPECTHEADERS-- Content-type: text/html; charset=UTF-8 --EXPECT-- - -<?php function hio(){} - - +
<?php function hio(){}
diff --git a/ext/phar/tests/frontcontroller9.phpt b/ext/phar/tests/frontcontroller9.phpt index 54b50b4821226..f18ea4c111dbb 100644 --- a/ext/phar/tests/frontcontroller9.phpt +++ b/ext/phar/tests/frontcontroller9.phpt @@ -13,7 +13,4 @@ files/frontcontroller3.phar --EXPECTHEADERS-- Content-type: text/html; charset=UTF-8 --EXPECT-- - -<?php function hio(){} - - +
<?php function hio(){}
diff --git a/ext/phar/tests/tar/frontcontroller15.phar.phpt b/ext/phar/tests/tar/frontcontroller15.phar.phpt index 941baef6b7a9a..243f9c3e33943 100644 --- a/ext/phar/tests/tar/frontcontroller15.phar.phpt +++ b/ext/phar/tests/tar/frontcontroller15.phar.phpt @@ -13,7 +13,4 @@ files/frontcontroller8.phar.tar --EXPECTHEADERS-- Content-type: text/html; charset=UTF-8 --EXPECT-- - -<?php function hio(){} - - +
<?php function hio(){}
diff --git a/ext/phar/tests/tar/frontcontroller3.phar.phpt b/ext/phar/tests/tar/frontcontroller3.phar.phpt index edbd20858ab2d..98c12e27814fc 100644 --- a/ext/phar/tests/tar/frontcontroller3.phar.phpt +++ b/ext/phar/tests/tar/frontcontroller3.phar.phpt @@ -13,7 +13,4 @@ files/frontcontroller.phar.tar --EXPECTHEADERS-- Content-type: text/html; charset=UTF-8 --EXPECT-- - -<?php function hio(){} - - +
<?php function hio(){}
diff --git a/ext/phar/tests/tar/frontcontroller9.phar.phpt b/ext/phar/tests/tar/frontcontroller9.phar.phpt index 2cbfdaa832246..997b438367ffa 100644 --- a/ext/phar/tests/tar/frontcontroller9.phar.phpt +++ b/ext/phar/tests/tar/frontcontroller9.phar.phpt @@ -13,7 +13,4 @@ files/frontcontroller3.phar.tar --EXPECTHEADERS-- Content-type: text/html; charset=UTF-8 --EXPECT-- - -<?php function hio(){} - - +
<?php function hio(){}
diff --git a/ext/phar/tests/zip/frontcontroller15.phar.phpt b/ext/phar/tests/zip/frontcontroller15.phar.phpt index f10b91f68e15f..d106af1487657 100644 --- a/ext/phar/tests/zip/frontcontroller15.phar.phpt +++ b/ext/phar/tests/zip/frontcontroller15.phar.phpt @@ -15,7 +15,4 @@ files/frontcontroller8.phar.zip --EXPECTHEADERS-- Content-type: text/html; charset=UTF-8 --EXPECT-- - -<?php function hio(){} - - +
<?php function hio(){}
diff --git a/ext/phar/tests/zip/frontcontroller3.phar.phpt b/ext/phar/tests/zip/frontcontroller3.phar.phpt index 9c4581c124356..f0103d3e26c06 100644 --- a/ext/phar/tests/zip/frontcontroller3.phar.phpt +++ b/ext/phar/tests/zip/frontcontroller3.phar.phpt @@ -15,7 +15,4 @@ files/frontcontroller.phar.zip --EXPECTHEADERS-- Content-type: text/html; charset=UTF-8 --EXPECT-- - -<?php function hio(){} - - +
<?php function hio(){}
diff --git a/ext/phar/tests/zip/frontcontroller9.phar.phpt b/ext/phar/tests/zip/frontcontroller9.phar.phpt index 9f118e6c5b9bd..20e3ab432eb1e 100644 --- a/ext/phar/tests/zip/frontcontroller9.phar.phpt +++ b/ext/phar/tests/zip/frontcontroller9.phar.phpt @@ -13,7 +13,4 @@ files/frontcontroller3.phar.zip --EXPECTHEADERS-- Content-type: text/html; charset=UTF-8 --EXPECT-- - -<?php function hio(){} - - +
<?php function hio(){}
diff --git a/ext/standard/tests/general_functions/highlight_heredoc.phpt b/ext/standard/tests/general_functions/highlight_heredoc.phpt index ee4e2e8281b02..d5d25f5eaf63e 100644 --- a/ext/standard/tests/general_functions/highlight_heredoc.phpt +++ b/ext/standard/tests/general_functions/highlight_heredoc.phpt @@ -16,6 +16,12 @@ DDDD; highlight_string($str); ?> --EXPECT-- - -
$x=<<<DD
jhdsjkfhjdsh
DD
."";
$a=<<<DDDD
jhdsjkfhjdsh
DDDD;
-
+

+$x=<<<DD
+jhdsjkfhjdsh
+DD
+."";
+$a=<<<DDDD
+jhdsjkfhjdsh
+DDDD;
+
diff --git a/ext/standard/tests/strings/highlight_file.phpt b/ext/standard/tests/strings/highlight_file.phpt index 28bde64a18237..048399216c907 100644 --- a/ext/standard/tests/strings/highlight_file.phpt +++ b/ext/standard/tests/strings/highlight_file.phpt @@ -42,16 +42,14 @@ Warning: highlight_file(%shighlight_file.dat): Failed to open stream: No such fi Warning: highlight_file(): Failed opening '%shighlight_file.dat' for highlighting in %s on line %d bool(false) - -<?php echo "test"?> - -bool(true) - -<?php echo "test ?> - -bool(true) - -
<?php
class test {
    public 
$var 1;
    private function 
foo() { echo "foo"; }
    public function 
bar() { var_dump(test::foo()); }
}
?> -
-
bool(true) +
<?php echo "test"; ?>
bool(true) +
<?php echo "test ?>
bool(true) +

+<?php
+class test {
+    public $var = 1;
+    private function foo() { echo "foo"; }
+    public function bar() { var_dump(test::foo()); }
+}
+?>
bool(true) Done diff --git a/ext/standard/tests/strings/show_source_basic.phpt b/ext/standard/tests/strings/show_source_basic.phpt index 4f21adf712409..a4d04c346b42d 100644 --- a/ext/standard/tests/strings/show_source_basic.phpt +++ b/ext/standard/tests/strings/show_source_basic.phpt @@ -21,7 +21,19 @@ show_source(__FILE__); ?> --EXPECT-- *** Test by calling method or function with its expected arguments *** - -<?php
echo "*** Test by calling method or function with its expected arguments ***\n";
$foo 'bar';
$baz "something ".$foo."\n";

if ( 
$foo == 'bar' )
{
  
$baz 'baz';
}

 
/* some code here */

show_source(__FILE__);

?>
-
-
+
<?php
+echo "*** Test by calling method or function with its expected arguments ***\n";
+$foo = 'bar';
+$baz = "something ".$foo."\n";
+
+if ( $foo == 'bar' )
+{
+  $baz = 'baz';
+}
+
+ /* some code here */
+
+show_source(__FILE__);
+
+?>
+
diff --git a/ext/standard/tests/strings/show_source_variation1.phpt b/ext/standard/tests/strings/show_source_variation1.phpt index 099dd233a1e95..27977809f0133 100644 --- a/ext/standard/tests/strings/show_source_variation1.phpt +++ b/ext/standard/tests/strings/show_source_variation1.phpt @@ -22,7 +22,19 @@ echo $foo; --EXPECT-- *** Test by calling method or function with its expected arguments and php output *** baz - -<?php
echo "*** Test by calling method or function with its expected arguments and php output ***\n";
$foo 'bar';
$baz "something ".$foo."\n";

if ( 
$foo == 'bar' )
{
  
$baz "baz\n";
}

 
/* some code here */
echo $baz;
show_source(__FILE__);
echo 
$foo;
?>
-
-
bar +
<?php
+echo "*** Test by calling method or function with its expected arguments and php output ***\n";
+$foo = 'bar';
+$baz = "something ".$foo."\n";
+
+if ( $foo == 'bar' )
+{
+  $baz = "baz\n";
+}
+
+ /* some code here */
+echo $baz;
+show_source(__FILE__);
+echo $foo;
+?>
+
bar diff --git a/ext/standard/tests/strings/show_source_variation2.phpt b/ext/standard/tests/strings/show_source_variation2.phpt index ca26ef29c9cec..064398356068c 100644 --- a/ext/standard/tests/strings/show_source_variation2.phpt +++ b/ext/standard/tests/strings/show_source_variation2.phpt @@ -21,7 +21,19 @@ var_dump($source); ?> --EXPECT-- *** Test by calling method or function with its expected arguments and output to variable *** -string(1975) " -<?php
echo "*** Test by calling method or function with its expected arguments and output to variable ***\n";
$foo 'bar';
$baz "something ".$foo."\n";

if ( 
$foo == 'bar' )
{
  
$baz "baz\n";
}

 
/* some code here */
$source show_source(__FILE__true);

var_dump($source);
?>
-
-
" +string(1705) "
<?php
+echo "*** Test by calling method or function with its expected arguments and output to variable ***\n";
+$foo = 'bar';
+$baz = "something ".$foo."\n";
+
+if ( $foo == 'bar' )
+{
+  $baz = "baz\n";
+}
+
+ /* some code here */
+$source = show_source(__FILE__, true);
+
+var_dump($source);
+?>
+
" diff --git a/sapi/cgi/tests/008.phpt b/sapi/cgi/tests/008.phpt index 05d9d8c190e87..5ba21028b3327 100644 --- a/sapi/cgi/tests/008.phpt +++ b/sapi/cgi/tests/008.phpt @@ -41,10 +41,22 @@ echo "Done\n"; string(%d) "X-Powered-By: PHP/%s Content-type: text/html%r; charset=.*|%r - -
<?php
$test 
"var"//var
/* test class */
class test {
    private 
$var = array();

    public static function 
foo(Test $arg) {
        echo 
"hello";
        
var_dump($this);
    }
}

$o = new test;
?>
-
-
" +

+<?php
+$test = "var"; //var
+/* test class */
+class test {
+    private $var = array();
+
+    public static function foo(Test $arg) {
+        echo "hello";
+        var_dump($this);
+    }
+}
+
+$o = new test;
+?>
+
" string(%d) "Status: 404 Not Found X-Powered-By: PHP/%s Content-type: text/html%r; charset=.*|%r diff --git a/sapi/cli/tests/014.phpt b/sapi/cli/tests/014.phpt index 734eef0c2ce95..8a13433d643bc 100644 --- a/sapi/cli/tests/014.phpt +++ b/sapi/cli/tests/014.phpt @@ -36,10 +36,22 @@ var_dump(`$php -n -s unknown`); echo "Done\n"; ?> --EXPECT-- -string(1478) " -
<?php
$test 
"var"//var
/* test class */
class test {
    private 
$var = array();

    public static function 
foo(Test $arg) {
        echo 
"hello";
        
var_dump($this);
    }
}

$o = new test;
?>
-
-
" +string(1158) "

+<?php
+$test = "var"; //var
+/* test class */
+class test {
+    private $var = array();
+
+    public static function foo(Test $arg) {
+        echo "hello";
+        var_dump($this);
+    }
+}
+
+$o = new test;
+?>
+
" Could not open input file: unknown NULL Done diff --git a/tests/strings/004.phpt b/tests/strings/004.phpt index 44bb000d14a3a..458278831d3fe 100644 --- a/tests/strings/004.phpt +++ b/tests/strings/004.phpt @@ -13,9 +13,5 @@ $var = highlight_string("

", TRUE); echo "\n[$var]\n"; ?> --EXPECT-- - -<br /><?php echo "foo"?><br /> - -[ -<br /><?php echo "bar"?><br /> -] +
<br /><?php echo "foo"; ?><br />
+[
<br /><?php echo "bar"; ?><br />
] diff --git a/tests/strings/bug26703.phpt b/tests/strings/bug26703.phpt index 4e48845fdf0a1..aaa7833f01660 100644 --- a/tests/strings/bug26703.phpt +++ b/tests/strings/bug26703.phpt @@ -11,7 +11,4 @@ highlight.html=#000000 highlight_string(''); ?> --EXPECT-- - -<?php echo "foo[] $a \n"?> - - +
<?php echo "foo[] $a \n"; ?>
From bb092ab4c6fa36b56c89216f3a127fa763940bf0 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 6 Aug 2023 14:40:54 +0200 Subject: [PATCH 02/47] Fix #80927: Removing documentElement after creating attribute node: possible use-after-free Closes GH-11892. --- NEWS | 2 + UPGRADING.INTERNALS | 2 + ext/dom/element.c | 2 +- ext/dom/php_dom.c | 31 +------------ ext/dom/php_dom.h | 1 - ext/dom/tests/bug80927.phpt | 89 +++++++++++++++++++++++++++++++++++++ ext/libxml/libxml.c | 68 +++++++++++++++++++++++++++- ext/libxml/php_libxml.h | 1 + 8 files changed, 163 insertions(+), 33 deletions(-) create mode 100644 ext/dom/tests/bug80927.phpt diff --git a/NEWS b/NEWS index c5f24c90b174d..1d1eedd249ac0 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,8 @@ PHP NEWS - DOM: . adoptNode now respects the strict error checking property. (nielsdos) . Align DOMChildNode parent checks with spec. (nielsdos) + . Fixed bug #80927 (Removing documentElement after creating attribute node: + possible use-after-free). (nielsdos) - Opcache: . Avoid resetting JIT counter handlers from multiple processes/threads. diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 1a8c5970c7f01..f35d54846f25d 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -148,6 +148,8 @@ PHP 8.3 INTERNALS UPGRADE NOTES dom_parent_node_before() now use an uint32_t argument for the number of nodes instead of int. - There is now a helper function php_dom_get_content_into_zval() to get the contents of a node. This avoids allocation if possible. + - The function dom_set_old_ns() has been moved into ext/libxml as php_libxml_set_old_ns() and + is now publicly exposed as an API. g. ext/libxml - Two new functions: php_libxml_invalidate_node_list_cache_from_doc() and diff --git a/ext/dom/element.c b/ext/dom/element.c index d09518419228b..8fb19d9e2eaed 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -1497,7 +1497,7 @@ PHP_METHOD(DOMElement, toggleAttribute) } ns->next = NULL; - dom_set_old_ns(thisp->doc, ns); + php_libxml_set_old_ns(thisp->doc, ns); dom_reconcile_ns(thisp->doc, thisp); } else { /* TODO: in the future when namespace bugs are fixed, diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 5a066db8d708c..9e036214bde53 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -1436,35 +1436,6 @@ void dom_normalize (xmlNodePtr nodep) } /* }}} end dom_normalize */ - -/* {{{ void dom_set_old_ns(xmlDoc *doc, xmlNs *ns) */ -void dom_set_old_ns(xmlDoc *doc, xmlNs *ns) { - if (doc == NULL) - return; - - ZEND_ASSERT(ns->next == NULL); - - /* Note: we'll use a prepend strategy instead of append to - * make sure we don't lose performance when the list is long. - * As libxml2 could assume the xml node is the first one, we'll place our - * new entries after the first one. */ - - if (doc->oldNs == NULL) { - doc->oldNs = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); - if (doc->oldNs == NULL) { - return; - } - memset(doc->oldNs, 0, sizeof(xmlNs)); - doc->oldNs->type = XML_LOCAL_NAMESPACE; - doc->oldNs->href = xmlStrdup(XML_XML_NAMESPACE); - doc->oldNs->prefix = xmlStrdup((const xmlChar *)"xml"); - } else { - ns->next = doc->oldNs->next; - } - doc->oldNs->next = ns; -} -/* }}} end dom_set_old_ns */ - static void dom_reconcile_ns_internal(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr search_parent) { xmlNsPtr nsptr, nsdftptr, curns, prevns = NULL; @@ -1486,7 +1457,7 @@ static void dom_reconcile_ns_internal(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePt /* Note: we can't get here if the ns is already on the oldNs list. * This is because in that case the definition won't be on the node, and * therefore won't be in the nodep->nsDef list. */ - dom_set_old_ns(doc, curns); + php_libxml_set_old_ns(doc, curns); curns = prevns; } } diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index f0a2d598625c3..b4a6a2d01e83b 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -128,7 +128,6 @@ void php_dom_throw_error_with_message(int error_code, char *error_message, int s void node_list_unlink(xmlNodePtr node); int dom_check_qname(char *qname, char **localname, char **prefix, int uri_len, int name_len); xmlNsPtr dom_get_ns(xmlNodePtr node, char *uri, int *errorcode, char *prefix); -void dom_set_old_ns(xmlDoc *doc, xmlNs *ns); void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep); void dom_reconcile_ns_list(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr last); xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName); diff --git a/ext/dom/tests/bug80927.phpt b/ext/dom/tests/bug80927.phpt new file mode 100644 index 0000000000000..5c12e8fb867d9 --- /dev/null +++ b/ext/dom/tests/bug80927.phpt @@ -0,0 +1,89 @@ +--TEST-- +Bug #80927 (Removing documentElement after creating attribute node: possible use-after-free) +--EXTENSIONS-- +dom +--FILE-- +appendChild($dom->createElement("html")); + $a = $dom->createAttributeNS("fake_ns", "test:test"); + $dom->removeChild($dom->documentElement); + + echo $dom->saveXML(); + + var_dump($a->namespaceURI); + var_dump($a->prefix); +} + +enum Test2Variation { + case REMOVE_DOCUMENT; + case REMOVE_CHILD; +} + +function test2(Test2Variation $variation) { + $dom = new DOMDocument(); + $dom->appendChild($dom->createElement("html")); + $a = $dom->createAttributeNS("fake_ns", "test:test"); + + $foo = $dom->appendChild($dom->createElement('foo')); + $foo->appendChild($dom->documentElement); + + unset($foo); + + match ($variation) { + Test2Variation::REMOVE_DOCUMENT => $dom->documentElement->remove(), + Test2Variation::REMOVE_CHILD => $dom->documentElement->firstElementChild->remove(), + }; + + echo $dom->saveXML(); + + var_dump($a->namespaceURI); + var_dump($a->prefix); +} + +function test3() { + $dom = new DOMDocument(); + $dom->appendChild($dom->createElement('html')); + $foobar = $dom->documentElement->appendChild($dom->createElementNS('some:ns', 'foo:bar')); + $foobar2 = $foobar->appendChild($dom->createElementNS('some:ns', 'foo:bar2')); + $foobar->remove(); + unset($foobar); + $dom->documentElement->appendChild($foobar2); + + echo $dom->saveXML(); + + var_dump($foobar2->namespaceURI); + var_dump($foobar2->prefix); +} + +echo "--- Remove namespace declarator for attribute, root ---\n"; +test1(); +echo "--- Remove namespace declarator for attribute, moved root ---\n"; +test2(Test2Variation::REMOVE_DOCUMENT); +echo "--- Remove namespace declarator for attribute, moved root child ---\n"; +test2(Test2Variation::REMOVE_CHILD); +echo "--- Remove namespace declarator for element ---\n"; +test3(); + +?> +--EXPECT-- +--- Remove namespace declarator for attribute, root --- + +string(7) "fake_ns" +string(4) "test" +--- Remove namespace declarator for attribute, moved root --- + +string(7) "fake_ns" +string(4) "test" +--- Remove namespace declarator for attribute, moved root child --- + + +string(7) "fake_ns" +string(4) "test" +--- Remove namespace declarator for element --- + + +string(7) "some:ns" +string(3) "foo" diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 97ceec45ddbfd..24ba80bd5de39 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -103,6 +103,39 @@ zend_module_entry libxml_module_entry = { /* }}} */ +static void php_libxml_set_old_ns_list(xmlDocPtr doc, xmlNsPtr first, xmlNsPtr last) +{ + if (UNEXPECTED(doc == NULL)) { + return; + } + + ZEND_ASSERT(last->next == NULL); + + /* Note: we'll use a prepend strategy instead of append to + * make sure we don't lose performance when the list is long. + * As libxml2 could assume the xml node is the first one, we'll place our + * new entries after the first one. */ + + if (UNEXPECTED(doc->oldNs == NULL)) { + doc->oldNs = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); + if (doc->oldNs == NULL) { + return; + } + memset(doc->oldNs, 0, sizeof(xmlNs)); + doc->oldNs->type = XML_LOCAL_NAMESPACE; + doc->oldNs->href = xmlStrdup(XML_XML_NAMESPACE); + doc->oldNs->prefix = xmlStrdup((const xmlChar *)"xml"); + } else { + last->next = doc->oldNs->next; + } + doc->oldNs->next = first; +} + +PHP_LIBXML_API void php_libxml_set_old_ns(xmlDocPtr doc, xmlNsPtr ns) +{ + php_libxml_set_old_ns_list(doc, ns, ns); +} + static void php_libxml_unlink_entity(void *data, void *table, const xmlChar *name) { xmlEntityPtr entity = data; @@ -211,8 +244,41 @@ static void php_libxml_node_free(xmlNodePtr node) xmlHashScan(dtd->pentities, php_libxml_unlink_entity, dtd->pentities); /* No unlinking of notations, see remark above at case XML_NOTATION_NODE. */ } - ZEND_FALLTHROUGH; + xmlFreeNode(node); + break; } + case XML_ELEMENT_NODE: + if (node->nsDef && node->doc) { + /* Make the namespace declaration survive the destruction of the holding element. + * This prevents a use-after-free on the namespace declaration. + * + * The main problem is that libxml2 doesn't have a reference count on the namespace declaration. + * We don't actually need to save the namespace declaration if we know the subtree it belongs to + * has no references from userland. However, we can't know that without traversing the whole subtree + * (=> slow), or without adding some subtree metadata (=> also slow). + * So we have to assume we need to save everything. + * + * However, namespace declarations are quite rare in comparison to other node types. + * Most node types are either elements, text or attributes. + * And you only need one namespace declaration per namespace (in principle). + * So I expect the number of namespace declarations to be low for an average XML document. + * + * In the worst possible case we have to save all namespace declarations when we for example remove + * the whole document. But given the above reasoning this likely won't be a lot of declarations even + * in the worst case. + * A single declaration only takes about 48 bytes of memory, and I don't expect the worst case to occur + * very often (why would you remove the whole document?). + */ + xmlNsPtr ns = node->nsDef; + xmlNsPtr last = ns; + while (last->next) { + last = last->next; + } + php_libxml_set_old_ns_list(node->doc, ns, last); + node->nsDef = NULL; + } + xmlFreeNode(node); + break; default: xmlFreeNode(node); break; diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h index 3bd5202f58790..78a5df8f97e66 100644 --- a/ext/libxml/php_libxml.h +++ b/ext/libxml/php_libxml.h @@ -134,6 +134,7 @@ PHP_LIBXML_API int php_libxml_xmlCheckUTF8(const unsigned char *s); PHP_LIBXML_API void php_libxml_switch_context(zval *context, zval *oldcontext); PHP_LIBXML_API void php_libxml_issue_error(int level, const char *msg); PHP_LIBXML_API bool php_libxml_disable_entity_loader(bool disable); +PHP_LIBXML_API void php_libxml_set_old_ns(xmlDocPtr doc, xmlNsPtr ns); /* Init/shutdown functions*/ PHP_LIBXML_API void php_libxml_initialize(void); From e56ed6e1ab21f6b0bd33abb8719eae86428f5fb7 Mon Sep 17 00:00:00 2001 From: Jorg Adam Sowa Date: Sun, 13 Aug 2023 17:17:36 +0200 Subject: [PATCH 03/47] BCmath extension code reformatting (#11896) Re-formats the BCmath extension to have consistent formatting. Mostly, it adds the spaces in calculations to have them more readable. Also: - removes unused headers - removes few variables which are used only once in the code Co-authored-by: George Peter Banyard --- ext/bcmath/libbcmath/src/add.c | 22 +++--- ext/bcmath/libbcmath/src/bcmath.h | 2 +- ext/bcmath/libbcmath/src/compare.c | 12 ++-- ext/bcmath/libbcmath/src/debug.c | 10 +-- ext/bcmath/libbcmath/src/div.c | 105 +++++++++++++--------------- ext/bcmath/libbcmath/src/divmod.c | 8 +-- ext/bcmath/libbcmath/src/doaddsub.c | 27 +++---- ext/bcmath/libbcmath/src/init.c | 16 ++--- ext/bcmath/libbcmath/src/int2num.c | 5 +- ext/bcmath/libbcmath/src/nearzero.c | 8 +-- ext/bcmath/libbcmath/src/num2long.c | 9 +-- ext/bcmath/libbcmath/src/num2str.c | 12 ++-- ext/bcmath/libbcmath/src/output.c | 87 +++++++++++------------ ext/bcmath/libbcmath/src/raise.c | 20 +++--- ext/bcmath/libbcmath/src/raisemod.c | 24 +++---- ext/bcmath/libbcmath/src/recmul.c | 100 +++++++++++++------------- ext/bcmath/libbcmath/src/rmzero.c | 2 +- ext/bcmath/libbcmath/src/sqrt.c | 38 +++++----- ext/bcmath/libbcmath/src/str2num.c | 23 +++--- ext/bcmath/libbcmath/src/sub.c | 17 +++-- ext/bcmath/libbcmath/src/zero.c | 4 +- 21 files changed, 265 insertions(+), 286 deletions(-) diff --git a/ext/bcmath/libbcmath/src/add.c b/ext/bcmath/libbcmath/src/add.c index a57ed32afaee1..1e6285a0df19a 100644 --- a/ext/bcmath/libbcmath/src/add.c +++ b/ext/bcmath/libbcmath/src/add.c @@ -39,34 +39,30 @@ N1 is added to N2 and the result placed into RESULT. SCALE_MIN is the minimum scale for the result. */ -void bc_add (bc_num n1, bc_num n2, bc_num *result, size_t scale_min) +void bc_add(bc_num n1, bc_num n2, bc_num *result, size_t scale_min) { bc_num sum = NULL; - int cmp_res; - size_t res_scale; if (n1->n_sign == n2->n_sign) { - sum = _bc_do_add (n1, n2, scale_min); + sum = _bc_do_add(n1, n2, scale_min); sum->n_sign = n1->n_sign; } else { /* subtraction must be done. */ /* Compare magnitudes. */ - cmp_res = _bc_do_compare(n1, n2, false, false); - switch (cmp_res) { + switch (_bc_do_compare(n1, n2, false, false)) { case -1: /* n1 is less than n2, subtract n1 from n2. */ - sum = _bc_do_sub (n2, n1, scale_min); + sum = _bc_do_sub(n2, n1, scale_min); sum->n_sign = n2->n_sign; break; - case 0: + case 0: /* They are equal! return zero with the correct scale! */ - res_scale = MAX (scale_min, MAX(n1->n_scale, n2->n_scale)); - sum = bc_new_num (1, res_scale); - memset (sum->n_value, 0, res_scale+1); + sum = bc_new_num (1, MAX(scale_min, MAX(n1->n_scale, n2->n_scale))); + memset(sum->n_value, 0, sum->n_scale + 1); break; - case 1: + case 1: /* n2 is less than n1, subtract n2 from n1. */ - sum = _bc_do_sub (n1, n2, scale_min); + sum = _bc_do_sub(n1, n2, scale_min); sum->n_sign = n1->n_sign; } } diff --git a/ext/bcmath/libbcmath/src/bcmath.h b/ext/bcmath/libbcmath/src/bcmath.h index 38ad86b34c718..de51ee7457110 100644 --- a/ext/bcmath/libbcmath/src/bcmath.h +++ b/ext/bcmath/libbcmath/src/bcmath.h @@ -61,7 +61,7 @@ typedef struct bc_struct { #include "../../php_bcmath.h" /* Needed for BCG() macro */ /* The base used in storing the numbers in n_value above. - Currently this MUST be 10. */ + Currently, this MUST be 10. */ #define BASE 10 diff --git a/ext/bcmath/libbcmath/src/compare.c b/ext/bcmath/libbcmath/src/compare.c index d21de720cb145..f8b36cb9c3155 100644 --- a/ext/bcmath/libbcmath/src/compare.c +++ b/ext/bcmath/libbcmath/src/compare.c @@ -30,9 +30,9 @@ *************************************************************************/ #include -#include #include "bcmath.h" #include "private.h" +#include /* Compare two bc numbers. Return value is 0 if equal, -1 if N1 is less @@ -46,9 +46,11 @@ int _bc_do_compare(bc_num n1, bc_num n2, bool use_sign, bool ignore_last) /* First, compare signs. */ if (use_sign && n1->n_sign != n2->n_sign) { if (n1->n_sign == PLUS) { - return (1); /* Positive N1 > Negative N2 */ + /* Positive N1 > Negative N2 */ + return (1); } else { - return (-1); /* Negative N1 < Positive N1 */ + /* Negative N1 < Positive N1 */ + return (-1); } } @@ -107,7 +109,7 @@ int _bc_do_compare(bc_num n1, bc_num n2, bool use_sign, bool ignore_last) /* They are equal up to the last part of the equal part of the fraction. */ if (n1->n_scale != n2->n_scale) { if (n1->n_scale > n2->n_scale) { - for (count = n1->n_scale-n2->n_scale; count>0; count--) { + for (count = n1->n_scale - n2->n_scale; count > 0; count--) { if (*n1ptr++ != 0) { /* Magnitude of n1 > n2. */ if (!use_sign || n1->n_sign == PLUS) { @@ -118,7 +120,7 @@ int _bc_do_compare(bc_num n1, bc_num n2, bool use_sign, bool ignore_last) } } } else { - for (count = n2->n_scale-n1->n_scale; count>0; count--) { + for (count = n2->n_scale - n1->n_scale; count > 0; count--) { if (*n2ptr++ != 0) { /* Magnitude of n1 < n2. */ if (!use_sign || n1->n_sign == PLUS) { diff --git a/ext/bcmath/libbcmath/src/debug.c b/ext/bcmath/libbcmath/src/debug.c index a9ed000196a08..d723dba86fe3c 100644 --- a/ext/bcmath/libbcmath/src/debug.c +++ b/ext/bcmath/libbcmath/src/debug.c @@ -36,7 +36,7 @@ /* pn prints the number NUM in base 10. */ -static void out_char (char c) +static void out_char(char c) { putchar(c); } @@ -45,16 +45,16 @@ static void out_char (char c) void pn(bc_num num) { bc_out_num(num, 10, out_char, 0); - out_char ('\n'); + out_char('\n'); } /* pv prints a character array as if it was a string of bcd digits. */ -void pv (char *name, unsigned char *num, size_t len) +void pv(char *name, unsigned char *num, size_t len) { printf("%s=", name); - for (size_t i = 0; i < len; i++){ - printf ("%c",BCD_CHAR(num[i])); + for (size_t i = 0; i < len; i++) { + printf("%c", BCD_CHAR(num[i])); } printf("\n"); } diff --git a/ext/bcmath/libbcmath/src/div.c b/ext/bcmath/libbcmath/src/div.c index b18f65d9db402..f45188455e7f2 100644 --- a/ext/bcmath/libbcmath/src/div.c +++ b/ext/bcmath/libbcmath/src/div.c @@ -33,6 +33,7 @@ #include "private.h" #include #include +#include #include "zend_alloc.h" @@ -43,7 +44,7 @@ static void _one_mult(unsigned char *num, size_t size, int digit, unsigned char *result) { - int carry, value; + size_t carry, value; unsigned char *nptr, *rptr; if (digit == 0) { @@ -53,8 +54,8 @@ static void _one_mult(unsigned char *num, size_t size, int digit, unsigned char memcpy(result, num, size); } else { /* Initialize */ - nptr = (unsigned char *) (num+size-1); - rptr = (unsigned char *) (result+size-1); + nptr = (unsigned char *) (num + size - 1); + rptr = (unsigned char *) (result + size - 1); carry = 0; while (size-- > 0) { @@ -74,59 +75,55 @@ static void _one_mult(unsigned char *num, size_t size, int digit, unsigned char /* The full division routine. This computes N1 / N2. It returns true if the division is ok and the result is in QUOT. The number of digits after the decimal point is SCALE. It returns false if division - by zero is tried. The algorithm is found in Knuth Vol 2. p237. */ + by zero is tried. The algorithm is found in Knuth Vol 2. p237. */ bool bc_divide(bc_num n1, bc_num n2, bc_num *quot, int scale) { bc_num qval; unsigned char *num1, *num2; unsigned char *ptr1, *ptr2, *n2ptr, *qptr; - int scale1, val; - unsigned int len1, len2, scale2, qdigits, extra, count; - unsigned int qdig, qguess, borrow, carry; + int scale1, val; + unsigned int len1, len2, scale2, qdigits, extra, count; + unsigned int qdig, qguess, borrow, carry; unsigned char *mval; + unsigned int norm; bool zero; - unsigned int norm; /* Test for divide by zero. */ if (bc_is_zero(n2)) { return false; } - /* Test for divide by 1. If it is we must truncate. */ - if (n2->n_scale == 0) { - if (n2->n_len == 1 && *n2->n_value == 1) { - qval = bc_new_num (n1->n_len, scale); - qval->n_sign = (n1->n_sign == n2->n_sign ? PLUS : MINUS); - memset (&qval->n_value[n1->n_len],0,scale); - memcpy (qval->n_value, n1->n_value, n1->n_len + MIN(n1->n_scale,scale)); - bc_free_num (quot); - *quot = qval; - } + /* Test for divide by 1. If it is we must truncate. */ + if (n2->n_scale == 0 && n2->n_len == 1 && *n2->n_value == 1) { + qval = bc_new_num (n1->n_len, scale); + qval->n_sign = (n1->n_sign == n2->n_sign ? PLUS : MINUS); + memset(&qval->n_value[n1->n_len], 0, scale); + memcpy(qval->n_value, n1->n_value, n1->n_len + MIN(n1->n_scale, scale)); + bc_free_num (quot); + *quot = qval; } /* Set up the divide. Move the decimal point on n1 by n2's scale. Remember, zeros on the end of num2 are wasted effort for dividing. */ scale2 = n2->n_scale; - n2ptr = (unsigned char *) n2->n_value+n2->n_len+scale2-1; - while ((scale2 > 0) && (*n2ptr-- == 0)) { + n2ptr = (unsigned char *) n2->n_value + n2->n_len + scale2 - 1; + while ((scale2 > 0) && (*n2ptr == 0)) { scale2--; + n2ptr--; } len1 = n1->n_len + scale2; scale1 = n1->n_scale - scale2; - if (scale1 < scale) { - extra = scale - scale1; - } else { - extra = 0; - } - num1 = (unsigned char *) safe_emalloc (1, n1->n_len+n1->n_scale, extra+2); - memset (num1, 0, n1->n_len+n1->n_scale+extra+2); - memcpy (num1+1, n1->n_value, n1->n_len+n1->n_scale); + extra = MAX(scale - scale1, 0); + + num1 = (unsigned char *) safe_emalloc(1, n1->n_len + n1->n_scale, extra + 2); + memset(num1, 0, n1->n_len + n1->n_scale + extra + 2); + memcpy(num1 + 1, n1->n_value, n1->n_len + n1->n_scale); len2 = n2->n_len + scale2; - num2 = (unsigned char *) safe_emalloc (1, len2, 1); - memcpy (num2, n2->n_value, len2); - *(num2+len2) = 0; + num2 = (unsigned char *) safe_emalloc(1, len2, 1); + memcpy(num2, n2->n_value, len2); + *(num2 + len2) = 0; n2ptr = num2; while (*n2ptr == 0) { n2ptr++; @@ -134,22 +131,22 @@ bool bc_divide(bc_num n1, bc_num n2, bc_num *quot, int scale) } /* Calculate the number of quotient digits. */ - if (len2 > len1+scale) { - qdigits = scale+1; + if (len2 > len1 + scale) { + qdigits = scale + 1; zero = true; } else { zero = false; if (len2 > len1) { /* One for the zero integer part. */ - qdigits = scale+1; + qdigits = scale + 1; } else { - qdigits = len1-len2+scale+1; + qdigits = len1 - len2 + scale + 1; } } /* Allocate and zero the storage for the quotient. */ - qval = bc_new_num (qdigits-scale,scale); - memset (qval->n_value, 0, qdigits); + qval = bc_new_num (qdigits - scale, scale); + memset(qval->n_value, 0, qdigits); /* Allocate storage for the temporary storage mval. */ mval = (unsigned char *) safe_emalloc(1, len2, 1); @@ -157,38 +154,34 @@ bool bc_divide(bc_num n1, bc_num n2, bc_num *quot, int scale) /* Now for the full divide algorithm. */ if (!zero) { /* Normalize */ - norm = 10 / ((int)*n2ptr + 1); + norm = 10 / ((int) *n2ptr + 1); if (norm != 1) { - _one_mult (num1, len1+scale1+extra+1, norm, num1); - _one_mult (n2ptr, len2, norm, n2ptr); + _one_mult(num1, len1 + scale1 + extra + 1, norm, num1); + _one_mult(n2ptr, len2, norm, n2ptr); } /* Initialize divide loop. */ qdig = 0; if (len2 > len1) { - qptr = (unsigned char *) qval->n_value+len2-len1; + qptr = (unsigned char *) qval->n_value + len2 - len1; } else { qptr = (unsigned char *) qval->n_value; } /* Loop */ - while (qdig <= len1+scale-len2) { + while (qdig <= len1 + scale - len2) { /* Calculate the quotient digit guess. */ if (*n2ptr == num1[qdig]) { qguess = 9; } else { - qguess = (num1[qdig]*10 + num1[qdig+1]) / *n2ptr; + qguess = (num1[qdig] * 10 + num1[qdig + 1]) / *n2ptr; } /* Test qguess. */ - if ( - n2ptr[1]*qguess > (num1[qdig]*10 + num1[qdig+1] - *n2ptr*qguess)*10 + num1[qdig+2] - ) { + if (n2ptr[1] * qguess > (num1[qdig] * 10 + num1[qdig + 1] - *n2ptr * qguess) * 10 + num1[qdig + 2]) { qguess--; /* And again. */ - if ( - n2ptr[1]*qguess > (num1[qdig]*10 + num1[qdig+1] - *n2ptr*qguess)*10 + num1[qdig+2] - ) { + if (n2ptr[1] * qguess > (num1[qdig] * 10 + num1[qdig + 1] - *n2ptr * qguess) * 10 + num1[qdig + 2]) { qguess--; } } @@ -197,10 +190,10 @@ bool bc_divide(bc_num n1, bc_num n2, bc_num *quot, int scale) borrow = 0; if (qguess != 0) { *mval = 0; - _one_mult (n2ptr, len2, qguess, mval+1); - ptr1 = (unsigned char *) num1+qdig+len2; - ptr2 = (unsigned char *) mval+len2; - for (count = 0; count < len2+1; count++) { + _one_mult(n2ptr, len2, qguess, mval + 1); + ptr1 = (unsigned char *) num1 + qdig + len2; + ptr2 = (unsigned char *) mval + len2; + for (count = 0; count < len2 + 1; count++) { val = (int) *ptr1 - (int) *ptr2-- - borrow; if (val < 0) { val += 10; @@ -215,8 +208,8 @@ bool bc_divide(bc_num n1, bc_num n2, bc_num *quot, int scale) /* Test for negative result. */ if (borrow == 1) { qguess--; - ptr1 = (unsigned char *) num1+qdig+len2; - ptr2 = (unsigned char *) n2ptr+len2-1; + ptr1 = (unsigned char *) num1 + qdig + len2; + ptr2 = (unsigned char *) n2ptr + len2 - 1; carry = 0; for (count = 0; count < len2; count++) { val = (int) *ptr1 + (int) *ptr2-- + carry; @@ -240,7 +233,7 @@ bool bc_divide(bc_num n1, bc_num n2, bc_num *quot, int scale) } /* Clean up and return the number. */ - qval->n_sign = ( n1->n_sign == n2->n_sign ? PLUS : MINUS ); + qval->n_sign = (n1->n_sign == n2->n_sign ? PLUS : MINUS); if (bc_is_zero(qval)) { qval->n_sign = PLUS; } diff --git a/ext/bcmath/libbcmath/src/divmod.c b/ext/bcmath/libbcmath/src/divmod.c index 2999cd8cd1c34..d4f9d4d38cfa9 100644 --- a/ext/bcmath/libbcmath/src/divmod.c +++ b/ext/bcmath/libbcmath/src/divmod.c @@ -53,16 +53,16 @@ bool bc_divmod(bc_num num1, bc_num num2, bc_num *quot, bc_num *rem, size_t scale } /* Calculate final scale. */ - rscale = MAX (num1->n_scale, num2->n_scale+scale); + rscale = MAX (num1->n_scale, num2->n_scale + scale); bc_init_num(&temp); /* Calculate it. */ - bc_divide (num1, num2, &temp, 0); + bc_divide(num1, num2, &temp, 0); if (quot) { quotient = bc_copy_num(temp); } - bc_multiply (temp, num2, &temp, rscale); - bc_sub (num1, temp, rem, rscale); + bc_multiply(temp, num2, &temp, rscale); + bc_sub(num1, temp, rem, rscale); bc_free_num (&temp); if (quot) { diff --git a/ext/bcmath/libbcmath/src/doaddsub.c b/ext/bcmath/libbcmath/src/doaddsub.c index 29de12fb272c8..c5eeab94b93fb 100644 --- a/ext/bcmath/libbcmath/src/doaddsub.c +++ b/ext/bcmath/libbcmath/src/doaddsub.c @@ -69,12 +69,12 @@ bc_num _bc_do_add(bc_num n1, bc_num n2, size_t scale_min) /* Add the fraction part. First copy the longer fraction.*/ if (n1bytes != n2bytes) { if (n1bytes > n2bytes) { - while (n1bytes>n2bytes) { + while (n1bytes > n2bytes) { *sumptr-- = *n1ptr--; n1bytes--; } } else { - while (n2bytes>n1bytes) { + while (n2bytes > n1bytes) { *sumptr-- = *n2ptr--; n2bytes--; } @@ -87,7 +87,7 @@ bc_num _bc_do_add(bc_num n1, bc_num n2, size_t scale_min) carry = 0; while ((n1bytes > 0) && (n2bytes > 0)) { *sumptr = *n1ptr-- + *n2ptr-- + carry; - if (*sumptr > (BASE-1)) { + if (*sumptr > (BASE - 1)) { carry = 1; *sumptr -= BASE; } else { @@ -105,7 +105,7 @@ bc_num _bc_do_add(bc_num n1, bc_num n2, size_t scale_min) } while (n1bytes-- > 0) { *sumptr = *n1ptr-- + carry; - if (*sumptr > (BASE-1)) { + if (*sumptr > (BASE - 1)) { carry = true; *sumptr -= BASE; } else { @@ -120,7 +120,7 @@ bc_num _bc_do_add(bc_num n1, bc_num n2, size_t scale_min) } /* Adjust sum and return. */ - _bc_rm_leading_zeros (sum); + _bc_rm_leading_zeros(sum); return sum; } @@ -132,10 +132,11 @@ bc_num _bc_do_add(bc_num n1, bc_num n2, size_t scale_min) bc_num _bc_do_sub(bc_num n1, bc_num n2, size_t scale_min) { bc_num diff; - int diff_scale, diff_len; + size_t diff_scale, diff_len; size_t min_scale, min_len; + size_t borrow, count; + int val; char *n1ptr, *n2ptr, *diffptr; - int borrow, count, val; /* Allocate temporary storage. */ diff_len = MAX(n1->n_len, n2->n_len); @@ -153,9 +154,9 @@ bc_num _bc_do_sub(bc_num n1, bc_num n2, size_t scale_min) } /* Initialize the subtract. */ - n1ptr = (char *) (n1->n_value + n1->n_len + n1->n_scale -1); - n2ptr = (char *) (n2->n_value + n2->n_len + n2->n_scale -1); - diffptr = (char *) (diff->n_value + diff_len + diff_scale -1); + n1ptr = (char *) (n1->n_value + n1->n_len + n1->n_scale - 1); + n2ptr = (char *) (n2->n_value + n2->n_len + n2->n_scale - 1); + diffptr = (char *) (diff->n_value + diff_len + diff_scale - 1); /* Subtract the numbers. */ borrow = 0; @@ -169,7 +170,7 @@ bc_num _bc_do_sub(bc_num n1, bc_num n2, size_t scale_min) } else { /* n2 has the longer scale */ for (count = n2->n_scale - min_scale; count > 0; count--) { - val = - *n2ptr-- - borrow; + val = -*n2ptr-- - borrow; if (val < 0) { val += BASE; borrow = 1; @@ -192,7 +193,7 @@ bc_num _bc_do_sub(bc_num n1, bc_num n2, size_t scale_min) *diffptr-- = val; } - /* If n1 has more digits then n2, we now do that subtract. */ + /* If n1 has more digits than n2, we now do that subtract. */ if (diff_len != min_len) { for (count = diff_len - min_len; count > 0; count--) { val = *n1ptr-- - borrow; @@ -207,6 +208,6 @@ bc_num _bc_do_sub(bc_num n1, bc_num n2, size_t scale_min) } /* Clean up and return. */ - _bc_rm_leading_zeros (diff); + _bc_rm_leading_zeros(diff); return diff; } diff --git a/ext/bcmath/libbcmath/src/init.c b/ext/bcmath/libbcmath/src/init.c index ec1d082b5fb3b..cef55324689f9 100644 --- a/ext/bcmath/libbcmath/src/init.c +++ b/ext/bcmath/libbcmath/src/init.c @@ -32,22 +32,22 @@ #include "bcmath.h" #include #include +#include #include "zend_alloc.h" /* new_num allocates a number and sets fields to known values. */ bc_num _bc_new_num_ex(size_t length, size_t scale, bool persistent) { - bc_num temp; /* PHP Change: malloc() -> pemalloc(), removed free_list code */ - temp = (bc_num) safe_pemalloc (1, sizeof(bc_struct)+length, scale, persistent); + bc_num temp = (bc_num) safe_pemalloc(1, sizeof(bc_struct) + length, scale, persistent); temp->n_sign = PLUS; temp->n_len = length; temp->n_scale = scale; temp->n_refs = 1; /* PHP Change: malloc() -> pemalloc() */ - temp->n_ptr = (char *) safe_pemalloc (1, length, scale, persistent); + temp->n_ptr = (char *) safe_pemalloc(1, length, scale, persistent); temp->n_value = temp->n_ptr; - memset (temp->n_ptr, 0, length+scale); + memset(temp->n_ptr, 0, length + scale); return temp; } @@ -63,7 +63,7 @@ void _bc_free_num_ex(bc_num *num, bool persistent) if ((*num)->n_refs == 0) { if ((*num)->n_ptr) { /* PHP Change: free() -> pefree(), removed free_list code */ - pefree ((*num)->n_ptr, persistent); + pefree((*num)->n_ptr, persistent); } pefree(*num, persistent); } @@ -75,10 +75,10 @@ void _bc_free_num_ex(bc_num *num, bool persistent) void bc_init_numbers(void) { - BCG(_zero_) = _bc_new_num_ex (1,0,1); - BCG(_one_) = _bc_new_num_ex (1,0,1); + BCG(_zero_) = _bc_new_num_ex(1, 0, 1); + BCG(_one_) = _bc_new_num_ex(1, 0, 1); BCG(_one_)->n_value[0] = 1; - BCG(_two_) = _bc_new_num_ex (1,0,1); + BCG(_two_) = _bc_new_num_ex(1, 0, 1); BCG(_two_)->n_value[0] = 2; } diff --git a/ext/bcmath/libbcmath/src/int2num.c b/ext/bcmath/libbcmath/src/int2num.c index e46ab06e2c7d3..dba972e7f914c 100644 --- a/ext/bcmath/libbcmath/src/int2num.c +++ b/ext/bcmath/libbcmath/src/int2num.c @@ -37,7 +37,7 @@ void bc_int2num(bc_num *num, int val) { char buffer[30]; char *bptr, *vptr; - int ix = 1; + int ix = 1; char neg = 0; /* Sign. */ @@ -55,7 +55,8 @@ void bc_int2num(bc_num *num, int val) while (val != 0) { *bptr++ = val % BASE; val = val / BASE; - ix++; /* Count the digits. */ + /* Count the digits. */ + ix++; } /* Make the number. */ diff --git a/ext/bcmath/libbcmath/src/nearzero.c b/ext/bcmath/libbcmath/src/nearzero.c index 9cecbdf390654..d590dc161bc7c 100644 --- a/ext/bcmath/libbcmath/src/nearzero.c +++ b/ext/bcmath/libbcmath/src/nearzero.c @@ -30,8 +30,8 @@ *************************************************************************/ #include -#include #include "bcmath.h" +#include /* In some places we need to check if the number NUM is almost zero. Specifically, all but the last digit is 0 and the last digit is 1. @@ -53,9 +53,5 @@ bool bc_is_near_zero(bc_num num, size_t scale) count--; } - if (count != 0 && (count != 1 || *--nptr != 1)) { - return false; - } else { - return true; - } + return count == 0 || (count == 1 && *--nptr == 1); } diff --git a/ext/bcmath/libbcmath/src/num2long.c b/ext/bcmath/libbcmath/src/num2long.c index 1e0fca68cd530..09305c35c30bf 100644 --- a/ext/bcmath/libbcmath/src/num2long.c +++ b/ext/bcmath/libbcmath/src/num2long.c @@ -39,16 +39,13 @@ long bc_num2long(bc_num num) { - long val; - char *nptr; - /* Extract the int value, ignore the fraction. */ - val = 0; - nptr = num->n_value; + long val = 0; + const char *nptr = num->n_value; for (size_t index = num->n_len; index > 0; index--) { char n = *nptr++; - if (val > LONG_MAX/BASE) { + if (val > LONG_MAX / BASE) { return 0; } val *= BASE; diff --git a/ext/bcmath/libbcmath/src/num2str.c b/ext/bcmath/libbcmath/src/num2str.c index f5667220e783d..904de2795af31 100644 --- a/ext/bcmath/libbcmath/src/num2str.c +++ b/ext/bcmath/libbcmath/src/num2str.c @@ -38,8 +38,8 @@ zend_string *bc_num2str_ex(bc_num num, size_t scale) { zend_string *str; char *sptr; - char *nptr; - int index, signch; + size_t index; + bool signch; /* Number of sign chars. */ signch = num->n_sign != PLUS && !bc_is_zero_for_scale(num, MIN(num->n_scale, scale)); @@ -55,18 +55,18 @@ zend_string *bc_num2str_ex(bc_num num, size_t scale) if (signch) *sptr++ = '-'; /* Load the whole number. */ - nptr = num->n_value; - for (index=num->n_len; index>0; index--) { + const char *nptr = num->n_value; + for (index = num->n_len; index > 0; index--) { *sptr++ = BCD_CHAR(*nptr++); } /* Now the fraction. */ if (scale > 0) { *sptr++ = '.'; - for (index=0; indexn_scale; index++) { + for (index = 0; index < scale && index < num->n_scale; index++) { *sptr++ = BCD_CHAR(*nptr++); } - for (index = num->n_scale; indexn_scale; index < scale; index++) { *sptr++ = BCD_CHAR(0); } } diff --git a/ext/bcmath/libbcmath/src/output.c b/ext/bcmath/libbcmath/src/output.c index e0fa507bc1b03..47a82d20655e3 100644 --- a/ext/bcmath/libbcmath/src/output.c +++ b/ext/bcmath/libbcmath/src/output.c @@ -31,6 +31,7 @@ #include "bcmath.h" #include +#include #include "zend_alloc.h" @@ -39,7 +40,7 @@ /* This structure is used for saving digits in the conversion process. */ typedef struct stk_rec { - long digit; + long digit; struct stk_rec *next; } stk_rec; @@ -52,93 +53,93 @@ static const char ref_str[] = "0123456789ABCDEF"; non-zero, we must output one space before the number. OUT_CHAR is the actual routine for writing the characters. */ -void bc_out_long (long val, size_t size, bool space, void (*out_char)(char) ) +void bc_out_long(long val, size_t size, bool space, void (*out_char)(char)) { char digits[40]; size_t len, ix; - if (space) (*out_char) (' '); + if (space) (*out_char)(' '); snprintf(digits, sizeof(digits), "%ld", val); len = strlen(digits); while (size > len) { - (*out_char) ('0'); + (*out_char)('0'); size--; } - for (ix=0; ix < len; ix++) { - (*out_char) (digits[ix]); + for (ix = 0; ix < len; ix++) { + (*out_char)(digits[ix]); } } /* Output of a bcd number. NUM is written in base O_BASE using OUT_CHAR as the routine to do the actual output of the characters. */ -void bc_out_num (bc_num num, int o_base, void (*out_char)(char), bool leading_zero) +void bc_out_num(bc_num num, int o_base, void (*out_char)(char), bool leading_zero) { char *nptr; - int index, fdigit; + int index, fdigit; bool pre_space; stk_rec *digits, *temp; bc_num int_part, frac_part, base, cur_dig, t_num, max_o_digit; /* The negative sign if needed. */ - if (num->n_sign == MINUS) (*out_char) ('-'); + if (num->n_sign == MINUS) (*out_char)('-'); /* Output the number. */ - if (bc_is_zero (num)) { - (*out_char) ('0'); + if (bc_is_zero(num)) { + (*out_char)('0'); } else { if (o_base == 10) { /* The number is in base 10, do it the fast way. */ nptr = num->n_value; if (num->n_len > 1 || *nptr != 0) { - for (index=num->n_len; index>0; index--) { - (*out_char) (BCD_CHAR(*nptr++)); + for (index = num->n_len; index > 0; index--) { + (*out_char)(BCD_CHAR(*nptr++)); } } else { nptr++; } if (leading_zero && bc_is_zero(num)) { - (*out_char) ('0'); + (*out_char)('0'); } /* Now the fraction. */ if (num->n_scale > 0) { - (*out_char) ('.'); - for (index=0; indexn_scale; index++) { - (*out_char) (BCD_CHAR(*nptr++)); + (*out_char)('.'); + for (index = 0; index < num->n_scale; index++) { + (*out_char)(BCD_CHAR(*nptr++)); } } } else { /* special case ... */ - if (leading_zero && bc_is_zero (num)) { - (*out_char) ('0'); + if (leading_zero && bc_is_zero(num)) { + (*out_char)('0'); } /* The number is some other base. */ digits = NULL; - bc_init_num (&int_part); - bc_divide (num, BCG(_one_), &int_part, 0); - bc_init_num (&frac_part); - bc_init_num (&cur_dig); - bc_init_num (&base); - bc_sub (num, int_part, &frac_part, 0); + bc_init_num(&int_part); + bc_divide(num, BCG(_one_), &int_part, 0); + bc_init_num(&frac_part); + bc_init_num(&cur_dig); + bc_init_num(&base); + bc_sub(num, int_part, &frac_part, 0); /* Make the INT_PART and FRAC_PART positive. */ int_part->n_sign = PLUS; frac_part->n_sign = PLUS; - bc_int2num (&base, o_base); - bc_init_num (&max_o_digit); - bc_int2num (&max_o_digit, o_base-1); + bc_int2num(&base, o_base); + bc_init_num(&max_o_digit); + bc_int2num(&max_o_digit, o_base - 1); /* Get the digits of the integer part and push them on a stack. */ while (!bc_is_zero(int_part)) { - bc_modulo (int_part, base, &cur_dig, 0); + bc_modulo(int_part, base, &cur_dig, 0); /* PHP Change: malloc() -> emalloc() */ - temp = (stk_rec *) emalloc (sizeof(stk_rec)); - temp->digit = bc_num2long (cur_dig); + temp = (stk_rec *) emalloc(sizeof(stk_rec)); + temp->digit = bc_num2long(cur_dig); temp->next = digits; digits = temp; - bc_divide (int_part, base, &int_part, 0); + bc_divide(int_part, base, &int_part, 0); } /* Print the digits on the stack. */ @@ -148,9 +149,9 @@ void bc_out_num (bc_num num, int o_base, void (*out_char)(char), bool leading_ze temp = digits; digits = digits->next; if (o_base <= 16) { - (*out_char) (ref_str[ (int) temp->digit]); + (*out_char)(ref_str[(int) temp->digit]); } else { - bc_out_long (temp->digit, max_o_digit->n_len, 1, out_char); + bc_out_long(temp->digit, max_o_digit->n_len, 1, out_char); } efree(temp); } @@ -158,21 +159,21 @@ void bc_out_num (bc_num num, int o_base, void (*out_char)(char), bool leading_ze /* Get and print the digits of the fraction part. */ if (num->n_scale > 0) { - (*out_char) ('.'); + (*out_char)('.'); pre_space = false; - t_num = bc_copy_num (BCG(_one_)); + t_num = bc_copy_num(BCG(_one_)); while (t_num->n_len <= num->n_scale) { - bc_multiply (frac_part, base, &frac_part, num->n_scale); - fdigit = bc_num2long (frac_part); - bc_int2num (&int_part, fdigit); - bc_sub (frac_part, int_part, &frac_part, 0); + bc_multiply(frac_part, base, &frac_part, num->n_scale); + fdigit = bc_num2long(frac_part); + bc_int2num(&int_part, fdigit); + bc_sub(frac_part, int_part, &frac_part, 0); if (o_base <= 16) { - (*out_char) (ref_str[fdigit]); + (*out_char)(ref_str[fdigit]); } else { - bc_out_long (fdigit, max_o_digit->n_len, pre_space, out_char); + bc_out_long(fdigit, max_o_digit->n_len, pre_space, out_char); pre_space = true; } - bc_multiply (t_num, base, &t_num, 0); + bc_multiply(t_num, base, &t_num, 0); } bc_free_num (&t_num); } diff --git a/ext/bcmath/libbcmath/src/raise.c b/ext/bcmath/libbcmath/src/raise.c index 4560e4e6916a5..61d64ace94744 100644 --- a/ext/bcmath/libbcmath/src/raise.c +++ b/ext/bcmath/libbcmath/src/raise.c @@ -50,7 +50,7 @@ void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) /* Special case if exponent is a zero. */ if (exponent == 0) { bc_free_num (result); - *result = bc_copy_num (BCG(_one_)); + *result = bc_copy_num(BCG(_one_)); return; } @@ -61,35 +61,35 @@ void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) rscale = scale; } else { is_neg = false; - rscale = MIN (num1->n_scale*exponent, MAX(scale, num1->n_scale)); + rscale = MIN (num1->n_scale * exponent, MAX(scale, num1->n_scale)); } /* Set initial value of temp. */ - power = bc_copy_num (num1); + power = bc_copy_num(num1); pwrscale = num1->n_scale; while ((exponent & 1) == 0) { - pwrscale = 2*pwrscale; - bc_multiply (power, power, &power, pwrscale); + pwrscale = 2 * pwrscale; + bc_multiply(power, power, &power, pwrscale); exponent = exponent >> 1; } - temp = bc_copy_num (power); + temp = bc_copy_num(power); calcscale = pwrscale; exponent = exponent >> 1; /* Do the calculation. */ while (exponent > 0) { - pwrscale = 2*pwrscale; - bc_multiply (power, power, &power, pwrscale); + pwrscale = 2 * pwrscale; + bc_multiply(power, power, &power, pwrscale); if ((exponent & 1) == 1) { calcscale = pwrscale + calcscale; - bc_multiply (temp, power, &temp, calcscale); + bc_multiply(temp, power, &temp, calcscale); } exponent = exponent >> 1; } /* Assign the value. */ if (is_neg) { - bc_divide (BCG(_one_), temp, result, rscale); + bc_divide(BCG(_one_), temp, result, rscale); bc_free_num (&temp); } else { bc_free_num (result); diff --git a/ext/bcmath/libbcmath/src/raisemod.c b/ext/bcmath/libbcmath/src/raisemod.c index badee6fca6faa..93bd4193da45c 100644 --- a/ext/bcmath/libbcmath/src/raisemod.c +++ b/ext/bcmath/libbcmath/src/raisemod.c @@ -59,26 +59,26 @@ raise_mod_status bc_raisemod(bc_num base, bc_num expo, bc_num mod, bc_num *resul } /* Set initial values. */ - power = bc_copy_num (base); - exponent = bc_copy_num (expo); - modulus = bc_copy_num (mod); - temp = bc_copy_num (BCG(_one_)); + power = bc_copy_num(base); + exponent = bc_copy_num(expo); + modulus = bc_copy_num(mod); + temp = bc_copy_num(BCG(_one_)); bc_init_num(&parity); /* Do the calculation. */ rscale = MAX(scale, power->n_scale); - if ( !bc_compare(modulus, BCG(_one_)) ) { + if (!bc_compare(modulus, BCG(_one_))) { bc_free_num (&temp); temp = bc_new_num (1, scale); } else { - while ( !bc_is_zero(exponent) ) { - (void) bc_divmod (exponent, BCG(_two_), &exponent, &parity, 0); - if ( !bc_is_zero(parity) ) { - bc_multiply (temp, power, &temp, rscale); - (void) bc_modulo (temp, modulus, &temp, scale); + while (!bc_is_zero(exponent)) { + (void) bc_divmod(exponent, BCG(_two_), &exponent, &parity, 0); + if (!bc_is_zero(parity)) { + bc_multiply(temp, power, &temp, rscale); + (void) bc_modulo(temp, modulus, &temp, scale); } - bc_multiply (power, power, &power, rscale); - (void) bc_modulo (power, modulus, &power, scale); + bc_multiply(power, power, &power, rscale); + (void) bc_modulo(power, modulus, &power, scale); } } diff --git a/ext/bcmath/libbcmath/src/recmul.c b/ext/bcmath/libbcmath/src/recmul.c index 0a4563d10b204..ec89c461fa188 100644 --- a/ext/bcmath/libbcmath/src/recmul.c +++ b/ext/bcmath/libbcmath/src/recmul.c @@ -50,9 +50,7 @@ int mul_base_digits = MUL_BASE_DIGITS; static bc_num new_sub_num(size_t length, size_t scale, char *value) { - bc_num temp; - - temp = (bc_num) emalloc (sizeof(bc_struct)); + bc_num temp = (bc_num) emalloc(sizeof(bc_struct)); temp->n_sign = PLUS; temp->n_len = length; @@ -66,22 +64,21 @@ static bc_num new_sub_num(size_t length, size_t scale, char *value) static void _bc_simp_mul(bc_num n1, size_t n1len, bc_num n2, int n2len, bc_num *prod) { char *n1ptr, *n2ptr, *pvptr; - char *n1end, *n2end; /* To the end of n1 and n2. */ - int indx, sum, prodlen; + char *n1end, *n2end; /* To the end of n1 and n2. */ + int sum = 0; - prodlen = n1len+n2len+1; + int prodlen = n1len + n2len + 1; *prod = bc_new_num (prodlen, 0); n1end = (char *) (n1->n_value + n1len - 1); n2end = (char *) (n2->n_value + n2len - 1); pvptr = (char *) ((*prod)->n_value + prodlen - 1); - sum = 0; /* Here is the loop... */ - for (indx = 0; indx < prodlen-1; indx++) { - n1ptr = (char *) (n1end - MAX(0, indx-n2len+1)); - n2ptr = (char *) (n2end - MIN(indx, n2len-1)); + for (int index = 0; index < prodlen - 1; index++) { + n1ptr = (char *) (n1end - MAX(0, index - n2len + 1)); + n2ptr = (char *) (n2end - MIN(index, n2len - 1)); while ((n1ptr >= n1->n_value) && (n2ptr <= n2end)) { sum += *n1ptr * *n2ptr; n1ptr--; @@ -98,7 +95,7 @@ static void _bc_simp_mul(bc_num n1, size_t n1len, bc_num n2, int n2len, bc_num * multiply algorithm. Note: if sub is called, accum must be larger that what is being subtracted. Also, accum and val must have n_scale = 0. (e.g. they must look like integers. *) */ -static void _bc_shift_addsub (bc_num accum, bc_num val, int shift, bool sub) +static void _bc_shift_addsub(bc_num accum, bc_num val, int shift, bool sub) { signed char *accp, *valp; unsigned int carry = 0; @@ -107,11 +104,11 @@ static void _bc_shift_addsub (bc_num accum, bc_num val, int shift, bool sub) if (val->n_value[0] == 0) { count--; } - assert(accum->n_len+accum->n_scale >= shift+count); + assert(accum->n_len + accum->n_scale >= shift + count); /* Set up pointers and others */ - accp = (signed char *)(accum->n_value + accum->n_len + accum->n_scale - shift - 1); - valp = (signed char *)(val->n_value + val->n_len - 1); + accp = (signed char *) (accum->n_value + accum->n_len + accum->n_scale - shift - 1); + valp = (signed char *) (val->n_value + val->n_len - 1); if (sub) { /* Subtraction, carry is really borrow. */ @@ -137,7 +134,7 @@ static void _bc_shift_addsub (bc_num accum, bc_num val, int shift, bool sub) /* Addition */ while (count--) { *accp += *valp-- + carry; - if (*accp > (BASE-1)) { + if (*accp > (BASE - 1)) { carry = 1; *accp-- -= BASE; } else { @@ -147,7 +144,7 @@ static void _bc_shift_addsub (bc_num accum, bc_num val, int shift, bool sub) } while (carry) { *accp += carry; - if (*accp > (BASE-1)) { + if (*accp > (BASE - 1)) { *accp-- -= BASE; } else { carry = 0; @@ -168,40 +165,40 @@ static void _bc_rec_mul(bc_num u, size_t ulen, bc_num v, size_t vlen, bc_num *pr { bc_num u0, u1, v0, v1; bc_num m1, m2, m3, d1, d2; - int n, prodlen, m1zero; - int d1len, d2len; + size_t n; + bool m1zero; /* Base case? */ - if ((ulen+vlen) < mul_base_digits + if ((ulen + vlen) < mul_base_digits || ulen < MUL_SMALL_DIGITS || vlen < MUL_SMALL_DIGITS ) { - _bc_simp_mul (u, ulen, v, vlen, prod); + _bc_simp_mul(u, ulen, v, vlen, prod); return; } /* Calculate n -- the u and v split point in digits. */ - n = (MAX(ulen, vlen)+1) / 2; + n = (MAX(ulen, vlen) + 1) / 2; /* Split u and v. */ if (ulen < n) { - u1 = bc_copy_num (BCG(_zero_)); - u0 = new_sub_num (ulen,0, u->n_value); + u1 = bc_copy_num(BCG(_zero_)); + u0 = new_sub_num(ulen, 0, u->n_value); } else { - u1 = new_sub_num (ulen-n, 0, u->n_value); - u0 = new_sub_num (n, 0, u->n_value+ulen-n); + u1 = new_sub_num(ulen - n, 0, u->n_value); + u0 = new_sub_num(n, 0, u->n_value + ulen - n); } if (vlen < n) { - v1 = bc_copy_num (BCG(_zero_)); - v0 = new_sub_num (vlen,0, v->n_value); + v1 = bc_copy_num(BCG(_zero_)); + v0 = new_sub_num(vlen, 0, v->n_value); } else { - v1 = new_sub_num (vlen-n, 0, v->n_value); - v0 = new_sub_num (n, 0, v->n_value+vlen-n); + v1 = new_sub_num(vlen - n, 0, v->n_value); + v0 = new_sub_num(n, 0, v->n_value + vlen - n); } - _bc_rm_leading_zeros (u1); - _bc_rm_leading_zeros (u0); - _bc_rm_leading_zeros (v1); - _bc_rm_leading_zeros (v0); + _bc_rm_leading_zeros(u1); + _bc_rm_leading_zeros(u0); + _bc_rm_leading_zeros(v1); + _bc_rm_leading_zeros(v0); m1zero = bc_is_zero(u1) || bc_is_zero(v1); @@ -209,42 +206,39 @@ static void _bc_rec_mul(bc_num u, size_t ulen, bc_num v, size_t vlen, bc_num *pr bc_init_num(&d1); bc_init_num(&d2); - bc_sub (u1, u0, &d1, 0); - d1len = d1->n_len; - bc_sub (v0, v1, &d2, 0); - d2len = d2->n_len; + bc_sub(u1, u0, &d1, 0); + bc_sub(v0, v1, &d2, 0); /* Do recursive multiplies and shifted adds. */ if (m1zero) { - m1 = bc_copy_num (BCG(_zero_)); + m1 = bc_copy_num(BCG(_zero_)); } else { - _bc_rec_mul (u1, u1->n_len, v1, v1->n_len, &m1); + _bc_rec_mul(u1, u1->n_len, v1, v1->n_len, &m1); } if (bc_is_zero(d1) || bc_is_zero(d2)) { - m2 = bc_copy_num (BCG(_zero_)); + m2 = bc_copy_num(BCG(_zero_)); } else { - _bc_rec_mul (d1, d1len, d2, d2len, &m2); + _bc_rec_mul(d1, d1->n_len, d2, d2->n_len, &m2); } if (bc_is_zero(u0) || bc_is_zero(v0)) { - m3 = bc_copy_num (BCG(_zero_)); + m3 = bc_copy_num(BCG(_zero_)); } else { - _bc_rec_mul (u0, u0->n_len, v0, v0->n_len, &m3); + _bc_rec_mul(u0, u0->n_len, v0, v0->n_len, &m3); } /* Initialize product */ - prodlen = ulen+vlen+1; - *prod = bc_new_num(prodlen, 0); + *prod = bc_new_num(ulen + vlen + 1, 0); if (!m1zero) { - _bc_shift_addsub (*prod, m1, 2*n, 0); - _bc_shift_addsub (*prod, m1, n, 0); + _bc_shift_addsub(*prod, m1, 2 * n, false); + _bc_shift_addsub(*prod, m1, n, false); } - _bc_shift_addsub (*prod, m3, n, 0); - _bc_shift_addsub (*prod, m3, 0, 0); - _bc_shift_addsub (*prod, m2, n, d1->n_sign != d2->n_sign); + _bc_shift_addsub(*prod, m3, n, false); + _bc_shift_addsub(*prod, m3, 0, false); + _bc_shift_addsub(*prod, m2, n, d1->n_sign != d2->n_sign); /* Now clean up! */ bc_free_num (&u1); @@ -272,13 +266,13 @@ void bc_multiply(bc_num n1, bc_num n2, bc_num *prod, size_t scale) len1 = n1->n_len + n1->n_scale; len2 = n2->n_len + n2->n_scale; full_scale = n1->n_scale + n2->n_scale; - prod_scale = MIN(full_scale,MAX(scale,MAX(n1->n_scale,n2->n_scale))); + prod_scale = MIN(full_scale, MAX(scale, MAX(n1->n_scale, n2->n_scale))); /* Do the multiply */ - _bc_rec_mul (n1, len1, n2, len2, &pval); + _bc_rec_mul(n1, len1, n2, len2, &pval); /* Assign to prod and clean up the number. */ - pval->n_sign = ( n1->n_sign == n2->n_sign ? PLUS : MINUS ); + pval->n_sign = (n1->n_sign == n2->n_sign ? PLUS : MINUS); pval->n_value = pval->n_ptr; pval->n_len = len2 + len1 + 1 - full_scale; pval->n_scale = prod_scale; diff --git a/ext/bcmath/libbcmath/src/rmzero.c b/ext/bcmath/libbcmath/src/rmzero.c index c2e749bec43a8..8228e54f4aa57 100644 --- a/ext/bcmath/libbcmath/src/rmzero.c +++ b/ext/bcmath/libbcmath/src/rmzero.c @@ -38,7 +38,7 @@ void _bc_rm_leading_zeros(bc_num num) { - /* We can move n_value to point to the first non zero digit! */ + /* We can move n_value to point to the first non-zero digit! */ while (*num->n_value == 0 && num->n_len > 1) { num->n_value++; num->n_len--; diff --git a/ext/bcmath/libbcmath/src/sqrt.c b/ext/bcmath/libbcmath/src/sqrt.c index d28838b155358..f29f9ce484bad 100644 --- a/ext/bcmath/libbcmath/src/sqrt.c +++ b/ext/bcmath/libbcmath/src/sqrt.c @@ -30,8 +30,8 @@ *************************************************************************/ #include "bcmath.h" -#include #include +#include /* Take the square root NUM and return it in NUM with SCALE digits after the decimal place. */ @@ -39,20 +39,20 @@ bool bc_sqrt(bc_num *num, size_t scale) { /* Initial checks. */ - int cmp_res = bc_compare (*num, BCG(_zero_)); + int cmp_res = bc_compare(*num, BCG(_zero_)); if (cmp_res < 0) { return false; /* error */ } else { if (cmp_res == 0) { bc_free_num (num); - *num = bc_copy_num (BCG(_zero_)); + *num = bc_copy_num(BCG(_zero_)); return true; } } - cmp_res = bc_compare (*num, BCG(_one_)); + cmp_res = bc_compare(*num, BCG(_one_)); if (cmp_res == 0) { bc_free_num (num); - *num = bc_copy_num (BCG(_one_)); + *num = bc_copy_num(BCG(_one_)); return true; } @@ -64,22 +64,22 @@ bool bc_sqrt(bc_num *num, size_t scale) rscale = MAX (scale, (*num)->n_scale); bc_init_num(&guess1); bc_init_num(&diff); - point5 = bc_new_num (1,1); + point5 = bc_new_num (1, 1); point5->n_value[1] = 5; /* Calculate the initial guess. */ if (cmp_res < 0) { /* The number is between 0 and 1. Guess should start at 1. */ - guess = bc_copy_num (BCG(_one_)); + guess = bc_copy_num(BCG(_one_)); cscale = (*num)->n_scale; } else { /* The number is greater than 1. Guess should start at 10^(exp/2). */ bc_init_num(&guess); - bc_int2num (&guess,10); + bc_int2num(&guess, 10); - bc_int2num (&guess1,(*num)->n_len); - bc_multiply (guess1, point5, &guess1, 0); + bc_int2num(&guess1, (*num)->n_len); + bc_multiply(guess1, point5, &guess1, 0); guess1->n_scale = 0; bc_raise_bc_exponent(guess, guess1, &guess, 0); bc_free_num (&guess1); @@ -90,14 +90,14 @@ bool bc_sqrt(bc_num *num, size_t scale) bool done = false; while (!done) { bc_free_num (&guess1); - guess1 = bc_copy_num (guess); - bc_divide (*num, guess, &guess, cscale); - bc_add (guess, guess1, &guess, 0); - bc_multiply (guess, point5, &guess, cscale); - bc_sub (guess, guess1, &diff, cscale+1); - if (bc_is_near_zero (diff, cscale)) { - if (cscale < rscale+1) { - cscale = MIN (cscale*3, rscale+1); + guess1 = bc_copy_num(guess); + bc_divide(*num, guess, &guess, cscale); + bc_add(guess, guess1, &guess, 0); + bc_multiply(guess, point5, &guess, cscale); + bc_sub(guess, guess1, &diff, cscale + 1); + if (bc_is_near_zero(diff, cscale)) { + if (cscale < rscale + 1) { + cscale = MIN (cscale * 3, rscale + 1); } else { done = true; } @@ -106,7 +106,7 @@ bool bc_sqrt(bc_num *num, size_t scale) /* Assign the number and clean up. */ bc_free_num (num); - bc_divide (guess,BCG(_one_),num,rscale); + bc_divide(guess, BCG(_one_), num, rscale); bc_free_num (&guess); bc_free_num (&guess1); bc_free_num (&point5); diff --git a/ext/bcmath/libbcmath/src/str2num.c b/ext/bcmath/libbcmath/src/str2num.c index 7bba9a9f6c149..cc876285d59c0 100644 --- a/ext/bcmath/libbcmath/src/str2num.c +++ b/ext/bcmath/libbcmath/src/str2num.c @@ -35,22 +35,21 @@ /* Convert strings to bc numbers. Base 10 only.*/ -bool bc_str2num (bc_num *num, char *str, size_t scale) +bool bc_str2num(bc_num *num, char *str, size_t scale) { - size_t digits, strscale; + size_t digits = 0; + size_t strscale = 0; char *ptr, *nptr; - bool zero_int = false; size_t trailing_zeros = 0; + bool zero_int = false; /* Prepare num. */ bc_free_num (num); /* Check for valid number and count digits. */ ptr = str; - digits = 0; - strscale = 0; - if ( (*ptr == '+') || (*ptr == '-')) { + if ((*ptr == '+') || (*ptr == '-')) { /* Skip Sign */ ptr++; } @@ -79,11 +78,11 @@ bool bc_str2num (bc_num *num, char *str, size_t scale) } if (trailing_zeros > 0) { - /* Trailining zeros should not take part in the computation of the overall scale, as it is pointless. */ + /* Trailing zeros should not take part in the computation of the overall scale, as it is pointless. */ strscale = strscale - trailing_zeros; } - if ((*ptr != '\0') || (digits+strscale == 0)) { - *num = bc_copy_num (BCG(_zero_)); + if ((*ptr != '\0') || (digits + strscale == 0)) { + *num = bc_copy_num(BCG(_zero_)); return *ptr == '\0'; } @@ -113,7 +112,7 @@ bool bc_str2num (bc_num *num, char *str, size_t scale) *nptr++ = 0; digits = 0; } - for (;digits > 0; digits--) { + for (; digits > 0; digits--) { *nptr++ = CH_VAL(*ptr++); } @@ -121,12 +120,12 @@ bool bc_str2num (bc_num *num, char *str, size_t scale) if (strscale > 0) { /* skip the decimal point! */ ptr++; - for (;strscale > 0; strscale--) { + for (; strscale > 0; strscale--) { *nptr++ = CH_VAL(*ptr++); } } - if (bc_is_zero (*num)) { + if (bc_is_zero(*num)) { (*num)->n_sign = PLUS; } diff --git a/ext/bcmath/libbcmath/src/sub.c b/ext/bcmath/libbcmath/src/sub.c index 4907ae5bcecdb..d90f1a8858f29 100644 --- a/ext/bcmath/libbcmath/src/sub.c +++ b/ext/bcmath/libbcmath/src/sub.c @@ -33,6 +33,7 @@ #include "private.h" #include #include +#include /* Here is the full subtract routine that takes care of negative numbers. N2 is subtracted from N1 and the result placed in RESULT. SCALE_MIN @@ -41,31 +42,29 @@ void bc_sub(bc_num n1, bc_num n2, bc_num *result, size_t scale_min) { bc_num diff = NULL; - int cmp_res; if (n1->n_sign != n2->n_sign) { - diff = _bc_do_add (n1, n2, scale_min); + diff = _bc_do_add(n1, n2, scale_min); diff->n_sign = n1->n_sign; } else { /* subtraction must be done. */ /* Compare magnitudes. */ - cmp_res = _bc_do_compare(n1, n2, false, false); - switch (cmp_res) { + switch (_bc_do_compare(n1, n2, false, false)) { case -1: /* n1 is less than n2, subtract n1 from n2. */ - diff = _bc_do_sub (n2, n1, scale_min); + diff = _bc_do_sub(n2, n1, scale_min); diff->n_sign = (n2->n_sign == PLUS ? MINUS : PLUS); break; - case 0: { + case 0: { /* They are equal! return zero! */ size_t res_scale = MAX (scale_min, MAX(n1->n_scale, n2->n_scale)); diff = bc_new_num (1, res_scale); - memset (diff->n_value, 0, res_scale+1); + memset(diff->n_value, 0, res_scale + 1); break; } - case 1: + case 1: /* n2 is less than n1, subtract n2 from n1. */ - diff = _bc_do_sub (n1, n2, scale_min); + diff = _bc_do_sub(n1, n2, scale_min); diff->n_sign = n1->n_sign; break; } diff --git a/ext/bcmath/libbcmath/src/zero.c b/ext/bcmath/libbcmath/src/zero.c index edcd188acb8db..550ea97df6743 100644 --- a/ext/bcmath/libbcmath/src/zero.c +++ b/ext/bcmath/libbcmath/src/zero.c @@ -30,12 +30,12 @@ *************************************************************************/ #include "bcmath.h" -#include #include +#include /* In some places we need to check if the number NUM is zero. */ -bool bc_is_zero_for_scale (bc_num num, size_t scale) +bool bc_is_zero_for_scale(bc_num num, size_t scale) { size_t count; char *nptr; From 2196e2299f2ff60f776818cd4fb7820a0ed81ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristian=20Rodr=C3=ADguez?= Date: Sun, 13 Aug 2023 18:51:14 -0400 Subject: [PATCH 04/47] Use zend_ast_size consistenly (#11955) * opcache: use zend_ast_size helper in zend_persist_ast * opcache: use zend_ast_size helper in zend_persist_ast_calc * Zend: fix zend_ast_size definition It is better not to use sizeof(struct_with_flexible_array) and instead rely on offsetof(type, member) like most other similar wrappers do. --- Zend/zend_ast.h | 2 +- ext/opcache/zend_persist.c | 2 +- ext/opcache/zend_persist_calc.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 0bbb3a820c291..4c1a87e288a73 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -313,7 +313,7 @@ typedef void (*zend_ast_apply_func)(zend_ast **ast_ptr, void *context); ZEND_API void zend_ast_apply(zend_ast *ast, zend_ast_apply_func fn, void *context); static zend_always_inline size_t zend_ast_size(uint32_t children) { - return sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children; + return XtOffsetOf(zend_ast, child) + (sizeof(zend_ast *) * children); } static zend_always_inline bool zend_ast_is_special(zend_ast *ast) { diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index c8330c1e79057..b10e5a8046ab1 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -188,7 +188,7 @@ static zend_ast *zend_persist_ast(zend_ast *ast) node = (zend_ast *) copy; } else { uint32_t children = zend_ast_get_num_children(ast); - node = zend_shared_memdup(ast, sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children); + node = zend_shared_memdup(ast, zend_ast_size(children)); for (i = 0; i < children; i++) { if (node->child[i]) { node->child[i] = zend_persist_ast(node->child[i]); diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index dfc281eb7f6f7..2d4a3c92afa29 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -87,7 +87,7 @@ static void zend_persist_ast_calc(zend_ast *ast) } } else { uint32_t children = zend_ast_get_num_children(ast); - ADD_SIZE(sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children); + ADD_SIZE(zend_ast_size(children)); for (i = 0; i < children; i++) { if (ast->child[i]) { zend_persist_ast_calc(ast->child[i]); From ee000ea186fcc312411a42df3832ca32057b0ad9 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 8 Aug 2023 11:33:51 +0200 Subject: [PATCH 05/47] Fix uouv on oom on object allocation We may OOM during object initialization. In this case, free_obj needs to guard against NULL values. There may be more cases where this is an issue, these were the ones I was able to discover via script. Fixes GH-11734 --- NEWS | 2 ++ Zend/tests/new_oom.inc | 15 +++++++++++++++ Zend/tests/new_oom.phpt | 24 ++++++++++++++++++++++++ ext/pdo/pdo_dbh.c | 6 ++++++ ext/spl/spl_dllist.c | 11 ++++++----- ext/spl/spl_heap.c | 5 +++++ ext/xsl/php_xsl.c | 12 ++++++++---- 7 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 Zend/tests/new_oom.inc create mode 100644 Zend/tests/new_oom.phpt diff --git a/NEWS b/NEWS index 1d1eedd249ac0..2018d967812c8 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,8 @@ PHP NEWS - Core: . Fixed strerror_r detection at configuration time. (Kévin Dunglas) + . Fixed segfault during freeing of some incompletely initialized objects due + to OOM error (PDO, SPL, XSL). (ilutov) - DOM: . adoptNode now respects the strict error checking property. (nielsdos) diff --git a/Zend/tests/new_oom.inc b/Zend/tests/new_oom.inc new file mode 100644 index 0000000000000..fa62764310a94 --- /dev/null +++ b/Zend/tests/new_oom.inc @@ -0,0 +1,15 @@ +newInstanceWithoutConstructor(); + } +} catch (Throwable) { +} diff --git a/Zend/tests/new_oom.phpt b/Zend/tests/new_oom.phpt new file mode 100644 index 0000000000000..1e9b0593e6bea --- /dev/null +++ b/Zend/tests/new_oom.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test OOM on new of each class +--SKIPIF-- + +--FILE-- +&1"); + if ($output && preg_match('(^\nFatal error: Allowed memory size of [0-9]+ bytes exhausted[^\r\n]* \(tried to allocate [0-9]+ bytes\) in [^\r\n]+ on line [0-9]+$)', $output) !== 1) { + echo "Class $class failed\n"; + echo $output, "\n"; + } +} + +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 833afa0d0f579..ce9c7967e7271 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -1414,6 +1414,12 @@ static void dbh_free(pdo_dbh_t *dbh, bool free_persistent) static void pdo_dbh_free_storage(zend_object *std) { pdo_dbh_t *dbh = php_pdo_dbh_fetch_inner(std); + + /* dbh might be null if we OOMed during object initialization. */ + if (!dbh) { + return; + } + if (dbh->driver_data && dbh->methods && dbh->methods->rollback && pdo_is_in_transaction(dbh)) { dbh->methods->rollback(dbh); dbh->in_txn = false; diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c index 74dc7731fd152..74dc3b8c99fa1 100644 --- a/ext/spl/spl_dllist.c +++ b/ext/spl/spl_dllist.c @@ -295,12 +295,13 @@ static void spl_dllist_object_free_storage(zend_object *object) /* {{{ */ zend_object_std_dtor(&intern->std); - while (intern->llist->count > 0) { - spl_ptr_llist_pop(intern->llist, &tmp); - zval_ptr_dtor(&tmp); + if (intern->llist) { + while (intern->llist->count > 0) { + spl_ptr_llist_pop(intern->llist, &tmp); + zval_ptr_dtor(&tmp); + } + spl_ptr_llist_destroy(intern->llist); } - - spl_ptr_llist_destroy(intern->llist); SPL_LLIST_CHECK_DELREF(intern->traverse_pointer); } /* }}} */ diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c index 4f242d3a3c394..5d61741425185 100644 --- a/ext/spl/spl_heap.c +++ b/ext/spl/spl_heap.c @@ -372,6 +372,11 @@ static spl_ptr_heap *spl_ptr_heap_clone(spl_ptr_heap *from) { /* {{{ */ /* }}} */ static void spl_ptr_heap_destroy(spl_ptr_heap *heap) { /* {{{ */ + /* Heap might be null if we OOMed during object initialization. */ + if (!heap) { + return; + } + int i; for (i = 0; i < heap->count; ++i) { diff --git a/ext/xsl/php_xsl.c b/ext/xsl/php_xsl.c index 82e782d18f9d1..5ecb7fd9b0b68 100644 --- a/ext/xsl/php_xsl.c +++ b/ext/xsl/php_xsl.c @@ -59,11 +59,15 @@ void xsl_objects_free_storage(zend_object *object) zend_object_std_dtor(&intern->std); - zend_hash_destroy(intern->parameter); - FREE_HASHTABLE(intern->parameter); + if (intern->parameter) { + zend_hash_destroy(intern->parameter); + FREE_HASHTABLE(intern->parameter); + } - zend_hash_destroy(intern->registered_phpfunctions); - FREE_HASHTABLE(intern->registered_phpfunctions); + if (intern->registered_phpfunctions) { + zend_hash_destroy(intern->registered_phpfunctions); + FREE_HASHTABLE(intern->registered_phpfunctions); + } if (intern->node_list) { zend_hash_destroy(intern->node_list); From 902d39d57cd4451f7b8a23d55452ce8dbb4d7259 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 14 Aug 2023 11:38:59 +0200 Subject: [PATCH 06/47] Use per-branch matrix for windows nightly I forgot this in the last PR. --- .github/nightly_matrix.php | 21 +++++++++++++++++++++ .github/workflows/nightly.yml | 15 +++++++-------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/.github/nightly_matrix.php b/.github/nightly_matrix.php index fdd5860f53923..868ecb41375c5 100644 --- a/.github/nightly_matrix.php +++ b/.github/nightly_matrix.php @@ -78,6 +78,25 @@ function get_matrix_include(array $branches) { return $jobs; } +function get_windows_matrix_include(array $branches) { + $jobs = []; + foreach ($branches as $branch) { + $jobs[] = [ + 'branch' => $branch, + 'x64' => true, + 'zts' => true, + 'opcache' => true, + ]; + $jobs[] = [ + 'branch' => $branch, + 'x64' => false, + 'zts' => false, + 'opcache' => false, + ]; + } + return $jobs; +} + $trigger = $argv[1] ?? 'schedule'; $attempt = (int) ($argv[2] ?? 1); $discard_cache = ($trigger === 'schedule' && $attempt !== 1) || $trigger === 'workflow_dispatch'; @@ -87,6 +106,8 @@ function get_matrix_include(array $branches) { $branches = get_branches(); $matrix_include = get_matrix_include($branches); +$windows_matrix_include = get_windows_matrix_include($branches); echo '::set-output name=branches::' . json_encode($branches, JSON_UNESCAPED_SLASHES) . "\n"; echo '::set-output name=matrix-include::' . json_encode($matrix_include, JSON_UNESCAPED_SLASHES) . "\n"; +echo '::set-output name=windows-matrix-include::' . json_encode($windows_matrix_include, JSON_UNESCAPED_SLASHES) . "\n"; diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index e098c904e3d23..3e8ce86f9dda9 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -11,6 +11,7 @@ jobs: outputs: branches: ${{ steps.set-matrix.outputs.branches }} matrix-include: ${{ steps.set-matrix.outputs.matrix-include }} + windows-matrix-include: ${{ steps.set-matrix.outputs.windows-matrix-include }} steps: - uses: actions/checkout@v3 with: @@ -646,17 +647,13 @@ jobs: with: token: ${{ secrets.ACTION_MONITORING_SLACK }} WINDOWS: + needs: GENERATE_MATRIX + if: ${{ needs.GENERATE_MATRIX.outputs.branches != '[]' }} strategy: fail-fast: false matrix: - include: - - x64: true - zts: true - opcache: true - - x64: false - zts: false - opcache: false - name: "WINDOWS_${{ matrix.x64 && 'X64' || 'X86' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}" + include: ${{ fromJson(needs.GENERATE_MATRIX.outputs.windows-matrix-include) }} + name: "${{ matrix.branch.name }}_WINDOWS_${{ matrix.x64 && 'X64' || 'X86' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}" runs-on: windows-2019 env: PHP_BUILD_CACHE_BASE_DIR: C:\build-cache @@ -674,6 +671,8 @@ jobs: run: git config --global core.autocrlf false && git config --global core.eol lf - name: git checkout uses: actions/checkout@v3 + with: + ref: ${{ matrix.branch.ref }} - name: Setup uses: ./.github/actions/setup-windows - name: Build From 67ab2b7d870d74a83ba13b935e298ace7ea7abfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Mon, 14 Aug 2023 12:42:55 +0200 Subject: [PATCH 07/47] Align the return type of snmp_set_oid_numeric_print() to its aliased funtion --- ext/snmp/snmp.stub.php | 2 +- ext/snmp/snmp_arginfo.h | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/ext/snmp/snmp.stub.php b/ext/snmp/snmp.stub.php index 9f4557e6f8fac..87496c936fe4d 100644 --- a/ext/snmp/snmp.stub.php +++ b/ext/snmp/snmp.stub.php @@ -132,7 +132,7 @@ function snmp_set_enum_print(bool $enable): true {} function snmp_set_oid_output_format(int $format): true {} /** @alias snmp_set_oid_output_format */ -function snmp_set_oid_numeric_print(int $format): bool {} +function snmp_set_oid_numeric_print(int $format): true {} function snmp2_get(string $hostname, string $community, array|string $object_id, int $timeout = -1, int $retries = -1): mixed {} diff --git a/ext/snmp/snmp_arginfo.h b/ext/snmp/snmp_arginfo.h index a268cd6e5d5ef..f19b91f4ec54d 100644 --- a/ext/snmp/snmp_arginfo.h +++ b/ext/snmp/snmp_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: ada00ea99d91d7e48e9965c5891227f97fd779a6 */ + * Stub hash: 449ae0af39f24f3e5696b88a30d2a440628c409b */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_snmpget, 0, 3, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, hostname, IS_STRING, 0) @@ -46,9 +46,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_snmp_set_oid_output_format, 0, 1 ZEND_ARG_TYPE_INFO(0, format, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_snmp_set_oid_numeric_print, 0, 1, _IS_BOOL, 0) - ZEND_ARG_TYPE_INFO(0, format, IS_LONG, 0) -ZEND_END_ARG_INFO() +#define arginfo_snmp_set_oid_numeric_print arginfo_snmp_set_oid_output_format #define arginfo_snmp2_get arginfo_snmpget From 17b3af29589ef5667c44dae7aaa11f1018e6949b Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 14 Aug 2023 13:23:40 +0200 Subject: [PATCH 08/47] GH-11964: In ext/date/php_date.stub.php, DateRangeError extends itself According to the RFC[1] it's supposed to extend DateError. [1] https://wiki.php.net/rfc/datetime-exceptions --- ext/date/php_date.stub.php | 2 +- ext/date/php_date_arginfo.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/date/php_date.stub.php b/ext/date/php_date.stub.php index 5105bdb320a05..d987e80b96b84 100644 --- a/ext/date/php_date.stub.php +++ b/ext/date/php_date.stub.php @@ -766,7 +766,7 @@ class DateObjectError extends DateError /** * @strict-properties */ -class DateRangeError extends DateRangeError +class DateRangeError extends DateError { } diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h index 311d035026e47..eb298575296f7 100644 --- a/ext/date/php_date_arginfo.h +++ b/ext/date/php_date_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 30e6ffeefc0c0ecd66f3f40c9211277ce02d7187 */ + * Stub hash: b967c9bbe6c73f56432473020a84919d7e953d4c */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_strtotime, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) @@ -1161,12 +1161,12 @@ static zend_class_entry *register_class_DateObjectError(zend_class_entry *class_ return class_entry; } -static zend_class_entry *register_class_DateRangeError(zend_class_entry *class_entry_DateRangeError) +static zend_class_entry *register_class_DateRangeError(zend_class_entry *class_entry_DateError) { zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "DateRangeError", class_DateRangeError_methods); - class_entry = zend_register_internal_class_ex(&ce, class_entry_DateRangeError); + class_entry = zend_register_internal_class_ex(&ce, class_entry_DateError); class_entry->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES; return class_entry; From 2012fd3f044a0a10c2ec6ffde823c84571dfb07f Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Fri, 21 Apr 2023 06:33:28 +0100 Subject: [PATCH 09/47] ci update freebsd image to the 13.2 image (#11110) --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index d29ed7e103a26..b3cc9d61f1d68 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -4,7 +4,7 @@ env: freebsd_task: name: FREEBSD_DEBUG_NTS freebsd_instance: - image_family: freebsd-13-0 + image_family: freebsd-13-2 env: ARCH: amd64 install_script: From 27fb8d1c0c17690c8284eab2a3a671920100ada2 Mon Sep 17 00:00:00 2001 From: Ayesh Karunaratne Date: Tue, 15 Aug 2023 00:55:38 +0800 Subject: [PATCH 10/47] Fix DateTime exception hierarchy for DateInvalidTimeZoneException (#11970) `DateInvalidTimeZoneException` is supposed to inherit from `DateException`, but the current stub has `Exception`. --- ext/date/php_date.stub.php | 2 +- ext/date/php_date_arginfo.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/date/php_date.stub.php b/ext/date/php_date.stub.php index d987e80b96b84..e0717fa7f4c85 100644 --- a/ext/date/php_date.stub.php +++ b/ext/date/php_date.stub.php @@ -780,7 +780,7 @@ class DateException extends Exception /** * @strict-properties */ -class DateInvalidTimeZoneException extends Exception +class DateInvalidTimeZoneException extends DateException { } diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h index eb298575296f7..bbede1f39596e 100644 --- a/ext/date/php_date_arginfo.h +++ b/ext/date/php_date_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: b967c9bbe6c73f56432473020a84919d7e953d4c */ + * Stub hash: 0f204ac6646be79b515189a384fce9bcea9a4f42 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_strtotime, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) @@ -1183,12 +1183,12 @@ static zend_class_entry *register_class_DateException(zend_class_entry *class_en return class_entry; } -static zend_class_entry *register_class_DateInvalidTimeZoneException(zend_class_entry *class_entry_Exception) +static zend_class_entry *register_class_DateInvalidTimeZoneException(zend_class_entry *class_entry_DateException) { zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "DateInvalidTimeZoneException", class_DateInvalidTimeZoneException_methods); - class_entry = zend_register_internal_class_ex(&ce, class_entry_Exception); + class_entry = zend_register_internal_class_ex(&ce, class_entry_DateException); class_entry->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES; return class_entry; From 0b887042aeb03a3a3a6ea216d1deaa333c7e5018 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Mon, 14 Aug 2023 19:04:34 +0100 Subject: [PATCH 11/47] mysqli_field_seek return type changed to true (#11948) --- UPGRADING | 2 ++ ext/mysqli/mysqli.stub.php | 4 ++-- ext/mysqli/mysqli_arginfo.h | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/UPGRADING b/UPGRADING index 75536b00f4a54..95f7a24ebc767 100644 --- a/UPGRADING +++ b/UPGRADING @@ -289,6 +289,8 @@ PHP 8.3 UPGRADE NOTES constructor. . mysqli_poll now raises a ValueError when the read nor error arguments are passed. + . mysqli_field_seek and mysqli_result::field_seek now specify return type + as true instead of bool. - ODBC . odbc_autocommit() now accepts null for the $enable parameter. diff --git a/ext/mysqli/mysqli.stub.php b/ext/mysqli/mysqli.stub.php index bb39ab5bbd147..4fd55442b7447 100644 --- a/ext/mysqli/mysqli.stub.php +++ b/ext/mysqli/mysqli.stub.php @@ -1144,7 +1144,7 @@ public function fetch_column(int $column = 0): null|int|float|string|false {} * @tentative-return-type * @alias mysqli_field_seek */ - public function field_seek(int $index): bool {} + public function field_seek(int $index): true {} // TODO make return type void /** * @tentative-return-type @@ -1457,7 +1457,7 @@ function mysqli_fetch_column(mysqli_result $result, int $column = 0): null|int|f function mysqli_field_count(mysqli $mysql): int {} -function mysqli_field_seek(mysqli_result $result, int $index): bool {} +function mysqli_field_seek(mysqli_result $result, int $index): true {} // TODO make return type void function mysqli_field_tell(mysqli_result $result): int {} diff --git a/ext/mysqli/mysqli_arginfo.h b/ext/mysqli/mysqli_arginfo.h index eacfc83a67103..ee4a2013809e9 100644 --- a/ext/mysqli/mysqli_arginfo.h +++ b/ext/mysqli/mysqli_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 656fe3f3475bb2f43d89ebf34361940e2f746373 */ + * Stub hash: a54ef005e9535458e886b7845a25a1c742a114ac */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_mysqli_affected_rows, 0, 1, MAY_BE_LONG|MAY_BE_STRING) ZEND_ARG_OBJ_INFO(0, mysql, mysqli, 0) @@ -130,7 +130,7 @@ ZEND_END_ARG_INFO() #define arginfo_mysqli_field_count arginfo_mysqli_errno -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_mysqli_field_seek, 0, 2, _IS_BOOL, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_mysqli_field_seek, 0, 2, IS_TRUE, 0) ZEND_ARG_OBJ_INFO(0, result, mysqli_result, 0) ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -628,7 +628,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_mysqli_result_fetch_column ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, column, IS_LONG, 0, "0") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_mysqli_result_field_seek, 0, 1, _IS_BOOL, 0) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_mysqli_result_field_seek, 0, 1, IS_TRUE, 0) ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0) ZEND_END_ARG_INFO() From b71c6b2c6c11547cf7bd11a6109b34e9cb0792b1 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 14 Aug 2023 01:41:54 +0200 Subject: [PATCH 12/47] Fix #81992: SplFixedArray::setSize() causes use-after-free Upon resizing, the elements are destroyed from lower index to higher index. When an element refers to an object with a destructor, it can refer to a lower (i.e. already destroyed) element, causing a uaf. Set refcounted zvals to NULL after destroying them to avoid a uaf. Closes GH-11959. --- NEWS | 4 +++ ext/spl/spl_fixedarray.c | 22 +++++++++++- ext/spl/tests/bug81992.phpt | 32 +++++++++++++++++ ext/spl/tests/bug81992b.phpt | 66 ++++++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 ext/spl/tests/bug81992.phpt create mode 100644 ext/spl/tests/bug81992b.phpt diff --git a/NEWS b/NEWS index b8bfcace9f65f..1970eca1c734c 100644 --- a/NEWS +++ b/NEWS @@ -55,6 +55,10 @@ PHP NEWS . Revert behaviour of receiving SIGCHLD signals back to the behaviour before 8.1.22. (nielsdos) +- SPL: + . Fixed bug #81992 (SplFixedArray::setSize() causes use-after-free). + (nielsdos) + - Standard: . Prevent int overflow on $decimals in number_format. (Marc Bennewitz) . Fixed bug GH-11870 (Fix off-by-one bug when truncating tempnam prefix) diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index 2cf5e2360adbf..562806a5c284b 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -46,6 +46,8 @@ typedef struct _spl_fixedarray { zval *elements; /* True if this was modified after the last call to get_properties or the hash table wasn't rebuilt. */ bool should_rebuild_properties; + /* If positive, it's a resize within a resize and the value gives the desired size. If -1, it's not. */ + zend_long cached_resize; } spl_fixedarray; typedef struct _spl_fixedarray_methods { @@ -117,6 +119,7 @@ static void spl_fixedarray_init(spl_fixedarray *array, zend_long size) } else { spl_fixedarray_default_ctor(array); } + array->cached_resize = -1; } /* Copies the range [begin, end) into the fixedarray, beginning at `offset`. @@ -148,6 +151,7 @@ static void spl_fixedarray_copy_ctor(spl_fixedarray *to, spl_fixedarray *from) */ static void spl_fixedarray_dtor_range(spl_fixedarray *array, zend_long from, zend_long to) { + array->size = from; zval *begin = array->elements + from, *end = array->elements + to; while (begin != end) { zval_ptr_dtor(begin++); @@ -184,19 +188,35 @@ static void spl_fixedarray_resize(spl_fixedarray *array, zend_long size) return; } + if (UNEXPECTED(array->cached_resize >= 0)) { + /* We're already resizing, so just remember the desired size. + * The resize will happen later. */ + array->cached_resize = size; + return; + } + array->cached_resize = size; + /* clearing the array */ if (size == 0) { spl_fixedarray_dtor(array); array->elements = NULL; + array->size = 0; } else if (size > array->size) { array->elements = safe_erealloc(array->elements, size, sizeof(zval), 0); spl_fixedarray_init_elems(array, array->size, size); + array->size = size; } else { /* size < array->size */ + /* Size set in spl_fixedarray_dtor_range() */ spl_fixedarray_dtor_range(array, size, array->size); array->elements = erealloc(array->elements, sizeof(zval) * size); } - array->size = size; + /* If resized within the destructor, take the last resize command and perform it */ + zend_long cached_resize = array->cached_resize; + array->cached_resize = -1; + if (cached_resize != size) { + spl_fixedarray_resize(array, cached_resize); + } } static HashTable* spl_fixedarray_object_get_gc(zend_object *obj, zval **table, int *n) diff --git a/ext/spl/tests/bug81992.phpt b/ext/spl/tests/bug81992.phpt new file mode 100644 index 0000000000000..52235218a78f6 --- /dev/null +++ b/ext/spl/tests/bug81992.phpt @@ -0,0 +1,32 @@ +--TEST-- +Bug #81992 (SplFixedArray::setSize() causes use-after-free) +--FILE-- +getMessage(), "\n"; + } + try { + var_dump($obj[4]); + } catch (Throwable $e) { + echo $e->getMessage(), "\n"; + } + } +} + +$obj = new SplFixedArray(5); +$obj[0] = str_repeat("A", 10); +$obj[2] = str_repeat('B', 10); +$obj[3] = new InvalidDestructor(); +$obj[4] = str_repeat('C', 10); +$obj->setSize(2); +?> +--EXPECT-- +string(10) "AAAAAAAAAA" +Index invalid or out of range +Index invalid or out of range diff --git a/ext/spl/tests/bug81992b.phpt b/ext/spl/tests/bug81992b.phpt new file mode 100644 index 0000000000000..ba4736dfff1f3 --- /dev/null +++ b/ext/spl/tests/bug81992b.phpt @@ -0,0 +1,66 @@ +--TEST-- +Bug #81992 (SplFixedArray::setSize() causes use-after-free) - setSize variation +--FILE-- +obj->setSize($this->desiredSize); + echo "Destroyed, size is now still ", $this->obj->getSize(), "\n"; + } +} + +class DestructorLogger { + public function __construct(private int $id) {} + + public function __destruct() { + echo "Destroyed the logger with id ", $this->id, "\n"; + } +} + +function test(int $desiredSize) { + $obj = new SplFixedArray(5); + $obj[0] = str_repeat("A", 10); + $obj[1] = new DestructorLogger(1); + $obj[2] = str_repeat('B', 10); + $obj[3] = new InvalidDestructor($desiredSize, $obj); + $obj[4] = new DestructorLogger(4); + $obj->setSize(2); + echo "Size is now ", $obj->getSize(), "\n"; + echo "Done\n"; +} + +echo "--- Smaller size test ---\n"; +test(1); +echo "--- Equal size test ---\n"; +test(2); +echo "--- Larger size test ---\n"; +test(10); +?> +--EXPECT-- +--- Smaller size test --- +In destructor +Destroyed, size is now still 2 +Destroyed the logger with id 4 +Destroyed the logger with id 1 +Size is now 1 +Done +--- Equal size test --- +In destructor +Destroyed, size is now still 2 +Destroyed the logger with id 4 +Size is now 2 +Done +Destroyed the logger with id 1 +--- Larger size test --- +In destructor +Destroyed, size is now still 2 +Destroyed the logger with id 4 +Size is now 10 +Done +Destroyed the logger with id 1 From 02a80c5b826ba86d2b315d5babfa340230ab20c6 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Mon, 14 Aug 2023 22:34:56 +0100 Subject: [PATCH 13/47] Fix various bugs related to DNF types - GH-11958: DNF types in trait properties do not get bound properly - GH-11883: Memory leak in zend_type_release() for non-arena allocated DNF types - Internal trait bound to userland class would not be arena allocated - Property DNF types were not properly deep copied during lazy loading Co-authored-by: Ilija Tovilo Co-authored-by: ju1ius --- NEWS | 10 +++- .../variance/invalid_invariance1_var.phpt | 18 +++++++ .../internal_trait_use_typed_union.phpt | 39 ++++++++++++++ Zend/zend_inheritance.c | 53 +++++++++++-------- Zend/zend_opcode.c | 12 +---- ext/zend_test/test.stub.php | 1 + ext/zend_test/test_arginfo.h | 15 +++++- 7 files changed, 113 insertions(+), 35 deletions(-) create mode 100644 Zend/tests/type_declarations/dnf_types/variance/invalid_invariance1_var.phpt create mode 100644 Zend/tests/type_declarations/union_types/internal_trait_use_typed_union.phpt diff --git a/NEWS b/NEWS index 1d657909a34db..8d8884ef7757b 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,14 @@ PHP NEWS - Core: . Fixed strerror_r detection at configuration time. (Kévin Dunglas) + . Fixed trait typed properties using a DNF type not being correctly bound. + (Girgias) + . Fixed trait property types not being arena allocated if copied from + an internal trait. (Girgias) + . Fixed deep copy of property DNF type during lazy class load. + (Girgias, ilutov) + . Fixed memory freeing of DNF types for non arena allocated types. + (Girgias, ju1ius) - DOM: . Fix DOMEntity field getter bugs. (nielsdos) @@ -156,7 +164,7 @@ PHP NEWS - Standard: . Fix serialization of RC1 objects appearing in object graph twice. (ilutov) - + - Streams: . Fixed bug GH-11735 (Use-after-free when unregistering user stream wrapper from itself). (ilutov) diff --git a/Zend/tests/type_declarations/dnf_types/variance/invalid_invariance1_var.phpt b/Zend/tests/type_declarations/dnf_types/variance/invalid_invariance1_var.phpt new file mode 100644 index 0000000000000..1a8a31f066ed6 --- /dev/null +++ b/Zend/tests/type_declarations/dnf_types/variance/invalid_invariance1_var.phpt @@ -0,0 +1,18 @@ +--TEST-- +Property types must be invariant +--FILE-- + +--EXPECTF-- +Fatal error: Type of B::$prop must be (X&Y&Z)|L (as in class A) in %s on line %d diff --git a/Zend/tests/type_declarations/union_types/internal_trait_use_typed_union.phpt b/Zend/tests/type_declarations/union_types/internal_trait_use_typed_union.phpt new file mode 100644 index 0000000000000..62bbefc8f5a0b --- /dev/null +++ b/Zend/tests/type_declarations/union_types/internal_trait_use_typed_union.phpt @@ -0,0 +1,39 @@ +--TEST-- +Internal trait used typed property (union type) +--EXTENSIONS-- +zend_test +--FILE-- +getType(); +$types = $union->getTypes(); +var_dump($types, (string)$types[0], (string)$types[1]); + +?> +===DONE=== +--EXPECT-- +object(C)#1 (1) { + ["testProp"]=> + NULL + ["classUnionProp"]=> + uninitialized(Traversable|Countable) +} +array(2) { + [0]=> + object(ReflectionNamedType)#4 (0) { + } + [1]=> + object(ReflectionNamedType)#5 (0) { + } +} +string(11) "Traversable" +string(9) "Countable" +===DONE=== diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 6b168a8b41106..b34823569fc1c 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -57,20 +57,33 @@ static void ZEND_COLD emit_incompatible_method_error( const zend_function *parent, zend_class_entry *parent_scope, inheritance_status status); -static void zend_type_copy_ctor(zend_type *type, bool persistent) { +static void zend_type_copy_ctor(zend_type *const type, bool use_arena, bool persistent); + +static void zend_type_list_copy_ctor( + zend_type *const parent_type, + bool use_arena, + bool persistent +) { + const zend_type_list *const old_list = ZEND_TYPE_LIST(*parent_type); + size_t size = ZEND_TYPE_LIST_SIZE(old_list->num_types); + zend_type_list *new_list = use_arena + ? zend_arena_alloc(&CG(arena), size) : pemalloc(size, persistent); + + memcpy(new_list, old_list, size); + ZEND_TYPE_SET_LIST(*parent_type, new_list); + if (use_arena) { + ZEND_TYPE_FULL_MASK(*parent_type) |= _ZEND_TYPE_ARENA_BIT; + } + + zend_type *list_type; + ZEND_TYPE_LIST_FOREACH(new_list, list_type) { + zend_type_copy_ctor(list_type, use_arena, persistent); + } ZEND_TYPE_LIST_FOREACH_END(); +} + +static void zend_type_copy_ctor(zend_type *const type, bool use_arena, bool persistent) { if (ZEND_TYPE_HAS_LIST(*type)) { - zend_type_list *old_list = ZEND_TYPE_LIST(*type); - size_t size = ZEND_TYPE_LIST_SIZE(old_list->num_types); - zend_type_list *new_list = ZEND_TYPE_USES_ARENA(*type) - ? zend_arena_alloc(&CG(arena), size) : pemalloc(size, persistent); - memcpy(new_list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types)); - ZEND_TYPE_SET_PTR(*type, new_list); - - zend_type *list_type; - ZEND_TYPE_LIST_FOREACH(new_list, list_type) { - ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*list_type)); - zend_string_addref(ZEND_TYPE_NAME(*list_type)); - } ZEND_TYPE_LIST_FOREACH_END(); + zend_type_list_copy_ctor(type, use_arena, persistent); } else if (ZEND_TYPE_HAS_NAME(*type)) { zend_string_addref(ZEND_TYPE_NAME(*type)); } @@ -2401,7 +2414,8 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent doc_comment = property_info->doc_comment ? zend_string_copy(property_info->doc_comment) : NULL; zend_type type = property_info->type; - zend_type_copy_ctor(&type, /* persistent */ 0); + /* Assumption: only userland classes can use traits, as such the type must be arena allocated */ + zend_type_copy_ctor(&type, /* use arena */ true, /* persistent */ false); new_prop = zend_declare_typed_property(ce, prop_name, prop_value, flags, doc_comment, type); if (property_info->attributes) { @@ -2789,15 +2803,8 @@ static zend_class_entry *zend_lazy_class_load(zend_class_entry *pce) Z_PTR(p->val) = new_prop_info; memcpy(new_prop_info, prop_info, sizeof(zend_property_info)); new_prop_info->ce = ce; - if (ZEND_TYPE_HAS_LIST(new_prop_info->type)) { - zend_type_list *new_list; - zend_type_list *list = ZEND_TYPE_LIST(new_prop_info->type); - - new_list = zend_arena_alloc(&CG(arena), ZEND_TYPE_LIST_SIZE(list->num_types)); - memcpy(new_list, list, ZEND_TYPE_LIST_SIZE(list->num_types)); - ZEND_TYPE_SET_PTR(new_prop_info->type, list); - ZEND_TYPE_FULL_MASK(new_prop_info->type) |= _ZEND_TYPE_ARENA_BIT; - } + /* Deep copy the type information */ + zend_type_copy_ctor(&new_prop_info->type, /* use_arena */ true, /* persistent */ false); } } diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 6d42379b2022e..3475bc4db3bf9 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -110,17 +110,9 @@ ZEND_API void destroy_zend_function(zend_function *function) ZEND_API void zend_type_release(zend_type type, bool persistent) { if (ZEND_TYPE_HAS_LIST(type)) { - zend_type *list_type, *sublist_type; + zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { - if (ZEND_TYPE_HAS_LIST(*list_type)) { - ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*list_type), sublist_type) { - if (ZEND_TYPE_HAS_NAME(*sublist_type)) { - zend_string_release(ZEND_TYPE_NAME(*sublist_type)); - } - } ZEND_TYPE_LIST_FOREACH_END(); - } else if (ZEND_TYPE_HAS_NAME(*list_type)) { - zend_string_release(ZEND_TYPE_NAME(*list_type)); - } + zend_type_release(*list_type, persistent); } ZEND_TYPE_LIST_FOREACH_END(); if (!ZEND_TYPE_USES_ARENA(type)) { pefree(ZEND_TYPE_LIST(type), persistent); diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index dabf89432425f..5ebe6b574b666 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -55,6 +55,7 @@ public function returnsThrowable(): Exception {} trait _ZendTestTrait { /** @var mixed */ public $testProp; + public Traversable|Countable $classUnionProp; public function testMethod(): bool {} } diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index ba6ff9c73bdaa..5a3af3879d7d6 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 9a8087f2c6f7fef676f4c955a3c530b83605ac9a */ + * Stub hash: fa6d8c58bdef20c6c9c8db75dbb6ede775324193 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -496,6 +496,19 @@ static zend_class_entry *register_class__ZendTestTrait(void) zend_declare_property_ex(class_entry, property_testProp_name, &property_testProp_default_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(property_testProp_name); + zend_string *property_classUnionProp_class_Traversable = zend_string_init("Traversable", sizeof("Traversable") - 1, 1); + zend_string *property_classUnionProp_class_Countable = zend_string_init("Countable", sizeof("Countable") - 1, 1); + zend_type_list *property_classUnionProp_type_list = malloc(ZEND_TYPE_LIST_SIZE(2)); + property_classUnionProp_type_list->num_types = 2; + property_classUnionProp_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(property_classUnionProp_class_Traversable, 0, 0); + property_classUnionProp_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS(property_classUnionProp_class_Countable, 0, 0); + zend_type property_classUnionProp_type = ZEND_TYPE_INIT_UNION(property_classUnionProp_type_list, 0); + zval property_classUnionProp_default_value; + ZVAL_UNDEF(&property_classUnionProp_default_value); + zend_string *property_classUnionProp_name = zend_string_init("classUnionProp", sizeof("classUnionProp") - 1, 1); + zend_declare_typed_property(class_entry, property_classUnionProp_name, &property_classUnionProp_default_value, ZEND_ACC_PUBLIC, NULL, property_classUnionProp_type); + zend_string_release(property_classUnionProp_name); + return class_entry; } From d46dc5694c3cd4c1492449941bb34c050efea751 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 23 Jul 2023 17:10:55 +0200 Subject: [PATCH 14/47] Fix various namespace prefix conflict resolution bugs and namespace shift bugs There are two linked issues: - Conflicts couldn't be resolved by changing the prefix name. - Lacking a prefix would shift the namespace as the default namespace, causing elements to suddenly become part of the namespace instead of the attributes. The output could still be improved by removing redundant namespace declarations, but that's another issue. At least the output is correct now. Closes GH-11777. --- NEWS | 3 + UPGRADING | 6 ++ ext/dom/document.c | 6 +- ext/dom/element.c | 58 ++--------------- ext/dom/php_dom.c | 59 +++++++++++------ ext/dom/php_dom.h | 1 + ..._importNode_attribute_prefix_conflict.phpt | 65 +++++++++++++++++++ ...lement_setAttributeNS_prefix_conflict.phpt | 35 ++++++++++ .../setAttributeNS_with_prefix.phpt | 36 ++++++++++ .../setAttributeNS_without_prefix.phpt | 36 ++++++++++ .../setAttribute_mixed_prefix.phpt | 20 ++++++ .../setAttribute_with_prefix.phpt | 36 ++++++++++ .../setAttribute_without_prefix.phpt | 36 ++++++++++ ...mespace_definition_crash_in_attribute.phpt | 6 +- 14 files changed, 323 insertions(+), 80 deletions(-) create mode 100644 ext/dom/tests/DOMDocument_importNode_attribute_prefix_conflict.phpt create mode 100644 ext/dom/tests/DOMElement_setAttributeNS_prefix_conflict.phpt create mode 100644 ext/dom/tests/createAttributeNS_prefix_conflicts/setAttributeNS_with_prefix.phpt create mode 100644 ext/dom/tests/createAttributeNS_prefix_conflicts/setAttributeNS_without_prefix.phpt create mode 100644 ext/dom/tests/createAttributeNS_prefix_conflicts/setAttribute_mixed_prefix.phpt create mode 100644 ext/dom/tests/createAttributeNS_prefix_conflicts/setAttribute_with_prefix.phpt create mode 100644 ext/dom/tests/createAttributeNS_prefix_conflicts/setAttribute_without_prefix.phpt diff --git a/NEWS b/NEWS index 0ad6a06adc048..75bfad53c8d20 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,9 @@ PHP NEWS . Align DOMChildNode parent checks with spec. (nielsdos) . Fixed bug #80927 (Removing documentElement after creating attribute node: possible use-after-free). (nielsdos) + . Fix various namespace prefix conflict resolution bugs. (nielsdos) + . Fix calling createAttributeNS() without prefix causing the default + namespace of the element to change. (nielsdos) - Opcache: . Avoid resetting JIT counter handlers from multiple processes/threads. diff --git a/UPGRADING b/UPGRADING index 95f7a24ebc767..3a33f42af076f 100644 --- a/UPGRADING +++ b/UPGRADING @@ -53,6 +53,12 @@ PHP 8.3 UPGRADE NOTES . Using the DOMParentNode and DOMChildNode methods without a document now works instead of throwing a HIERARCHY_REQUEST_ERR DOMException. This is in line with the behaviour spec demands. + . createAttributeNS() without specifying a prefix would incorrectly create a default + namespace, placing the element inside the namespace instead of the attribute. + This bug is now fixed. + . createAttributeNS() would previously incorrectly throw a NAMESPACE_ERR when the + prefix was already used for a different uri. It now correctly chooses a + different prefix when there's a prefix name conflict. - FFI: . C functions that have a return type of void now return null instead of diff --git a/ext/dom/document.c b/ext/dom/document.c index bfb06d7e4cca4..f254d87e7d63c 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -805,7 +805,7 @@ PHP_METHOD(DOMDocument, importNode) xmlNodePtr root = xmlDocGetRootElement(docp); nsptr = xmlSearchNsByHref (nodep->doc, root, nodep->ns->href); - if (nsptr == NULL) { + if (nsptr == NULL || nsptr->prefix == NULL) { int errorcode; nsptr = dom_get_ns(root, (char *) nodep->ns->href, &errorcode, (char *) nodep->ns->prefix); } @@ -910,8 +910,8 @@ PHP_METHOD(DOMDocument, createAttributeNS) nodep = (xmlNodePtr) xmlNewDocProp(docp, (xmlChar *) localname, NULL); if (nodep != NULL && uri_len > 0) { nsptr = xmlSearchNsByHref(nodep->doc, root, (xmlChar *) uri); - if (nsptr == NULL) { - nsptr = dom_get_ns(root, uri, &errorcode, prefix); + if (nsptr == NULL || nsptr->prefix == NULL) { + nsptr = dom_get_ns(root, uri, &errorcode, prefix ? prefix : "default"); } xmlSetNs(nodep, nsptr); } diff --git a/ext/dom/element.c b/ext/dom/element.c index 8fb19d9e2eaed..fabc3c514bf96 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -655,45 +655,6 @@ PHP_METHOD(DOMElement, getAttributeNS) } /* }}} end dom_element_get_attribute_ns */ -static xmlNsPtr _dom_new_reconNs(xmlDocPtr doc, xmlNodePtr tree, xmlNsPtr ns) /* {{{ */ -{ - xmlNsPtr def; - xmlChar prefix[50]; - int counter = 1; - - if ((tree == NULL) || (ns == NULL) || (ns->type != XML_NAMESPACE_DECL)) { - return NULL; - } - - /* Code taken from libxml2 (2.6.20) xmlNewReconciliedNs - * - * Find a close prefix which is not already in use. - * Let's strip namespace prefixes longer than 20 chars ! - */ - if (ns->prefix == NULL) - snprintf((char *) prefix, sizeof(prefix), "default"); - else - snprintf((char *) prefix, sizeof(prefix), "%.20s", (char *)ns->prefix); - - def = xmlSearchNs(doc, tree, prefix); - while (def != NULL) { - if (counter > 1000) return(NULL); - if (ns->prefix == NULL) - snprintf((char *) prefix, sizeof(prefix), "default%d", counter++); - else - snprintf((char *) prefix, sizeof(prefix), "%.20s%d", - (char *)ns->prefix, counter++); - def = xmlSearchNs(doc, tree, prefix); - } - - /* - * OK, now we are ready to create a new one. - */ - def = xmlNewNs(tree, ns->href, prefix); - return(def); -} -/* }}} */ - /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAttrNS Since: DOM Level 2 */ @@ -756,27 +717,18 @@ PHP_METHOD(DOMElement, setAttributeNS) tmpnsptr = tmpnsptr->next; } if (tmpnsptr == NULL) { - nsptr = _dom_new_reconNs(elemp->doc, elemp, nsptr); + nsptr = dom_get_ns_resolve_prefix_conflict(elemp, (const char *) nsptr->href); } } } if (nsptr == NULL) { - if (prefix == NULL) { - if (is_xmlns == 1) { - xmlNewNs(elemp, (xmlChar *)value, NULL); - xmlReconciliateNs(elemp->doc, elemp); - } else { - errorcode = NAMESPACE_ERR; - } + if (is_xmlns == 1) { + xmlNewNs(elemp, (xmlChar *)value, prefix == NULL ? NULL : (xmlChar *)localname); } else { - if (is_xmlns == 1) { - xmlNewNs(elemp, (xmlChar *)value, (xmlChar *)localname); - } else { - nsptr = dom_get_ns(elemp, uri, &errorcode, prefix); - } - xmlReconciliateNs(elemp->doc, elemp); + nsptr = dom_get_ns(elemp, uri, &errorcode, prefix); } + xmlReconciliateNs(elemp->doc, elemp); } else { if (is_xmlns == 1) { if (nsptr->href) { diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 9e036214bde53..bada817e301e8 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -32,10 +32,6 @@ #define PHP_XPATH 1 #define PHP_XPTR 2 -/* libxml2 doesn't expose this constant as part of their public API. - * See xmlDOMReconcileNSOptions in tree.c */ -#define PHP_LIBXML2_DOM_RECONNS_REMOVEREDUND (1 << 0) - /* {{{ class entries */ PHP_DOM_EXPORT zend_class_entry *dom_node_class_entry; PHP_DOM_EXPORT zend_class_entry *dom_domexception_class_entry; @@ -1473,8 +1469,7 @@ static void dom_libxml_reconcile_ensure_namespaces_are_declared(xmlNodePtr nodep * Although libxml2 currently does not use this for the reconciliation, it still * makes sense to do this just in case libxml2's internal change in the future. */ xmlDOMWrapCtxt dummy_ctxt = {0}; - bool remove_redundant = nodep->nsDef == NULL && nodep->ns != NULL; - xmlDOMWrapReconcileNamespaces(&dummy_ctxt, nodep, /* options */ remove_redundant ? PHP_LIBXML2_DOM_RECONNS_REMOVEREDUND : 0); + xmlDOMWrapReconcileNamespaces(&dummy_ctxt, nodep, /* options */ 0); } void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep) /* {{{ */ @@ -1557,6 +1552,35 @@ int dom_check_qname(char *qname, char **localname, char **prefix, int uri_len, i } /* }}} */ +/* Creates a new namespace declaration with a random prefix with the given uri on the tree. + * This is used to resolve a namespace prefix conflict in cases where spec does not want a + * namespace error in case of conflicts, but demands a resolution. */ +xmlNsPtr dom_get_ns_resolve_prefix_conflict(xmlNodePtr tree, const char *uri) +{ + ZEND_ASSERT(tree != NULL); + xmlDocPtr doc = tree->doc; + + if (UNEXPECTED(doc == NULL)) { + return NULL; + } + + /* Code adapted from libxml2 (2.10.4) */ + char prefix[50]; + int counter = 1; + snprintf(prefix, sizeof(prefix), "default"); + xmlNsPtr nsptr = xmlSearchNs(doc, tree, (const xmlChar *) prefix); + while (nsptr != NULL) { + if (counter > 1000) { + return NULL; + } + snprintf(prefix, sizeof(prefix), "default%d", counter++); + nsptr = xmlSearchNs(doc, tree, (const xmlChar *) prefix); + } + + /* Search yielded no conflict */ + return xmlNewNs(tree, (const xmlChar *) uri, (const xmlChar *) prefix); +} + /* http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocCrElNS @@ -1574,28 +1598,21 @@ xmlNsPtr dom_get_ns(xmlNodePtr nodep, char *uri, int *errorcode, char *prefix) { if (! ((prefix && !strcmp (prefix, "xml") && strcmp(uri, (char *)XML_XML_NAMESPACE)) || (prefix && !strcmp (prefix, "xmlns") && strcmp(uri, (char *)DOM_XMLNS_NAMESPACE)) || (prefix && !strcmp(uri, (char *)DOM_XMLNS_NAMESPACE) && strcmp (prefix, "xmlns")))) { - /* Reuse the old namespaces from doc->oldNs if possible, before creating a new one. - * This will prevent the oldNs list from growing with duplicates. */ - xmlDocPtr doc = nodep->doc; - if (doc && doc->oldNs != NULL) { - nsptr = doc->oldNs; - do { - if (xmlStrEqual(nsptr->prefix, (xmlChar *)prefix) && xmlStrEqual(nsptr->href, (xmlChar *)uri)) { - goto out; - } - nsptr = nsptr->next; - } while (nsptr); - } - /* Couldn't reuse one, create a new one. */ nsptr = xmlNewNs(nodep, (xmlChar *)uri, (xmlChar *)prefix); if (UNEXPECTED(nsptr == NULL)) { - goto err; + /* Either memory allocation failure, or it's because of a prefix conflict. + * We'll assume a conflict and try again. If it was a memory allocation failure we'll just fail again, whatever. + * This isn't needed for every caller (such as createElementNS & DOMElement::__construct), but isn't harmful and simplifies the mental model "when do I use which function?". + * This branch will also be taken unlikely anyway as in those cases it'll be for allocation failure. */ + nsptr = dom_get_ns_resolve_prefix_conflict(nodep, uri); + if (UNEXPECTED(nsptr == NULL)) { + goto err; + } } } else { goto err; } -out: *errorcode = 0; return nsptr; err: diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index b4a6a2d01e83b..4d415256788af 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -151,6 +151,7 @@ zend_string *dom_node_concatenated_name_helper(size_t name_len, const char *name zend_string *dom_node_get_node_name_attribute_or_element(const xmlNode *nodep); bool php_dom_is_node_connected(const xmlNode *node); bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, xmlDocPtr new_document); +xmlNsPtr dom_get_ns_resolve_prefix_conflict(xmlNodePtr tree, const char *uri); /* parentnode */ void dom_parent_node_prepend(dom_object *context, zval *nodes, uint32_t nodesc); diff --git a/ext/dom/tests/DOMDocument_importNode_attribute_prefix_conflict.phpt b/ext/dom/tests/DOMDocument_importNode_attribute_prefix_conflict.phpt new file mode 100644 index 0000000000000..d00fedbfb3d6c --- /dev/null +++ b/ext/dom/tests/DOMDocument_importNode_attribute_prefix_conflict.phpt @@ -0,0 +1,65 @@ +--TEST-- +DOMDocument::importNode() with attribute prefix name conflict +--EXTENSIONS-- +dom +--FILE-- +loadXML(''); +$dom2->loadXML(''); +$attribute = $dom1->documentElement->getAttributeNode('foo:bar'); +$imported = $dom2->importNode($attribute); +$dom2->documentElement->setAttributeNodeNS($imported); + +echo $dom1->saveXML(); +echo $dom2->saveXML(); + +echo "--- Non-default namespace test case with a default namespace in the destination ---\n"; + +$dom1 = new DOMDocument(); +$dom2 = new DOMDocument(); +$dom1->loadXML(''); +$dom2->loadXML(''); +$attribute = $dom1->documentElement->getAttributeNode('foo:bar'); +$imported = $dom2->importNode($attribute); +$dom2->documentElement->setAttributeNodeNS($imported); + +echo $dom1->saveXML(); +echo $dom2->saveXML(); + +echo "--- Default namespace test case ---\n"; + +// We don't expect the namespace to be imported because default namespaces on the same element don't apply to attributes +// but the attribute should be imported +$dom1 = new DOMDocument(); +$dom2 = new DOMDocument(); +$dom1->loadXML(''); +$dom2->loadXML(''); +$attribute = $dom1->documentElement->getAttributeNode('bar'); +$imported = $dom2->importNode($attribute); +$dom2->documentElement->setAttributeNodeNS($imported); + +echo $dom1->saveXML(); +echo $dom2->saveXML(); + +?> +--EXPECT-- +--- Non-default namespace test case without a default namespace in the destination --- + + + + +--- Non-default namespace test case with a default namespace in the destination --- + + + + +--- Default namespace test case --- + + + + diff --git a/ext/dom/tests/DOMElement_setAttributeNS_prefix_conflict.phpt b/ext/dom/tests/DOMElement_setAttributeNS_prefix_conflict.phpt new file mode 100644 index 0000000000000..fc491b59ea31d --- /dev/null +++ b/ext/dom/tests/DOMElement_setAttributeNS_prefix_conflict.phpt @@ -0,0 +1,35 @@ +--TEST-- +DOMElement::setAttributeNS() with prefix name conflict +--EXTENSIONS-- +dom +--FILE-- +loadXML(''); +$dom->documentElement->setAttributeNS('http://php.net/2', 'foo:bar', 'no1'); +echo $dom->saveXML(); +$dom->documentElement->setAttributeNS('http://php.net/2', 'bar', 'no2'); +echo $dom->saveXML(); + +echo "--- Default namespace test case ---\n"; + +$dom = new DOMDocument(); +$dom->loadXML(''); +$dom->documentElement->setAttributeNS('http://php.net/2', 'bar', 'no1'); +echo $dom->saveXML(); +$dom->documentElement->setAttributeNS('http://php.net/2', 'bar', 'no2'); +echo $dom->saveXML(); +?> +--EXPECT-- +--- Non-default namespace test case --- + + + + +--- Default namespace test case --- + + + + diff --git a/ext/dom/tests/createAttributeNS_prefix_conflicts/setAttributeNS_with_prefix.phpt b/ext/dom/tests/createAttributeNS_prefix_conflicts/setAttributeNS_with_prefix.phpt new file mode 100644 index 0000000000000..1ca679d576d2f --- /dev/null +++ b/ext/dom/tests/createAttributeNS_prefix_conflicts/setAttributeNS_with_prefix.phpt @@ -0,0 +1,36 @@ +--TEST-- +DOMDocument::createAttributeNS() with prefix name conflict - setAttributeNodeNS variation, with prefix +--EXTENSIONS-- +dom +--FILE-- +appendChild($doc->createElement('container')); + +var_dump($doc->documentElement->setAttributeNodeNS($doc->createAttributeNS('http://php.net/ns1', 'foo:hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; +var_dump($doc->documentElement->setAttributeNodeNS($doc->createAttributeNS('http://php.net/ns2', 'foo:hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; +var_dump($doc->documentElement->setAttributeNodeNS($doc->createAttributeNS('http://php.net/ns3', 'foo:hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; +var_dump($doc->documentElement->setAttributeNodeNS($doc->createAttributeNS('http://php.net/ns4', 'foo:hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; + +?> +--EXPECT-- +NULL + + + +NULL + + + +NULL + + + +NULL + + diff --git a/ext/dom/tests/createAttributeNS_prefix_conflicts/setAttributeNS_without_prefix.phpt b/ext/dom/tests/createAttributeNS_prefix_conflicts/setAttributeNS_without_prefix.phpt new file mode 100644 index 0000000000000..6ab6c2292a787 --- /dev/null +++ b/ext/dom/tests/createAttributeNS_prefix_conflicts/setAttributeNS_without_prefix.phpt @@ -0,0 +1,36 @@ +--TEST-- +DOMDocument::createAttributeNS() with prefix name conflict - setAttributeNodeNS variation, without prefix +--EXTENSIONS-- +dom +--FILE-- +appendChild($doc->createElement('container')); + +var_dump($doc->documentElement->setAttributeNodeNS($doc->createAttributeNS('http://php.net/ns1', 'hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; +var_dump($doc->documentElement->setAttributeNodeNS($doc->createAttributeNS('http://php.net/ns2', 'hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; +var_dump($doc->documentElement->setAttributeNodeNS($doc->createAttributeNS('http://php.net/ns3', 'hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; +var_dump($doc->documentElement->setAttributeNodeNS($doc->createAttributeNS('http://php.net/ns4', 'hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; + +?> +--EXPECT-- +NULL + + + +NULL + + + +NULL + + + +NULL + + diff --git a/ext/dom/tests/createAttributeNS_prefix_conflicts/setAttribute_mixed_prefix.phpt b/ext/dom/tests/createAttributeNS_prefix_conflicts/setAttribute_mixed_prefix.phpt new file mode 100644 index 0000000000000..a53d7304dc87e --- /dev/null +++ b/ext/dom/tests/createAttributeNS_prefix_conflicts/setAttribute_mixed_prefix.phpt @@ -0,0 +1,20 @@ +--TEST-- +DOMDocument::createAttributeNS() with prefix name conflict - setAttributeNode variation (DOM Level 3), mixed +--EXTENSIONS-- +dom +--FILE-- +appendChild($doc->createElement('container')); + +var_dump($doc->documentElement->setAttributeNode($doc->createAttributeNS('http://php.net/ns1', 'foo:hello'))?->namespaceURI); +var_dump($doc->documentElement->setAttributeNode($doc->createAttributeNS('http://php.net/ns1', 'hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; + +?> +--EXPECT-- +NULL +string(18) "http://php.net/ns1" + + diff --git a/ext/dom/tests/createAttributeNS_prefix_conflicts/setAttribute_with_prefix.phpt b/ext/dom/tests/createAttributeNS_prefix_conflicts/setAttribute_with_prefix.phpt new file mode 100644 index 0000000000000..161eadd353ba6 --- /dev/null +++ b/ext/dom/tests/createAttributeNS_prefix_conflicts/setAttribute_with_prefix.phpt @@ -0,0 +1,36 @@ +--TEST-- +DOMDocument::createAttributeNS() with prefix name conflict - setAttributeNode variation (DOM Level 3), with prefix +--EXTENSIONS-- +dom +--FILE-- +appendChild($doc->createElement('container')); + +var_dump($doc->documentElement->setAttributeNode($doc->createAttributeNS('http://php.net/ns1', 'foo:hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; +var_dump($doc->documentElement->setAttributeNode($doc->createAttributeNS('http://php.net/ns2', 'foo:hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; +var_dump($doc->documentElement->setAttributeNode($doc->createAttributeNS('http://php.net/ns3', 'foo:hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; +var_dump($doc->documentElement->setAttributeNode($doc->createAttributeNS('http://php.net/ns4', 'foo:hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; + +?> +--EXPECT-- +NULL + + + +string(18) "http://php.net/ns1" + + + +string(18) "http://php.net/ns2" + + + +string(18) "http://php.net/ns3" + + diff --git a/ext/dom/tests/createAttributeNS_prefix_conflicts/setAttribute_without_prefix.phpt b/ext/dom/tests/createAttributeNS_prefix_conflicts/setAttribute_without_prefix.phpt new file mode 100644 index 0000000000000..34717ff6ab5be --- /dev/null +++ b/ext/dom/tests/createAttributeNS_prefix_conflicts/setAttribute_without_prefix.phpt @@ -0,0 +1,36 @@ +--TEST-- +DOMDocument::createAttributeNS() with prefix name conflict - setAttributeNode variation (DOM Level 3), without prefix +--EXTENSIONS-- +dom +--FILE-- +appendChild($doc->createElement('container')); + +var_dump($doc->documentElement->setAttributeNode($doc->createAttributeNS('http://php.net/ns1', 'hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; +var_dump($doc->documentElement->setAttributeNode($doc->createAttributeNS('http://php.net/ns2', 'hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; +var_dump($doc->documentElement->setAttributeNode($doc->createAttributeNS('http://php.net/ns3', 'hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; +var_dump($doc->documentElement->setAttributeNode($doc->createAttributeNS('http://php.net/ns4', 'hello'))?->namespaceURI); +echo $doc->saveXML(), "\n"; + +?> +--EXPECT-- +NULL + + + +string(18) "http://php.net/ns1" + + + +string(18) "http://php.net/ns2" + + + +string(18) "http://php.net/ns3" + + diff --git a/ext/dom/tests/delayed_freeing/namespace_definition_crash_in_attribute.phpt b/ext/dom/tests/delayed_freeing/namespace_definition_crash_in_attribute.phpt index 64f2fbfa007a9..b43f7c34bae1d 100644 --- a/ext/dom/tests/delayed_freeing/namespace_definition_crash_in_attribute.phpt +++ b/ext/dom/tests/delayed_freeing/namespace_definition_crash_in_attribute.phpt @@ -39,9 +39,9 @@ echo $doc->saveXML($attr3->parentNode), "\n"; ?> --EXPECT-- - + - + string(15) "hello content 2" string(0) "" string(8) "some:ns2" @@ -49,5 +49,5 @@ NULL string(0) "" string(7) "some:ns" string(7) "some:ns" - hello="" + default1:hello="" From 6e3f93f2f8ecd222701a31dbf3109785dc4e2325 Mon Sep 17 00:00:00 2001 From: Patrick Allaert Date: Tue, 15 Aug 2023 21:09:58 +0200 Subject: [PATCH 15/47] PHP-8.1 is now for PHP 8.1.24-dev --- NEWS | 6 +++++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 1970eca1c734c..58a89b81cc318 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.1.23 +?? ??? ????, PHP 8.1.24 + + + +31 Aug 2023, PHP 8.1.23 - CLI: . Fixed bug GH-11716 (cli server crashes on SIGINT when compiled with diff --git a/Zend/zend.h b/Zend/zend.h index 237adfe01ddad..5b27b725db262 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.1.23-dev" +#define ZEND_VERSION "4.1.24-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 6d6d44e0597c4..0c723046adfdc 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.1.23-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.1.24-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index b1c3d7c65affb..7c1327eb68217 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 1 -#define PHP_RELEASE_VERSION 23 +#define PHP_RELEASE_VERSION 24 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.1.23-dev" -#define PHP_VERSION_ID 80123 +#define PHP_VERSION "8.1.24-dev" +#define PHP_VERSION_ID 80124 From 32fa67331bf164ac41c3f05ed291280e92426436 Mon Sep 17 00:00:00 2001 From: Eric Mann Date: Tue, 15 Aug 2023 12:45:42 -0700 Subject: [PATCH 16/47] Update NEWS for PHP 8.3.0beta3 --- NEWS | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 75bfad53c8d20..ae4b02a9de27f 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.3.0beta3 +?? ??? ????, PHP 8.3.0RC1 + + +17 Aug 2023, PHP 8.3.0beta3 - Core: . Fixed strerror_r detection at configuration time. (Kévin Dunglas) From 4467f33e89b1f9c9eb402dce164c74949e80ccd2 Mon Sep 17 00:00:00 2001 From: Pierrick Charron Date: Tue, 15 Aug 2023 16:08:52 -0400 Subject: [PATCH 17/47] PHP-8.2 is now for PHP 8.2.11-dev --- NEWS | 4 +++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 8d8884ef7757b..a961fde049189 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,8 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.2.10 +?? ??? ????, PHP 8.2.11 + +31 Aug 2023, PHP 8.2.10 - CLI: . Fixed bug GH-11716 (cli server crashes on SIGINT when compiled with diff --git a/Zend/zend.h b/Zend/zend.h index c7ef9d12fc2c7..eb2105abfa57d 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.2.10-dev" +#define ZEND_VERSION "4.2.11-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 23827974270d6..b7721b2fb6003 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.2.10-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.2.11-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index 7e4f058576ce5..afeeec17ccc3e 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 2 -#define PHP_RELEASE_VERSION 10 +#define PHP_RELEASE_VERSION 11 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.2.10-dev" -#define PHP_VERSION_ID 80210 +#define PHP_VERSION "8.2.11-dev" +#define PHP_VERSION_ID 80211 From 543eedf9e128f9bc76567d599005c8b5364d4a5a Mon Sep 17 00:00:00 2001 From: Ayesh Karunaratne Date: Wed, 16 Aug 2023 13:51:56 +0800 Subject: [PATCH 18/47] [skip-ci] minor typo fixes in UPGRADING and CONTRIBUTING.md (#11976) --- CONTRIBUTING.md | 8 ++++---- UPGRADING | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 60a630f5735ff..53ea64bcfc69f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -75,8 +75,8 @@ accompanied by [pull requests](#pull-requests). You can find the extremely large list of RFCs that have been previously considered on the [PHP Wiki](https://wiki.php.net/rfc). -To create a RFC, discuss it with the extension maintainer, and discuss it on the -development mailing list internals@lists.php.net. RFC Wiki accounts can be +To create an RFC, discuss it with the extension maintainer, and discuss it on +the development mailing list internals@lists.php.net. RFC Wiki accounts can be requested on https://wiki.php.net/start?do=register. PHP extension maintainers can be found in the [EXTENSIONS](/EXTENSIONS) file in the PHP source code repository. Mailing list subscription is explained on the @@ -318,7 +318,7 @@ detailed [information on Git](https://git-scm.com/). PHP is developed through the efforts of a large number of people. Collaboration is a Good Thing(tm), and Git lets us do this. Thus, following some basic rules -with regards to Git usage will: +with regard to Git usage will: * Make everybody happier, especially those responsible for maintaining PHP itself. @@ -348,7 +348,7 @@ Having said that, here are the organizational rules: `--enable-zts` switch to ensure your code handles TSRM correctly and doesn't break for those who need that. -Currently we have the following branches in use: +Currently, we have the following branches in use: | Branch | | | --------- | --------- | diff --git a/UPGRADING b/UPGRADING index 3a33f42af076f..156039af1c72a 100644 --- a/UPGRADING +++ b/UPGRADING @@ -258,7 +258,7 @@ PHP 8.3 UPGRADE NOTES now returns true on success, previously null was returned. . IntlBreakiterator::setText() now returns false on failure, previously null was returned. - now returns true on sucess, previously null was returned. + now returns true on success, previously null was returned. . IntlChar::enumCharNames is now returning a boolean. Previously it returned null on success and false on failure. @@ -449,7 +449,7 @@ PHP 8.3 UPGRADE NOTES A warning is emitted when trying to decrement values of type null, as this will change in the next major version. Internal objects that implement an _IS_NUMBER cast but not a do_operator - handler that overrides addition and substraction now can be incremented + handler that overrides addition and subtraction now can be incremented and decrement as if one would do $o += 1 or $o -= 1 - DOM: From 2b61f71046fc200484a491fe0389377021db4dbc Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 15 Aug 2023 20:57:38 +0200 Subject: [PATCH 19/47] Add test for SimpleXMLElement::asXML() with a fragment and a filename --- ...pleXMLElement_asXML_fragment_filename.phpt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 ext/simplexml/tests/SimpleXMLElement_asXML_fragment_filename.phpt diff --git a/ext/simplexml/tests/SimpleXMLElement_asXML_fragment_filename.phpt b/ext/simplexml/tests/SimpleXMLElement_asXML_fragment_filename.phpt new file mode 100644 index 0000000000000..0ab6c432499f8 --- /dev/null +++ b/ext/simplexml/tests/SimpleXMLElement_asXML_fragment_filename.phpt @@ -0,0 +1,29 @@ +--TEST-- +SimpleXMLElement::asXML() with a fragment and a filename +--EXTENSIONS-- +simplexml +--FILE-- + + + + bar + + +XML); +$sxe->container2->asXML(__DIR__."/SimpleXMLElement_asXML_fragment_filename_output.tmp"); + +// Note: the strange indent is correct: the indent text node preceding container2 is not emitted. +echo file_get_contents(__DIR__."/SimpleXMLElement_asXML_fragment_filename_output.tmp"); + +?> +--CLEAN-- + +--EXPECT-- + + bar + From c1103a97725060e3fa1418d83e25875aa4a0f400 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Fri, 24 Mar 2023 13:52:20 +0000 Subject: [PATCH 20/47] Fix implicit/explicit port in mysqlnd --- NEWS | 4 +++- ext/mysqli/tests/gh8978.phpt | 29 +++++++++++++++++++++++++++++ ext/mysqlnd/mysqlnd_commands.c | 2 ++ ext/mysqlnd/mysqlnd_connection.c | 17 +++++++++-------- ext/mysqlnd/mysqlnd_vio.c | 1 - 5 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 ext/mysqli/tests/gh8978.phpt diff --git a/NEWS b/NEWS index 58a89b81cc318..0d2bd565067b8 100644 --- a/NEWS +++ b/NEWS @@ -2,7 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.1.24 - +- MySQLnd: + . Fixed bug GH-10270 (Invalid error message when connection via SSL fails: + "trying to connect via (null)"). (Kamil Tekiela) 31 Aug 2023, PHP 8.1.23 diff --git a/ext/mysqli/tests/gh8978.phpt b/ext/mysqli/tests/gh8978.phpt new file mode 100644 index 0000000000000..394c07ee954ff --- /dev/null +++ b/ext/mysqli/tests/gh8978.phpt @@ -0,0 +1,29 @@ +--TEST-- +Bug GH-8267 (Invalid error message when connection via SSL fails) +--EXTENSIONS-- +mysqli +--SKIPIF-- + +--FILE-- +getMessage()."\n"; +} + +echo 'done!'; +?> +--EXPECTF-- +Warning: failed loading cafile stream: `x509.ca' in %s +Cannot connect to MySQL using SSL +done! diff --git a/ext/mysqlnd/mysqlnd_commands.c b/ext/mysqlnd/mysqlnd_commands.c index 40821bb1efedd..ae64560850531 100644 --- a/ext/mysqlnd/mysqlnd_commands.c +++ b/ext/mysqlnd/mysqlnd_commands.c @@ -571,6 +571,8 @@ MYSQLND_METHOD(mysqlnd_command, enable_ssl)(MYSQLND_CONN_DATA * const conn, cons conn->vio->data->m.set_client_option(conn->vio, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *) &verify); if (FAIL == conn->vio->data->m.enable_ssl(conn->vio)) { + SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT); + SET_CLIENT_ERROR(conn->error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "Cannot connect to MySQL using SSL"); goto end; } } diff --git a/ext/mysqlnd/mysqlnd_connection.c b/ext/mysqlnd/mysqlnd_connection.c index 4f720cdb15495..c905e38c8573d 100644 --- a/ext/mysqlnd/mysqlnd_connection.c +++ b/ext/mysqlnd/mysqlnd_connection.c @@ -289,7 +289,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, free_contents)(MYSQLND_CONN_DATA * conn) mysqlnd_set_persistent_string(&conn->unix_socket, NULL, 0, pers); DBG_INF_FMT("scheme=%s", conn->scheme.s); mysqlnd_set_persistent_string(&conn->scheme, NULL, 0, pers); - + if (conn->server_version) { mnd_pefree(conn->server_version, pers); conn->server_version = NULL; @@ -726,19 +726,20 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * conn, DBG_RETURN(PASS); } err: - if (transport.s) { - mnd_sprintf_free(transport.s); - transport.s = NULL; - } - - DBG_ERR_FMT("[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, conn->scheme.s); + DBG_ERR_FMT("[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, transport.s ? transport.s : conn->scheme.s); if (!conn->error_info->error_no) { + /* There was an unknown error if the connection failed but we have no error number */ char * msg; - mnd_sprintf(&msg, 0, "%s (trying to connect via %s)",conn->error_info->error, conn->scheme.s); + mnd_sprintf(&msg, 0, "Unknown error while trying to connect via %s", transport.s ? transport.s : conn->scheme.s); SET_CLIENT_ERROR(conn->error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, msg); mnd_sprintf_free(msg); } + if (transport.s) { + mnd_sprintf_free(transport.s); + transport.s = NULL; + } + conn->m->free_contents(conn); MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_FAILURE); DBG_RETURN(FAIL); diff --git a/ext/mysqlnd/mysqlnd_vio.c b/ext/mysqlnd/mysqlnd_vio.c index 2bd77906a1b9a..cd9c714f23bb9 100644 --- a/ext/mysqlnd/mysqlnd_vio.c +++ b/ext/mysqlnd/mysqlnd_vio.c @@ -569,7 +569,6 @@ MYSQLND_METHOD(mysqlnd_vio, enable_ssl)(MYSQLND_VIO * const net) php_stream_xport_crypto_enable(net_stream, 1) < 0) { DBG_ERR("Cannot connect to MySQL by using SSL"); - php_error_docref(NULL, E_WARNING, "Cannot connect to MySQL by using SSL"); DBG_RETURN(FAIL); } net->data->ssl = TRUE; From f78d1d0d10284b3599a099b783b56f16b338f728 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sat, 12 Aug 2023 16:11:29 +0200 Subject: [PATCH 21/47] Fix segfault in format_default_value due to unexpected enum/object Evaluating constants at comptime can result in arrays that contain objects. This is problematic for printing the default value of constant ASTs containing objects, because we don't actually know what the constructor arguments were. Avoid this by not propagating array constants. Fixes GH-11937 Closes GH-11947 --- NEWS | 3 +++ Zend/zend_compile.c | 40 +++++++++++++++++++++++++++-- ext/reflection/php_reflection.c | 1 + ext/reflection/tests/gh11937_1.inc | 13 ++++++++++ ext/reflection/tests/gh11937_1.phpt | 30 ++++++++++++++++++++++ ext/reflection/tests/gh11937_2.inc | 4 +++ ext/reflection/tests/gh11937_2.phpt | 27 +++++++++++++++++++ 7 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 ext/reflection/tests/gh11937_1.inc create mode 100644 ext/reflection/tests/gh11937_1.phpt create mode 100644 ext/reflection/tests/gh11937_2.inc create mode 100644 ext/reflection/tests/gh11937_2.phpt diff --git a/NEWS b/NEWS index 0d2bd565067b8..010acdaf17c63 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.1.24 +- Core: + . Fixed bug GH-11937 (Constant ASTs containing objects). (ilutov) + - MySQLnd: . Fixed bug GH-10270 (Invalid error message when connection via SSL fails: "trying to connect via (null)"). (Kamil Tekiela) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index af9be18d10f5b..351c2708bf409 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1458,6 +1458,35 @@ ZEND_API zend_result zend_unmangle_property_name_ex(const zend_string *name, con } /* }}} */ +static bool array_is_const_ex(zend_array *array, uint32_t *max_checks) +{ + if (zend_hash_num_elements(array) > *max_checks) { + return false; + } + *max_checks -= zend_hash_num_elements(array); + + zval *element; + ZEND_HASH_FOREACH_VAL(array, element) { + if (Z_TYPE_P(element) < IS_ARRAY) { + continue; + } else if (Z_TYPE_P(element) == IS_ARRAY) { + if (!array_is_const_ex(array, max_checks)) { + return false; + } + } else if (UNEXPECTED(Z_TYPE_P(element) >=IS_OBJECT)) { + return false; + } + } ZEND_HASH_FOREACH_END(); + + return true; +} + +static bool array_is_const(zend_array *array) +{ + uint32_t max_checks = 50; + return array_is_const_ex(array, &max_checks); +} + static bool can_ct_eval_const(zend_constant *c) { if (ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED) { return 0; @@ -1468,9 +1497,13 @@ static bool can_ct_eval_const(zend_constant *c) { && (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) { return 1; } - if (Z_TYPE(c->value) < IS_OBJECT + if (Z_TYPE(c->value) < IS_ARRAY && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) { return 1; + } else if (Z_TYPE(c->value) == IS_ARRAY + && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION) + && array_is_const(Z_ARR(c->value))) { + return 1; } return 0; } @@ -1690,7 +1723,10 @@ static bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend c = &cc->value; /* Substitute case-sensitive (or lowercase) persistent class constants */ - if (Z_TYPE_P(c) < IS_OBJECT) { + if (Z_TYPE_P(c) < IS_ARRAY) { + ZVAL_COPY_OR_DUP(zv, c); + return 1; + } else if (Z_TYPE_P(c) == IS_ARRAY && array_is_const(Z_ARR_P(c))) { ZVAL_COPY_OR_DUP(zv, c); return 1; } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index d3d5914866a1e..876338f4ab127 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -639,6 +639,7 @@ static int format_default_value(smart_str *str, zval *value) { } ZEND_HASH_FOREACH_END(); smart_str_appendc(str, ']'); } else if (Z_TYPE_P(value) == IS_OBJECT) { + /* This branch may only be reached for default properties, which don't support arbitrary objects. */ zend_object *obj = Z_OBJ_P(value); zend_class_entry *class = obj->ce; ZEND_ASSERT(class->ce_flags & ZEND_ACC_ENUM); diff --git a/ext/reflection/tests/gh11937_1.inc b/ext/reflection/tests/gh11937_1.inc new file mode 100644 index 0000000000000..4d55213f2f831 --- /dev/null +++ b/ext/reflection/tests/gh11937_1.inc @@ -0,0 +1,13 @@ +getAttributes('Attr')[0]; + +?> +--EXPECT-- +array(2) { + [0]=> + enum(TestEnum::One) + [1]=> + enum(TestEnum::Two) +} +Attribute [ Attr ] { + - Arguments [1] { + Argument #0 [ new \Foo(TestEnum::CASES) ] + } +} diff --git a/ext/reflection/tests/gh11937_2.inc b/ext/reflection/tests/gh11937_2.inc new file mode 100644 index 0000000000000..d6e21e6ba5c55 --- /dev/null +++ b/ext/reflection/tests/gh11937_2.inc @@ -0,0 +1,4 @@ +getAttributes('Attr')[0]; + +?> +--EXPECT-- +array(1) { + [0]=> + object(Foo)#1 (0) { + } +} +Attribute [ Attr ] { + - Arguments [1] { + Argument #0 [ FOOS ] + } +} From dd01c74a6fd7813fc41c658005447f9e2fc9e586 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 17 Aug 2023 18:54:30 +0200 Subject: [PATCH 22/47] Remove redundant condition Never refactor code just before pushing --- Zend/zend_compile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 351c2708bf409..75127b8b8d18a 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1473,7 +1473,7 @@ static bool array_is_const_ex(zend_array *array, uint32_t *max_checks) if (!array_is_const_ex(array, max_checks)) { return false; } - } else if (UNEXPECTED(Z_TYPE_P(element) >=IS_OBJECT)) { + } else { return false; } } ZEND_HASH_FOREACH_END(); From ffd398b4feed1069ffd3dd0d0ceeeafd5d76994b Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Thu, 17 Aug 2023 18:45:10 +0100 Subject: [PATCH 23/47] Fix failing test on nightly --- ext/mysqli/tests/gh8978.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/mysqli/tests/gh8978.phpt b/ext/mysqli/tests/gh8978.phpt index 394c07ee954ff..92de63b381ca8 100644 --- a/ext/mysqli/tests/gh8978.phpt +++ b/ext/mysqli/tests/gh8978.phpt @@ -16,7 +16,7 @@ $mysql = mysqli_init(); mysqli_ssl_set($mysql, 'x509.key', 'x509.pem', 'x509.ca', null, null); try { // There should be no warning here, only exception - mysqli_real_connect($mysql, '127.0.0.1:3306', 'username', 'password', null, null, null, MYSQLI_CLIENT_SSL); + mysqli_real_connect($mysql, $host, $user, $passwd, null, $port, null, MYSQLI_CLIENT_SSL); } catch (mysqli_sql_exception $e) { echo $e->getMessage()."\n"; } From 4ff93f779c0ecad1a79b0b9f43159b5eb2c9da94 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 18 Aug 2023 18:06:04 +0200 Subject: [PATCH 24/47] Remove unnecessary invalidation from processing instructions These invalidations only need to happen when elements are added, removed, or manipulated. Processing instructions are not elements and their contents are just text. --- ext/dom/processinginstruction.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/ext/dom/processinginstruction.c b/ext/dom/processinginstruction.c index afabab1d983ba..2e8695e276c2e 100644 --- a/ext/dom/processinginstruction.c +++ b/ext/dom/processinginstruction.c @@ -118,8 +118,6 @@ int dom_processinginstruction_data_write(dom_object *obj, zval *newval) ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING); zend_string *str = Z_STR_P(newval); - php_libxml_invalidate_node_list_cache_from_doc(nodep->doc); - xmlNodeSetContentLen(nodep, (xmlChar *) ZSTR_VAL(str), ZSTR_LEN(str)); return SUCCESS; From 7f1c3bf09bb83191eea9ddf23be032e2c14d1d37 Mon Sep 17 00:00:00 2001 From: ju1ius Date: Sat, 19 Aug 2023 01:11:06 +0200 Subject: [PATCH 25/47] Adds support for DNF types in internal functions and properties (#11969) Note that this does not add support for items generated by gen_stubs, only for items registered dynamically via the Zend API. Closes GH-10120 --- .../typed_properties_095.phpt | 4 + Zend/zend_API.c | 59 ++++++---- Zend/zend_compile.c | 2 +- Zend/zend_types.h | 15 ++- ext/zend_test/test.c | 105 ++++++++++++++++++ ext/zend_test/test.stub.php | 2 + ext/zend_test/test_arginfo.h | 8 +- .../tests/internal_dnf_arguments.phpt | 34 ++++++ 8 files changed, 205 insertions(+), 24 deletions(-) create mode 100644 ext/zend_test/tests/internal_dnf_arguments.phpt diff --git a/Zend/tests/type_declarations/typed_properties_095.phpt b/Zend/tests/type_declarations/typed_properties_095.phpt index 5caf862e72da7..321b07e34c73f 100644 --- a/Zend/tests/type_declarations/typed_properties_095.phpt +++ b/Zend/tests/type_declarations/typed_properties_095.phpt @@ -75,6 +75,8 @@ object(_ZendTestClass)#1 (3) { uninitialized(Traversable&Countable) ["readonlyProp"]=> uninitialized(int) + ["dnfProperty"]=> + uninitialized(Iterator|(Traversable&Countable)) } int(123) Cannot assign string to property _ZendTestClass::$intProp of type int @@ -91,6 +93,8 @@ object(Test)#4 (3) { uninitialized(Traversable&Countable) ["readonlyProp"]=> uninitialized(int) + ["dnfProperty"]=> + uninitialized(Iterator|(Traversable&Countable)) } int(123) Cannot assign string to property _ZendTestClass::$staticIntProp of type int diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 34ab803321aae..d0b863335e291 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2756,6 +2756,28 @@ ZEND_API void zend_add_magic_method(zend_class_entry *ce, zend_function *fptr, z ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arg_info_toString, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() +static zend_always_inline void zend_normalize_internal_type(zend_type *type) { + ZEND_ASSERT(!ZEND_TYPE_HAS_LITERAL_NAME(*type)); + zend_type *current; + ZEND_TYPE_FOREACH(*type, current) { + if (ZEND_TYPE_HAS_NAME(*current)) { + zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*current)); + zend_alloc_ce_cache(name); + ZEND_TYPE_SET_PTR(*current, name); + } else if (ZEND_TYPE_HAS_LIST(*current)) { + zend_type *inner; + ZEND_TYPE_FOREACH(*current, inner) { + ZEND_ASSERT(!ZEND_TYPE_HAS_LITERAL_NAME(*inner) && !ZEND_TYPE_HAS_LIST(*inner)); + if (ZEND_TYPE_HAS_NAME(*inner)) { + zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*inner)); + zend_alloc_ce_cache(name); + ZEND_TYPE_SET_PTR(*inner, name); + } + } ZEND_TYPE_FOREACH_END(); + } + } ZEND_TYPE_FOREACH_END(); +} + /* registers all functions in *library_functions in the function hash */ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend_function_entry *functions, HashTable *function_table, int type) /* {{{ */ { @@ -2934,10 +2956,12 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend memcpy(new_arg_info, arg_info, sizeof(zend_internal_arg_info) * num_args); reg_function->arg_info = new_arg_info + 1; for (i = 0; i < num_args; i++) { - if (ZEND_TYPE_IS_COMPLEX(new_arg_info[i].type)) { - ZEND_ASSERT(ZEND_TYPE_HAS_NAME(new_arg_info[i].type) - && "Should be stored as simple name"); + if (ZEND_TYPE_HAS_LITERAL_NAME(new_arg_info[i].type)) { + // gen_stubs.php does not support codegen for DNF types in arg infos. + // As a temporary workaround, we split the type name on `|` characters, + // converting it to an union type if necessary. const char *class_name = ZEND_TYPE_LITERAL_NAME(new_arg_info[i].type); + new_arg_info[i].type.type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT; size_t num_types = 1; const char *p = class_name; @@ -2948,8 +2972,10 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend if (num_types == 1) { /* Simple class type */ - ZEND_TYPE_SET_PTR(new_arg_info[i].type, - zend_string_init_interned(class_name, strlen(class_name), 1)); + zend_string *str = zend_string_init_interned(class_name, strlen(class_name), 1); + zend_alloc_ce_cache(str); + ZEND_TYPE_SET_PTR(new_arg_info[i].type, str); + new_arg_info[i].type.type_mask |= _ZEND_TYPE_NAME_BIT; } else { /* Union type */ zend_type_list *list = malloc(ZEND_TYPE_LIST_SIZE(num_types)); @@ -2961,8 +2987,8 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend uint32_t j = 0; while (true) { const char *end = strchr(start, '|'); - zend_string *str = zend_string_init_interned( - start, end ? end - start : strlen(start), 1); + zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), 1); + zend_alloc_ce_cache(str); list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0); if (!end) { break; @@ -2977,10 +3003,14 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend zend_error(E_CORE_WARNING, "iterable type is now a compile time alias for array|Traversable," " regenerate the argument info via the php-src gen_stub build script"); */ - zend_type legacy_iterable = ZEND_TYPE_INIT_CLASS_CONST_MASK(ZSTR_KNOWN(ZEND_STR_TRAVERSABLE), - (new_arg_info[i].type.type_mask|MAY_BE_ARRAY)); + zend_type legacy_iterable = ZEND_TYPE_INIT_CLASS_MASK( + ZSTR_KNOWN(ZEND_STR_TRAVERSABLE), + (new_arg_info[i].type.type_mask | MAY_BE_ARRAY) + ); new_arg_info[i].type = legacy_iterable; } + + zend_normalize_internal_type(&new_arg_info[i].type); } } @@ -4367,16 +4397,7 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z property_info->type = type; if (is_persistent_class(ce)) { - zend_type *single_type; - ZEND_TYPE_FOREACH(property_info->type, single_type) { - // TODO Add support and test cases when gen_stub support added - ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*single_type)); - if (ZEND_TYPE_HAS_NAME(*single_type)) { - zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*single_type)); - ZEND_TYPE_SET_PTR(*single_type, name); - zend_alloc_ce_cache(name); - } - } ZEND_TYPE_FOREACH_END(); + zend_normalize_internal_type(&property_info->type); } zend_hash_update_ptr(&ce->properties_info, name, property_info); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index dc7d00570279d..26705861c228c 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6438,7 +6438,7 @@ static zend_type zend_compile_single_typename(zend_ast *ast) /* Transform iterable into a type union alias */ if (type_code == IS_ITERABLE) { /* Set iterable bit for BC compat during Reflection and string representation of type */ - zend_type iterable = (zend_type) ZEND_TYPE_INIT_CLASS_CONST_MASK(ZSTR_KNOWN(ZEND_STR_TRAVERSABLE), + zend_type iterable = (zend_type) ZEND_TYPE_INIT_CLASS_MASK(ZSTR_KNOWN(ZEND_STR_TRAVERSABLE), (MAY_BE_ARRAY|_ZEND_TYPE_ITERABLE_BIT)); return iterable; } diff --git a/Zend/zend_types.h b/Zend/zend_types.h index df9bff9015eac..bf4fd9d18a41a 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -115,6 +115,7 @@ typedef void (*copy_ctor_func_t)(zval *pElement); * ZEND_TYPE_IS_ONLY_MASK() - checks if type-hint refer to standard type only * ZEND_TYPE_IS_COMPLEX() - checks if type is a type_list, or contains a class either as a CE or as a name * ZEND_TYPE_HAS_NAME() - checks if type-hint contains some class as zend_string * + * ZEND_TYPE_HAS_LITERAL_NAME() - checks if type-hint contains some class as const char * * ZEND_TYPE_IS_INTERSECTION() - checks if the type_list represents an intersection type list * ZEND_TYPE_IS_UNION() - checks if the type_list represents a union type list * @@ -145,8 +146,10 @@ typedef struct { #define _ZEND_TYPE_MASK ((1u << 25) - 1) /* Only one of these bits may be set. */ #define _ZEND_TYPE_NAME_BIT (1u << 24) +// Used to signify that type.ptr is not a `zend_string*` but a `const char*`, +#define _ZEND_TYPE_LITERAL_NAME_BIT (1u << 23) #define _ZEND_TYPE_LIST_BIT (1u << 22) -#define _ZEND_TYPE_KIND_MASK (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_NAME_BIT) +#define _ZEND_TYPE_KIND_MASK (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_NAME_BIT|_ZEND_TYPE_LITERAL_NAME_BIT) /* For BC behaviour with iterable type */ #define _ZEND_TYPE_ITERABLE_BIT (1u << 21) /* Whether the type list is arena allocated */ @@ -171,6 +174,9 @@ typedef struct { #define ZEND_TYPE_HAS_NAME(t) \ ((((t).type_mask) & _ZEND_TYPE_NAME_BIT) != 0) +#define ZEND_TYPE_HAS_LITERAL_NAME(t) \ + ((((t).type_mask) & _ZEND_TYPE_LITERAL_NAME_BIT) != 0) + #define ZEND_TYPE_HAS_LIST(t) \ ((((t).type_mask) & _ZEND_TYPE_LIST_BIT) != 0) @@ -289,11 +295,14 @@ typedef struct { #define ZEND_TYPE_INIT_CLASS(class_name, allow_null, extra_flags) \ ZEND_TYPE_INIT_PTR(class_name, _ZEND_TYPE_NAME_BIT, allow_null, extra_flags) +#define ZEND_TYPE_INIT_CLASS_MASK(class_name, type_mask) \ + ZEND_TYPE_INIT_PTR_MASK(class_name, _ZEND_TYPE_NAME_BIT | (type_mask)) + #define ZEND_TYPE_INIT_CLASS_CONST(class_name, allow_null, extra_flags) \ - ZEND_TYPE_INIT_PTR(class_name, _ZEND_TYPE_NAME_BIT, allow_null, extra_flags) + ZEND_TYPE_INIT_PTR(class_name, _ZEND_TYPE_LITERAL_NAME_BIT, allow_null, extra_flags) #define ZEND_TYPE_INIT_CLASS_CONST_MASK(class_name, type_mask) \ - ZEND_TYPE_INIT_PTR_MASK(class_name, _ZEND_TYPE_NAME_BIT | (type_mask)) + ZEND_TYPE_INIT_PTR_MASK(class_name, (_ZEND_TYPE_LITERAL_NAME_BIT | (type_mask))) typedef union _zend_value { zend_long lval; /* long value */ diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index b4ea2b27c9e9d..ec82c727650b3 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -884,11 +884,116 @@ static void le_throwing_resource_dtor(zend_resource *rsrc) zend_throw_exception(NULL, "Throwing resource destructor called", 0); } +static ZEND_METHOD(_ZendTestClass, takesUnionType) +{ + zend_object *obj; + ZEND_PARSE_PARAMETERS_START(1, 1); + Z_PARAM_OBJ(obj) + ZEND_PARSE_PARAMETERS_END(); + // we have to perform type-checking to avoid arginfo/zpp mismatch error + bool type_matches = ( + instanceof_function(obj->ce, zend_standard_class_def) + || + instanceof_function(obj->ce, zend_ce_iterator) + ); + if (!type_matches) { + zend_string *ty = zend_type_to_string(execute_data->func->internal_function.arg_info->type); + zend_argument_type_error(1, "must be of type %s, %s given", ty->val, obj->ce->name->val); + zend_string_release(ty); + RETURN_THROWS(); + } + + RETURN_NULL(); +} + +// Returns a newly allocated DNF type `Iterator|(Traversable&Countable)`. +// +// We need to generate it "manually" because gen_stubs.php does not support codegen for DNF types ATM. +static zend_type create_test_dnf_type(void) { + zend_string *class_Iterator = zend_string_init_interned("Iterator", sizeof("Iterator") - 1, true); + zend_alloc_ce_cache(class_Iterator); + zend_string *class_Traversable = ZSTR_KNOWN(ZEND_STR_TRAVERSABLE); + zend_string *class_Countable = zend_string_init_interned("Countable", sizeof("Countable") - 1, true); + zend_alloc_ce_cache(class_Countable); + // + zend_type_list *intersection_list = malloc(ZEND_TYPE_LIST_SIZE(2)); + intersection_list->num_types = 2; + intersection_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(class_Traversable, 0, 0); + intersection_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS(class_Countable, 0, 0); + zend_type_list *union_list = malloc(ZEND_TYPE_LIST_SIZE(2)); + union_list->num_types = 2; + union_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(class_Iterator, 0, 0); + union_list->types[1] = (zend_type) ZEND_TYPE_INIT_INTERSECTION(intersection_list, 0); + return (zend_type) ZEND_TYPE_INIT_UNION(union_list, 0); +} + +static void register_ZendTestClass_dnf_property(zend_class_entry *ce) { + zend_string *prop_name = zend_string_init_interned("dnfProperty", sizeof("dnfProperty") - 1, true); + zval default_value; + ZVAL_UNDEF(&default_value); + zend_type type = create_test_dnf_type(); + zend_declare_typed_property(ce, prop_name, &default_value, ZEND_ACC_PUBLIC, NULL, type); +} + +// arg_info for `zend_test_internal_dnf_arguments` +// The types are upgraded to DNF types in `register_dynamic_function_entries()` +static zend_internal_arg_info arginfo_zend_test_internal_dnf_arguments[] = { + // first entry is a zend_internal_function_info (see zend_compile.h): {argument_count, return_type, unused} + {(const char*)(uintptr_t)(1), {0}, NULL}, + {"arg", {0}, NULL} +}; + +static ZEND_NAMED_FUNCTION(zend_test_internal_dnf_arguments) +{ + zend_object *obj; + ZEND_PARSE_PARAMETERS_START(1, 1); + Z_PARAM_OBJ(obj) + ZEND_PARSE_PARAMETERS_END(); + // we have to perform type-checking to avoid arginfo/zpp mismatch error + bool type_matches = ( + instanceof_function(obj->ce, zend_ce_iterator) + || ( + instanceof_function(obj->ce, zend_ce_traversable) + && instanceof_function(obj->ce, zend_ce_countable) + ) + ); + if (!type_matches) { + zend_string *ty = zend_type_to_string(arginfo_zend_test_internal_dnf_arguments[1].type); + zend_argument_type_error(1, "must be of type %s, %s given", ty->val, obj->ce->name->val); + zend_string_release(ty); + RETURN_THROWS(); + } + + RETURN_OBJ_COPY(obj); +} + +static const zend_function_entry dynamic_function_entries[] = { + { + .fname = "zend_test_internal_dnf_arguments", + .handler = zend_test_internal_dnf_arguments, + .arg_info = arginfo_zend_test_internal_dnf_arguments, + .num_args = 1, + .flags = 0, + }, + ZEND_FE_END, +}; + +static void register_dynamic_function_entries(int module_type) { + // return-type is at index 0 + arginfo_zend_test_internal_dnf_arguments[0].type = create_test_dnf_type(); + arginfo_zend_test_internal_dnf_arguments[1].type = create_test_dnf_type(); + // + zend_register_functions(NULL, dynamic_function_entries, NULL, module_type); +} + PHP_MINIT_FUNCTION(zend_test) { + register_dynamic_function_entries(type); + zend_test_interface = register_class__ZendTestInterface(); zend_test_class = register_class__ZendTestClass(zend_test_interface); + register_ZendTestClass_dnf_property(zend_test_class); zend_test_class->create_object = zend_test_class_new; zend_test_class->get_static_method = zend_test_class_static_method_get; diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index afaf79da2acd0..312ad689b31d4 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -53,6 +53,8 @@ public function returnsStatic(): static {} public function returnsThrowable(): Throwable {} static public function variadicTest(string|Iterator ...$elements) : static {} + + public function takesUnionType(stdclass|Iterator $arg): void {} } class _ZendTestChildClass extends _ZendTestClass diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index a56090fd565af..d5546638949b1 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 87c580bffe8794d7597572c0d8571c7459420df8 */ + * Stub hash: b458993ee586284b1e33848313d9ddf61273604e */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -172,6 +172,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class__ZendTestClass_variadicTes ZEND_ARG_VARIADIC_OBJ_TYPE_MASK(0, elements, Iterator, MAY_BE_STRING) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class__ZendTestClass_takesUnionType, 0, 1, IS_VOID, 0) + ZEND_ARG_OBJ_TYPE_MASK(0, arg, stdclass|Iterator, 0, NULL) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class__ZendTestChildClass_returnsThrowable, 0, 0, Exception, 0) ZEND_END_ARG_INFO() @@ -258,6 +262,7 @@ static ZEND_METHOD(_ZendTestClass, __toString); static ZEND_METHOD(_ZendTestClass, returnsStatic); static ZEND_METHOD(_ZendTestClass, returnsThrowable); static ZEND_METHOD(_ZendTestClass, variadicTest); +static ZEND_METHOD(_ZendTestClass, takesUnionType); static ZEND_METHOD(_ZendTestChildClass, returnsThrowable); static ZEND_METHOD(_ZendTestTrait, testMethod); static ZEND_METHOD(ZendTestParameterAttribute, __construct); @@ -340,6 +345,7 @@ static const zend_function_entry class__ZendTestClass_methods[] = { ZEND_ME(_ZendTestClass, returnsStatic, arginfo_class__ZendTestClass_returnsStatic, ZEND_ACC_PUBLIC) ZEND_ME(_ZendTestClass, returnsThrowable, arginfo_class__ZendTestClass_returnsThrowable, ZEND_ACC_PUBLIC) ZEND_ME(_ZendTestClass, variadicTest, arginfo_class__ZendTestClass_variadicTest, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + ZEND_ME(_ZendTestClass, takesUnionType, arginfo_class__ZendTestClass_takesUnionType, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/ext/zend_test/tests/internal_dnf_arguments.phpt b/ext/zend_test/tests/internal_dnf_arguments.phpt new file mode 100644 index 0000000000000..eb6f5fb514f00 --- /dev/null +++ b/ext/zend_test/tests/internal_dnf_arguments.phpt @@ -0,0 +1,34 @@ +--TEST-- +DNF types for internal functions +--EXTENSIONS-- +zend_test +spl +reflection +--FILE-- +getReturnType()); +$paramType = $rf->getParameters()[0]->getType(); +var_dump((string)$paramType); + +try { + zend_test_internal_dnf_arguments(new stdClass); +} catch (\Throwable $err) { + echo $err->getMessage(), "\n"; +} + +$obj = new \ArrayIterator([]); +$result = zend_test_internal_dnf_arguments($obj); +var_dump($result); + +?> +--EXPECT-- +string(32) "Iterator|(Traversable&Countable)" +string(32) "Iterator|(Traversable&Countable)" +zend_test_internal_dnf_arguments(): Argument #1 ($arg) must be of type Iterator|(Traversable&Countable), stdClass given +object(ArrayIterator)#5 (1) { + ["storage":"ArrayIterator":private]=> + array(0) { + } +} From c180e9b48add3d99548b54cff082fc3d3de45c73 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sat, 19 Aug 2023 14:45:54 +0200 Subject: [PATCH 26/47] Remove unused call to Makefile.frag in ext/zip The Makefile.frag has been removed in ext/zip. --- ext/zip/config.m4 | 3 --- 1 file changed, 3 deletions(-) diff --git a/ext/zip/config.m4 b/ext/zip/config.m4 index 5111f91ee358b..3b60bb3e54303 100644 --- a/ext/zip/config.m4 +++ b/ext/zip/config.m4 @@ -65,7 +65,4 @@ if test "$PHP_ZIP" != "no"; then PHP_NEW_EXTENSION(zip, $PHP_ZIP_SOURCES, $ext_shared) PHP_SUBST(ZIP_SHARED_LIBADD) - - dnl so we always include the known-good working hack. - PHP_ADD_MAKEFILE_FRAGMENT fi From 85661a35f01604d8180eb20370803ce039c4c5a7 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sun, 30 Jul 2023 14:58:46 +0200 Subject: [PATCH 27/47] Remove `mysqli.reconnect` from php.ini files (#11836) The `mysqli.reconnect` ini directive was removed in PHP 8.2.0. --- php.ini-development | 3 --- php.ini-production | 3 --- 2 files changed, 6 deletions(-) diff --git a/php.ini-development b/php.ini-development index 62320e36e066d..61cd33e89504c 100644 --- a/php.ini-development +++ b/php.ini-development @@ -1201,9 +1201,6 @@ mysqli.default_user = ; https://php.net/mysqli.default-pw mysqli.default_pw = -; Allow or prevent reconnect -mysqli.reconnect = Off - ; If this option is enabled, closing a persistent connection will rollback ; any pending transactions of this connection, before it is put back ; into the persistent connection pool. diff --git a/php.ini-production b/php.ini-production index cb36654a72672..2189660c02c48 100644 --- a/php.ini-production +++ b/php.ini-production @@ -1203,9 +1203,6 @@ mysqli.default_user = ; https://php.net/mysqli.default-pw mysqli.default_pw = -; Allow or prevent reconnect -mysqli.reconnect = Off - ; If this option is enabled, closing a persistent connection will rollback ; any pending transactions of this connection, before it is put back ; into the persistent connection pool. From fc8d5c72e5e62a9486981960d4a94b39789044c4 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 19 Aug 2023 19:20:01 +0100 Subject: [PATCH 28/47] ext/iconv: fix build for netbsd. NetBSD still adopts the old iconv signature for buffer inputs. The next release will too so we can assume it will remain that way for a while. Close GH-12001 --- NEWS | 4 ++++ ext/iconv/iconv.c | 26 +++++++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index 010acdaf17c63..d3e73e95c9752 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,10 @@ PHP NEWS - Core: . Fixed bug GH-11937 (Constant ASTs containing objects). (ilutov) +- Iconv: + . Fixed build for NetBSD which still uses the old iconv signature. + (David Carlier) + - MySQLnd: . Fixed bug GH-10270 (Invalid error message when connection via SSL fails: "trying to connect via (null)"). (Kamil Tekiela) diff --git a/ext/iconv/iconv.c b/ext/iconv/iconv.c index f05e58aa1d953..83d89a0df4835 100644 --- a/ext/iconv/iconv.c +++ b/ext/iconv/iconv.c @@ -44,6 +44,14 @@ #undef iconv #endif +#if defined(__NetBSD__) +// unfortunately, netbsd has still the old non posix conformant signature +// libiconv tends to match the eventual system's iconv too. +#define ICONV_CONST const +#else +#define ICONV_CONST +#endif + #include "zend_smart_str.h" #include "ext/standard/base64.h" #include "ext/standard/quot_print.h" @@ -360,7 +368,7 @@ static php_iconv_err_t _php_iconv_appendl(smart_str *d, const char *s, size_t l, out_p = ZSTR_VAL((d)->s) + ZSTR_LEN((d)->s); - if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) { + if (iconv(cd, (ICONV_CONST char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) { switch (errno) { case EINVAL: return PHP_ICONV_ERR_ILLEGAL_CHAR; @@ -456,7 +464,7 @@ PHP_ICONV_API php_iconv_err_t php_iconv_string(const char *in_p, size_t in_len, out_p = ZSTR_VAL(out_buf); while (in_left > 0) { - result = iconv(cd, (char **) &in_p, &in_left, (char **) &out_p, &out_left); + result = iconv(cd, (ICONV_CONST char **) &in_p, &in_left, (char **) &out_p, &out_left); out_size = bsz - out_left; if (result == (size_t)(-1)) { if (ignore_ilseq && errno == EILSEQ) { @@ -576,7 +584,7 @@ static php_iconv_err_t _php_iconv_strlen(size_t *pretval, const char *str, size_ more = in_left > 0; - iconv(cd, more ? (char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left); + iconv(cd, more ? (ICONV_CONST char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left); if (out_left == sizeof(buf)) { break; } else { @@ -683,7 +691,7 @@ static php_iconv_err_t _php_iconv_substr(smart_str *pretval, more = in_left > 0 && len > 0; - iconv(cd1, more ? (char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left); + iconv(cd1, more ? (ICONV_CONST char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left); if (out_left == sizeof(buf)) { break; } @@ -805,7 +813,7 @@ static php_iconv_err_t _php_iconv_strpos(size_t *pretval, more = in_left > 0; - iconv_ret = iconv(cd, more ? (char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left); + iconv_ret = iconv(cd, more ? (ICONV_CONST char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left); if (out_left == sizeof(buf)) { break; } @@ -1012,7 +1020,7 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn out_left = out_size - out_reserved; - if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) { + if (iconv(cd, (ICONV_CONST char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) { switch (errno) { case EINVAL: err = PHP_ICONV_ERR_ILLEGAL_CHAR; @@ -1096,7 +1104,7 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn out_p = buf; out_left = out_size; - if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) { + if (iconv(cd, (ICONV_CONST char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) { switch (errno) { case EINVAL: err = PHP_ICONV_ERR_ILLEGAL_CHAR; @@ -2373,7 +2381,7 @@ static int php_iconv_stream_filter_append_bucket( tcnt = self->stub_len; while (tcnt > 0) { - if (iconv(self->cd, &pt, &tcnt, &pd, &ocnt) == (size_t)-1) { + if (iconv(self->cd, (ICONV_CONST char **)&pt, &tcnt, &pd, &ocnt) == (size_t)-1) { switch (errno) { case EILSEQ: php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset); @@ -2439,7 +2447,7 @@ static int php_iconv_stream_filter_append_bucket( while (icnt > 0) { if ((ps == NULL ? iconv(self->cd, NULL, NULL, &pd, &ocnt): - iconv(self->cd, (char **)&ps, &icnt, &pd, &ocnt)) == (size_t)-1) { + iconv(self->cd, (ICONV_CONST char **)&ps, &icnt, &pd, &ocnt)) == (size_t)-1) { switch (errno) { case EILSEQ: php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset); From 20ac42e1b065e23376e7ea548995636369809a7d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 19 Aug 2023 23:54:53 +0200 Subject: [PATCH 29/47] Fix memory leak when setting an invalid DOMDocument encoding Because the failure path did not release the string, there was a memory leak. As the only valid types for this function are IS_NULL and IS_STRING, we and IS_NULL is always rejected in practice, solve the issue by not using a function that increments the refcount in the first place. Closes GH-12002. --- NEWS | 3 +++ ext/dom/document.c | 19 ++++++++++++------- ext/dom/tests/gh12002.phpt | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 ext/dom/tests/gh12002.phpt diff --git a/NEWS b/NEWS index d3e73e95c9752..a4d3499a58315 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,9 @@ PHP NEWS - Core: . Fixed bug GH-11937 (Constant ASTs containing objects). (ilutov) +- DOM: + . Fix memory leak when setting an invalid DOMDocument encoding. (nielsdos) + - Iconv: . Fixed build for NetBSD which still uses the old iconv signature. (David Carlier) diff --git a/ext/dom/document.c b/ext/dom/document.c index ea6daafeb30fb..64da4f051be2c 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -139,7 +139,6 @@ int dom_document_encoding_read(dom_object *obj, zval *retval) zend_result dom_document_encoding_write(dom_object *obj, zval *newval) { xmlDoc *docp = (xmlDocPtr) dom_object_get_node(obj); - zend_string *str; xmlCharEncodingHandlerPtr handler; if (docp == NULL) { @@ -147,11 +146,15 @@ zend_result dom_document_encoding_write(dom_object *obj, zval *newval) return FAILURE; } - str = zval_try_get_string(newval); - if (UNEXPECTED(!str)) { - return FAILURE; + /* Typed property, can only be IS_STRING or IS_NULL. */ + ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING || Z_TYPE_P(newval) == IS_NULL); + + if (Z_TYPE_P(newval) == IS_NULL) { + goto invalid_encoding; } + zend_string *str = Z_STR_P(newval); + handler = xmlFindCharEncodingHandler(ZSTR_VAL(str)); if (handler != NULL) { @@ -161,12 +164,14 @@ zend_result dom_document_encoding_write(dom_object *obj, zval *newval) } docp->encoding = xmlStrdup((const xmlChar *) ZSTR_VAL(str)); } else { - zend_value_error("Invalid document encoding"); - return FAILURE; + goto invalid_encoding; } - zend_string_release_ex(str, 0); return SUCCESS; + +invalid_encoding: + zend_value_error("Invalid document encoding"); + return FAILURE; } /* }}} */ diff --git a/ext/dom/tests/gh12002.phpt b/ext/dom/tests/gh12002.phpt new file mode 100644 index 0000000000000..7d1ae944646ae --- /dev/null +++ b/ext/dom/tests/gh12002.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-12002 (DOMDocument::encoding memory leak with invalid encoding) +--EXTENSIONS-- +dom +--FILE-- +encoding = make_nonconst('utf-8'); +var_dump($dom->encoding); +try { + $dom->encoding = make_nonconst('foobar'); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +var_dump($dom->encoding); +$dom->encoding = make_nonconst('utf-16le'); +var_dump($dom->encoding); +try { + $dom->encoding = NULL; +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +var_dump($dom->encoding); + +?> +--EXPECT-- +string(5) "utf-8" +Invalid document encoding +string(5) "utf-8" +string(8) "utf-16le" +Invalid document encoding +string(8) "utf-16le" From e6627ccb520fd02df4513066f095fb3cedb546b9 Mon Sep 17 00:00:00 2001 From: Ayesh Karunaratne Date: Sun, 20 Aug 2023 20:20:56 +0800 Subject: [PATCH 30/47] gen_stub: fix regexps with unintentional range due to `-` character placement (#12004) --- build/gen_stub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index 1fff123d84c0d..0ab8ca26adfb8 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -272,7 +272,7 @@ public static function fromString(string $typeString): SimpleType } $matches = []; - $isArray = preg_match("/array\s*<\s*([A-Za-z0-9_-|]+)?(\s*,\s*)?([A-Za-z0-9_-|]+)?\s*>/i", $typeString, $matches); + $isArray = preg_match("/array\s*<\s*([A-Za-z0-9_|-]+)?(\s*,\s*)?([A-Za-z0-9_|-]+)?\s*>/i", $typeString, $matches); if ($isArray) { if (empty($matches[1]) || empty($matches[3])) { throw new Exception("array<> type hint must have both a key and a value"); From 278a57f95a30253d2a462baca43144f4dabad4c6 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sun, 20 Aug 2023 21:53:45 +0200 Subject: [PATCH 31/47] Add all README.* files to paths-ignore (#12003) Within the entire repository these are documentation files and CI don't need to run when they are changed. This now includes also README.REDIST.BINS, README.md, and similar README files within the sapi and ext directories. README files in tests directories are also only for internals documentations purposes. --- .github/workflows/push.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 236fb31c28ecf..8481b10a5e36e 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -6,7 +6,7 @@ on: - NEWS - UPGRADING - UPGRADING.INTERNALS - - README.md + - '**/README.*' - CONTRIBUTING.md - CODING_STANDARDS.md branches: @@ -21,7 +21,7 @@ on: - NEWS - UPGRADING - UPGRADING.INTERNALS - - README.md + - '**/README.*' - CONTRIBUTING.md - CODING_STANDARDS.md branches: From 1887f02b0b39f1cdb89343a4d9fd9372aedb33b6 Mon Sep 17 00:00:00 2001 From: Ayesh Karunaratne Date: Mon, 21 Aug 2023 13:14:45 +0800 Subject: [PATCH 32/47] Add class constant types to Phar extension (#11826) --- UPGRADING | 3 +++ ext/phar/phar_object.stub.php | 48 ++++++++++++---------------------- ext/phar/phar_object_arginfo.h | 34 ++++++++++++------------ 3 files changed, 36 insertions(+), 49 deletions(-) diff --git a/UPGRADING b/UPGRADING index 156039af1c72a..3e8f694c801eb 100644 --- a/UPGRADING +++ b/UPGRADING @@ -73,6 +73,9 @@ PHP 8.3 UPGRADE NOTES modifiable pointers but was rejected due to complexity. For this reason, it was decided to remove the feature instead. +- Phar: + . The type of Phar class constants are now declared. + - Standard: . The range() function has had various changes: * A TypeError is now thrown when passing objects, resources, or arrays diff --git a/ext/phar/phar_object.stub.php b/ext/phar/phar_object.stub.php index 3004a1ee83f43..f39fdba5a48fe 100644 --- a/ext/phar/phar_object.stub.php +++ b/ext/phar/phar_object.stub.php @@ -9,85 +9,69 @@ class PharException extends Exception class Phar extends RecursiveDirectoryIterator implements Countable, ArrayAccess { /** - * @var int * @cvalue PHAR_ENT_COMPRESSED_BZ2 */ - const BZ2 = UNKNOWN; + const int BZ2 = UNKNOWN; /** - * @var int * @cvalue PHAR_ENT_COMPRESSED_GZ */ - const GZ = UNKNOWN; + const int GZ = UNKNOWN; /** - * @var int * @cvalue PHAR_ENT_COMPRESSED_NONE */ - const NONE = UNKNOWN; + const int NONE = UNKNOWN; /** - * @var int * @cvalue PHAR_FORMAT_PHAR */ - const PHAR = UNKNOWN; + const int PHAR = UNKNOWN; /** - * @var int * @cvalue PHAR_FORMAT_TAR */ - const TAR = UNKNOWN; + const int TAR = UNKNOWN; /** - * @var int * @cvalue PHAR_FORMAT_ZIP */ - const ZIP = UNKNOWN; + const int ZIP = UNKNOWN; /** - * @var int * @cvalue PHAR_ENT_COMPRESSION_MASK */ - const COMPRESSED = UNKNOWN; + const int COMPRESSED = UNKNOWN; /** - * @var int * @cvalue PHAR_MIME_PHP */ - const PHP = UNKNOWN; + const int PHP = UNKNOWN; /** - * @var int * @cvalue PHAR_MIME_PHPS */ - const PHPS = UNKNOWN; + const int PHPS = UNKNOWN; /** - * @var int * @cvalue PHAR_SIG_MD5 */ - const MD5 = UNKNOWN; + const int MD5 = UNKNOWN; /** - * @var int * @cvalue PHAR_SIG_OPENSSL */ - const OPENSSL = UNKNOWN; + const int OPENSSL = UNKNOWN; /** - * @var int * @cvalue PHAR_SIG_OPENSSL_SHA256 */ - const OPENSSL_SHA256 = UNKNOWN; + const int OPENSSL_SHA256 = UNKNOWN; /** - * @var int * @cvalue PHAR_SIG_OPENSSL_SHA512 */ - const OPENSSL_SHA512 = UNKNOWN; + const int OPENSSL_SHA512 = UNKNOWN; /** - * @var int * @cvalue PHAR_SIG_SHA1 */ - const SHA1 = UNKNOWN; + const int SHA1 = UNKNOWN; /** - * @var int * @cvalue PHAR_SIG_SHA256 */ - const SHA256 = UNKNOWN; + const int SHA256 = UNKNOWN; /** - * @var int * @cvalue PHAR_SIG_SHA512 */ - const SHA512 = UNKNOWN; + const int SHA512 = UNKNOWN; public function __construct(string $filename, int $flags = FilesystemIterator::SKIP_DOTS|FilesystemIterator::UNIX_PATHS, ?string $alias = null) {} diff --git a/ext/phar/phar_object_arginfo.h b/ext/phar/phar_object_arginfo.h index 17d96a94a0eb8..b19b4998c25fe 100644 --- a/ext/phar/phar_object_arginfo.h +++ b/ext/phar/phar_object_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 799b653b10dbdfa58747e41715700cfd5300fa4f */ + * Stub hash: 00f5d4fc74e8b7dc67da6f12180c9fae343954cc */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Phar___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) @@ -625,97 +625,97 @@ static zend_class_entry *register_class_Phar(zend_class_entry *class_entry_Recur zval const_BZ2_value; ZVAL_LONG(&const_BZ2_value, PHAR_ENT_COMPRESSED_BZ2); zend_string *const_BZ2_name = zend_string_init_interned("BZ2", sizeof("BZ2") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_BZ2_name, &const_BZ2_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_BZ2_name, &const_BZ2_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_BZ2_name); zval const_GZ_value; ZVAL_LONG(&const_GZ_value, PHAR_ENT_COMPRESSED_GZ); zend_string *const_GZ_name = zend_string_init_interned("GZ", sizeof("GZ") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_GZ_name, &const_GZ_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_GZ_name, &const_GZ_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_GZ_name); zval const_NONE_value; ZVAL_LONG(&const_NONE_value, PHAR_ENT_COMPRESSED_NONE); zend_string *const_NONE_name = zend_string_init_interned("NONE", sizeof("NONE") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_NONE_name, &const_NONE_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_NONE_name, &const_NONE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_NONE_name); zval const_PHAR_value; ZVAL_LONG(&const_PHAR_value, PHAR_FORMAT_PHAR); zend_string *const_PHAR_name = zend_string_init_interned("PHAR", sizeof("PHAR") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_PHAR_name, &const_PHAR_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_PHAR_name, &const_PHAR_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_PHAR_name); zval const_TAR_value; ZVAL_LONG(&const_TAR_value, PHAR_FORMAT_TAR); zend_string *const_TAR_name = zend_string_init_interned("TAR", sizeof("TAR") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_TAR_name, &const_TAR_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_TAR_name, &const_TAR_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_TAR_name); zval const_ZIP_value; ZVAL_LONG(&const_ZIP_value, PHAR_FORMAT_ZIP); zend_string *const_ZIP_name = zend_string_init_interned("ZIP", sizeof("ZIP") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_ZIP_name, &const_ZIP_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_ZIP_name, &const_ZIP_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_ZIP_name); zval const_COMPRESSED_value; ZVAL_LONG(&const_COMPRESSED_value, PHAR_ENT_COMPRESSION_MASK); zend_string *const_COMPRESSED_name = zend_string_init_interned("COMPRESSED", sizeof("COMPRESSED") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_COMPRESSED_name, &const_COMPRESSED_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_COMPRESSED_name, &const_COMPRESSED_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_COMPRESSED_name); zval const_PHP_value; ZVAL_LONG(&const_PHP_value, PHAR_MIME_PHP); zend_string *const_PHP_name = zend_string_init_interned("PHP", sizeof("PHP") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_PHP_name, &const_PHP_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_PHP_name, &const_PHP_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_PHP_name); zval const_PHPS_value; ZVAL_LONG(&const_PHPS_value, PHAR_MIME_PHPS); zend_string *const_PHPS_name = zend_string_init_interned("PHPS", sizeof("PHPS") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_PHPS_name, &const_PHPS_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_PHPS_name, &const_PHPS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_PHPS_name); zval const_MD5_value; ZVAL_LONG(&const_MD5_value, PHAR_SIG_MD5); zend_string *const_MD5_name = zend_string_init_interned("MD5", sizeof("MD5") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_MD5_name, &const_MD5_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_MD5_name, &const_MD5_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_MD5_name); zval const_OPENSSL_value; ZVAL_LONG(&const_OPENSSL_value, PHAR_SIG_OPENSSL); zend_string *const_OPENSSL_name = zend_string_init_interned("OPENSSL", sizeof("OPENSSL") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_OPENSSL_name, &const_OPENSSL_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_OPENSSL_name, &const_OPENSSL_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPENSSL_name); zval const_OPENSSL_SHA256_value; ZVAL_LONG(&const_OPENSSL_SHA256_value, PHAR_SIG_OPENSSL_SHA256); zend_string *const_OPENSSL_SHA256_name = zend_string_init_interned("OPENSSL_SHA256", sizeof("OPENSSL_SHA256") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_OPENSSL_SHA256_name, &const_OPENSSL_SHA256_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_OPENSSL_SHA256_name, &const_OPENSSL_SHA256_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPENSSL_SHA256_name); zval const_OPENSSL_SHA512_value; ZVAL_LONG(&const_OPENSSL_SHA512_value, PHAR_SIG_OPENSSL_SHA512); zend_string *const_OPENSSL_SHA512_name = zend_string_init_interned("OPENSSL_SHA512", sizeof("OPENSSL_SHA512") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_OPENSSL_SHA512_name, &const_OPENSSL_SHA512_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_OPENSSL_SHA512_name, &const_OPENSSL_SHA512_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPENSSL_SHA512_name); zval const_SHA1_value; ZVAL_LONG(&const_SHA1_value, PHAR_SIG_SHA1); zend_string *const_SHA1_name = zend_string_init_interned("SHA1", sizeof("SHA1") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_SHA1_name, &const_SHA1_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_SHA1_name, &const_SHA1_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_SHA1_name); zval const_SHA256_value; ZVAL_LONG(&const_SHA256_value, PHAR_SIG_SHA256); zend_string *const_SHA256_name = zend_string_init_interned("SHA256", sizeof("SHA256") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_SHA256_name, &const_SHA256_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_SHA256_name, &const_SHA256_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_SHA256_name); zval const_SHA512_value; ZVAL_LONG(&const_SHA512_value, PHAR_SIG_SHA512); zend_string *const_SHA512_name = zend_string_init_interned("SHA512", sizeof("SHA512") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_SHA512_name, &const_SHA512_value, ZEND_ACC_PUBLIC, NULL); + zend_declare_typed_class_constant(class_entry, const_SHA512_name, &const_SHA512_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_SHA512_name); return class_entry; From 9026596fe96a36ef03e2f220a1791c0d5d9f925f Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Mon, 21 Aug 2023 16:38:04 +0200 Subject: [PATCH 33/47] Sync --enable-mysqlnd-compression-support option (#12006) This syncs the style of the --enable-mysqlnd-compression-support option name, otherwise in Autoconf both --enable-foo_bar and --enable-foo-bar work. Also the configure output message is synced to match the check information. --- ext/mysqlnd/config9.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/mysqlnd/config9.m4 b/ext/mysqlnd/config9.m4 index 188023cbb5cde..80a6301076566 100644 --- a/ext/mysqlnd/config9.m4 +++ b/ext/mysqlnd/config9.m4 @@ -6,8 +6,8 @@ PHP_ARG_ENABLE([mysqlnd], [no], [yes]) -PHP_ARG_ENABLE([mysqlnd_compression_support], - [whether to disable compressed protocol support in mysqlnd], +PHP_ARG_ENABLE([mysqlnd-compression-support], + [whether to enable compressed protocol support in mysqlnd], [AS_HELP_STRING([--disable-mysqlnd-compression-support], [Disable support for the MySQL compressed protocol in mysqlnd])], [yes], From e199104a353bceaa1da80f9316d5bb2b8d556f4f Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 22 Aug 2023 03:51:31 +0200 Subject: [PATCH 34/47] Fix passing null to parameter of type string (#12014) This fixes builds without cgi or phpdbg: ./configure --disable-cgi --disable-phpdbg make ./sapi/cli/php run-tests.php Otherwise, deprecation warnings (since PHP-8.1) are emitted: Deprecated: escapeshellarg(): Passing null to parameter #1 ($arg) of type string is deprecated in run-tests.php... --- run-tests.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/run-tests.php b/run-tests.php index 6f10e4090bdbf..1bcc6aa7b22d9 100755 --- a/run-tests.php +++ b/run-tests.php @@ -683,8 +683,8 @@ function main(): void $environment['TEST_PHP_EXECUTABLE_ESCAPED'] = escapeshellarg($php); putenv("TEST_PHP_CGI_EXECUTABLE=$php_cgi"); $environment['TEST_PHP_CGI_EXECUTABLE'] = $php_cgi; - putenv("TEST_PHP_CGI_EXECUTABLE_ESCAPED=" . escapeshellarg($php_cgi)); - $environment['TEST_PHP_CGI_EXECUTABLE_ESCAPED'] = escapeshellarg($php_cgi); + putenv("TEST_PHP_CGI_EXECUTABLE_ESCAPED=" . escapeshellarg($php_cgi ?? '')); + $environment['TEST_PHP_CGI_EXECUTABLE_ESCAPED'] = escapeshellarg($php_cgi ?? ''); putenv("TEST_PHPDBG_EXECUTABLE=$phpdbg"); $environment['TEST_PHPDBG_EXECUTABLE'] = $phpdbg; putenv("TEST_PHPDBG_EXECUTABLE_ESCAPED=" . escapeshellarg($phpdbg ?? '')); From adc4a48d26e33f506dcc5e3492b2249cee76724f Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 22 Aug 2023 03:52:06 +0200 Subject: [PATCH 35/47] Fix configure phpdbg help output (#12013) By default phpdbg is enabled (--enable-phpdbg) and user can get info in the `./configure --help` output if they want to disable it like with the other configuration options. --- sapi/phpdbg/config.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sapi/phpdbg/config.m4 b/sapi/phpdbg/config.m4 index ac95cf3c57f91..10dd2029b4c32 100644 --- a/sapi/phpdbg/config.m4 +++ b/sapi/phpdbg/config.m4 @@ -1,7 +1,7 @@ PHP_ARG_ENABLE([phpdbg], [for phpdbg support], - [AS_HELP_STRING([--enable-phpdbg], - [Build phpdbg])], + [AS_HELP_STRING([--disable-phpdbg], + [Disable building of phpdbg])], [yes], [yes]) From 32cdd330f33f01605f17d39c9ddd27cbe783f00c Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 22 Aug 2023 11:21:42 +0200 Subject: [PATCH 36/47] Remove unneeded zend_language_parser.h patch (#11974) This was cleaned in 4cbffd89d9e82d81a26746aadca27ad061cab43a and patching the Zend/zend_language_parser.h file to include zend.h is not needed anymore. --- Zend/Makefile.frag | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Zend/Makefile.frag b/Zend/Makefile.frag index 054a63bf60af5..9d7f1c55aade4 100644 --- a/Zend/Makefile.frag +++ b/Zend/Makefile.frag @@ -20,10 +20,6 @@ $(srcdir)/zend_language_parser.c: $(srcdir)/zend_language_parser.y @$(SED) -e 's,^int zendparse\(.*\),ZEND_API int zendparse\1,g' < $(srcdir)/zend_language_parser.h \ > $(srcdir)/zend_language_parser.h.tmp && \ mv $(srcdir)/zend_language_parser.h.tmp $(srcdir)/zend_language_parser.h - @nlinit=`echo 'nl="'; echo '"'`; eval "$$nlinit"; \ - $(SED) -e "s/^#ifndef YYTOKENTYPE/#include \"zend.h\"\\$${nl}#ifndef YYTOKENTYPE/" < $(srcdir)/zend_language_parser.h \ - > $(srcdir)/zend_language_parser.h.tmp && \ - mv $(srcdir)/zend_language_parser.h.tmp $(srcdir)/zend_language_parser.h $(srcdir)/zend_ini_parser.h: $(srcdir)/zend_ini_parser.c $(srcdir)/zend_ini_parser.c: $(srcdir)/zend_ini_parser.y From 782ffd761baaef016608efc71508eff8f7fd6d73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristian=20Rodr=C3=ADguez?= Date: Tue, 22 Aug 2023 11:40:24 -0400 Subject: [PATCH 37/47] Use a single version of strnlen (#12015) * Zend: Make zend_strnlen available for use outside zend_compile * exif: remove local php_strnlen, use zend_strnlen instead * main: remove local strnlen, use zend_strnlen instead * phar: remove local strnlen, use zend_strnlen --- Zend/zend_compile.c | 8 -------- Zend/zend_operators.h | 10 ++++++++++ ext/exif/exif.c | 26 +++++++------------------- ext/phar/tar.c | 11 ++--------- main/spprintf.c | 9 +-------- 5 files changed, 20 insertions(+), 44 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 26705861c228c..8d56da9eac6d1 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1499,14 +1499,6 @@ ZEND_API zend_string *zend_mangle_property_name(const char *src1, size_t src1_le } /* }}} */ -static zend_always_inline size_t zend_strnlen(const char* s, size_t maxlen) /* {{{ */ -{ - size_t len = 0; - while (*s++ && maxlen--) len++; - return len; -} -/* }}} */ - ZEND_API zend_result zend_unmangle_property_name_ex(const zend_string *name, const char **class_name, const char **prop_name, size_t *prop_len) /* {{{ */ { size_t class_name_len; diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index 4b47debc8032d..2c2d8ed7aa7fb 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -264,6 +264,16 @@ zend_memnrstr(const char *haystack, const char *needle, size_t needle_len, const } } +static zend_always_inline size_t zend_strnlen(const char* s, size_t maxlen) +{ +#if defined(HAVE_STRNLEN) + return strnlen(s, maxlen); +#else + const char *p = memchr(s, '\0', maxlen); + return p ? p-s : maxlen; +#endif +} + ZEND_API zend_result ZEND_FASTCALL increment_function(zval *op1); ZEND_API zend_result ZEND_FASTCALL decrement_function(zval *op2); diff --git a/ext/exif/exif.c b/ext/exif/exif.c index fa5ec9c855d4a..aa83fd969ef42 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -234,18 +234,6 @@ static ssize_t exif_read_from_stream_file_looped(php_stream *stream, char *buf, return total_read; } -/* {{{ php_strnlen - * get length of string if buffer if less than buffer size or buffer size */ -static size_t php_strnlen(char* str, size_t maxlen) { - size_t len = 0; - - if (str && maxlen && *str) { - do { - len++; - } while (--maxlen && *(++str)); - } - return len; -} /* }}} */ /* {{{ error messages */ @@ -2223,7 +2211,7 @@ static void exif_iif_add_value(image_info_type *image_info, int section_index, c value = NULL; } if (value) { - length = (int)php_strnlen(value, length); + length = (int)zend_strnlen(value, length); info_value->s = estrndup(value, length); info_data->length = length; } else { @@ -2254,7 +2242,7 @@ static void exif_iif_add_value(image_info_type *image_info, int section_index, c } if (value) { if (tag == TAG_MAKER_NOTE) { - length = (int) php_strnlen(value, length); + length = (int) zend_strnlen(value, length); } /* do not recompute length here */ @@ -3034,11 +3022,11 @@ static int exif_process_string(char **result, char *value, size_t byte_count) { /* we cannot use strlcpy - here the problem is that we cannot use strlen to * determine length of string and we cannot use strlcpy with len=byte_count+1 * because then we might get into an EXCEPTION if we exceed an allocated - * memory page...so we use php_strnlen in conjunction with memcpy and add the NUL + * memory page...so we use zend_strnlen in conjunction with memcpy and add the NUL * char. * estrdup would sometimes allocate more memory and does not return length */ - if ((byte_count=php_strnlen(value, byte_count)) > 0) { + if ((byte_count=zend_strnlen(value, byte_count)) > 0) { return exif_process_undefined(result, value, byte_count); } (*result) = estrndup("", 1); /* force empty string */ @@ -3412,7 +3400,7 @@ static bool exif_process_IFD_TAG_impl(image_info_type *ImageInfo, char *dir_entr switch(tag) { case TAG_COPYRIGHT: /* check for " NUL NUL" */ - if (byte_count>1 && (length=php_strnlen(value_ptr, byte_count)) > 0) { + if (byte_count>1 && (length=zend_strnlen(value_ptr, byte_count)) > 0) { if (lengthCopyrightPhotographer); @@ -3776,10 +3764,10 @@ static void exif_process_APP12(image_info_type *ImageInfo, char *buffer, size_t { size_t l1, l2=0; - if ((l1 = php_strnlen(buffer+2, length-2)) > 0) { + if ((l1 = zend_strnlen(buffer+2, length-2)) > 0) { exif_iif_add_tag(ImageInfo, SECTION_APP12, "Company", TAG_NONE, TAG_FMT_STRING, l1, buffer+2, l1); if (length > 2+l1+1) { - l2 = php_strnlen(buffer+2+l1+1, length-2-l1-1); + l2 = zend_strnlen(buffer+2+l1+1, length-2-l1-1); exif_iif_add_tag(ImageInfo, SECTION_APP12, "Info", TAG_NONE, TAG_FMT_STRING, l2, buffer+2+l1+1, l2); } } diff --git a/ext/phar/tar.c b/ext/phar/tar.c index 60e248c78df5f..ce8ee0a369882 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -200,13 +200,6 @@ static int phar_tar_process_metadata(phar_entry_info *entry, php_stream *fp) /* } /* }}} */ -#ifndef HAVE_STRNLEN -static size_t strnlen(const char *s, size_t maxlen) { - char *r = (char *)memchr(s, '\0', maxlen); - return r ? r-s : maxlen; -} -#endif - int phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, char *alias, size_t alias_len, phar_archive_data** pphar, int is_data, uint32_t compression, char **error) /* {{{ */ { char buf[512], *actual_alias = NULL, *p; @@ -286,7 +279,7 @@ int phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, char *alia goto next; } - if (((!old && hdr->prefix[0] == 0) || old) && strnlen(hdr->name, 100) == sizeof(".phar/signature.bin")-1 && !strncmp(hdr->name, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) { + if (((!old && hdr->prefix[0] == 0) || old) && zend_strnlen(hdr->name, 100) == sizeof(".phar/signature.bin")-1 && !strncmp(hdr->name, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) { zend_off_t curloc; size_t sig_len; @@ -499,7 +492,7 @@ int phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, char *alia entry.link = NULL; /* link field is null-terminated unless it has 100 non-null chars. * Thus we cannot use strlen. */ - linkname_len = strnlen(hdr->linkname, 100); + linkname_len = zend_strnlen(hdr->linkname, 100); if (entry.tar_type == TAR_LINK) { if (!zend_hash_str_exists(&myphar->manifest, hdr->linkname, linkname_len)) { if (error) { diff --git a/main/spprintf.c b/main/spprintf.c index 37b81dc6d530b..64bde33406073 100644 --- a/main/spprintf.c +++ b/main/spprintf.c @@ -175,13 +175,6 @@ /* }}} */ -#if !HAVE_STRNLEN -static size_t strnlen(const char *s, size_t maxlen) { - char *r = memchr(s, '\0', maxlen); - return r ? r-s : maxlen; -} -#endif - /* * Do format conversion placing the output in buffer */ @@ -552,7 +545,7 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ if (!adjust_precision) { s_len = strlen(s); } else { - s_len = strnlen(s, precision); + s_len = zend_strnlen(s, precision); } } else { s = S_NULL; From bad529870754ee0e214d6eea99a7f0ba42f44a03 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 22 Aug 2023 23:51:56 +0200 Subject: [PATCH 38/47] Make php_cli_server_pdeathsig.phpt SKIPIF more specific --- sapi/cli/tests/php_cli_server_pdeathsig.phpt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sapi/cli/tests/php_cli_server_pdeathsig.phpt b/sapi/cli/tests/php_cli_server_pdeathsig.phpt index db4b99af61a43..8b70bbcad4927 100644 --- a/sapi/cli/tests/php_cli_server_pdeathsig.phpt +++ b/sapi/cli/tests/php_cli_server_pdeathsig.phpt @@ -8,8 +8,7 @@ include "skipif.inc"; if (!(str_contains(PHP_OS, 'Linux') || str_contains(PHP_OS, 'FreeBSD'))) { die('skip PDEATHSIG is only supported on Linux and FreeBSD'); } -// This fails on 32-bit GitHub actions (probably due to Docker rather than 32-bit) -if (PHP_INT_SIZE != 8) die("skip 64-bit only"); +if (@file_exists('/.dockerenv')) die("skip Broken in Docker"); ?> --FILE-- Date: Wed, 23 Aug 2023 10:56:12 +0200 Subject: [PATCH 39/47] Move installation of oracle instant client in GHA Closes GH-12033 --- .github/actions/apt-x64/action.yml | 12 ------------ .github/actions/install-linux-x32/action.yml | 2 +- .github/actions/install-linux/action.yml | 4 +--- .github/actions/setup-oracle/action.yml | 17 +++++++++++++++++ .github/workflows/nightly.yml | 2 +- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/.github/actions/apt-x64/action.yml b/.github/actions/apt-x64/action.yml index 621b18532e05f..4e1a03dd58cc5 100644 --- a/.github/actions/apt-x64/action.yml +++ b/.github/actions/apt-x64/action.yml @@ -59,15 +59,3 @@ runs: libjpeg-dev \ libpng-dev \ libfreetype6-dev - - mkdir /opt/oracle - wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-basiclite-linuxx64.zip - unzip instantclient-basiclite-linuxx64.zip && rm instantclient-basiclite-linuxx64.zip - wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-sdk-linuxx64.zip - unzip instantclient-sdk-linuxx64.zip && rm instantclient-sdk-linuxx64.zip - mv instantclient_*_* /opt/oracle/instantclient - # interferes with libldap2 headers - rm /opt/oracle/instantclient/sdk/include/ldap.h - # fix debug build warning: zend_signal: handler was replaced for signal (2) after startup - echo DISABLE_INTERRUPT=on > /opt/oracle/instantclient/network/admin/sqlnet.ora - sudo sh -c 'echo /opt/oracle/instantclient >/etc/ld.so.conf.d/oracle-instantclient.conf && ldconfig' diff --git a/.github/actions/install-linux-x32/action.yml b/.github/actions/install-linux-x32/action.yml index bf5f09cd779d4..4ef87ee03fd3f 100644 --- a/.github/actions/install-linux-x32/action.yml +++ b/.github/actions/install-linux-x32/action.yml @@ -6,7 +6,7 @@ runs: run: | set -x make install - mkdir /etc/php.d + mkdir -p /etc/php.d chmod 777 /etc/php.d echo mysqli.default_socket=/var/run/mysqld/mysqld.sock > /etc/php.d/mysqli.ini echo pdo_mysql.default_socket=/var/run/mysqld/mysqld.sock > /etc/php.d/pdo_mysql.ini diff --git a/.github/actions/install-linux/action.yml b/.github/actions/install-linux/action.yml index 7ac2ae4c4fcb1..576357b94f1c5 100644 --- a/.github/actions/install-linux/action.yml +++ b/.github/actions/install-linux/action.yml @@ -6,9 +6,7 @@ runs: run: | set -x sudo make install - sudo mkdir /etc/php.d + sudo mkdir -p /etc/php.d sudo chmod 777 /etc/php.d echo mysqli.default_socket=/var/run/mysqld/mysqld.sock > /etc/php.d/mysqli.ini echo pdo_mysql.default_socket=/var/run/mysqld/mysqld.sock > /etc/php.d/pdo_mysql.ini - echo extension=oci8.so > /etc/php.d/oci8.ini - echo extension=pdo_oci.so > /etc/php.d/pdo_oci.ini diff --git a/.github/actions/setup-oracle/action.yml b/.github/actions/setup-oracle/action.yml index 11c16fe93d525..1208e93a24893 100644 --- a/.github/actions/setup-oracle/action.yml +++ b/.github/actions/setup-oracle/action.yml @@ -11,3 +11,20 @@ runs: --name oracle \ -h oracle \ -d gvenzl/oracle-xe:slim + + mkdir /opt/oracle + wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-basiclite-linuxx64.zip + unzip instantclient-basiclite-linuxx64.zip && rm instantclient-basiclite-linuxx64.zip + wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-sdk-linuxx64.zip + unzip instantclient-sdk-linuxx64.zip && rm instantclient-sdk-linuxx64.zip + mv instantclient_*_* /opt/oracle/instantclient + # interferes with libldap2 headers + rm /opt/oracle/instantclient/sdk/include/ldap.h + # fix debug build warning: zend_signal: handler was replaced for signal (2) after startup + echo DISABLE_INTERRUPT=on > /opt/oracle/instantclient/network/admin/sqlnet.ora + sudo sh -c 'echo /opt/oracle/instantclient >/etc/ld.so.conf.d/oracle-instantclient.conf && ldconfig' + + sudo mkdir -p /etc/php.d + sudo chmod 777 /etc/php.d + echo extension=oci8.so > /etc/php.d/oci8.ini + echo extension=pdo_oci.so > /etc/php.d/pdo_oci.ini diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 3e8ce86f9dda9..1ff8b6847a4a0 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -555,7 +555,7 @@ jobs: - name: make install run: | sudo make install - sudo mkdir /etc/php.d + sudo mkdir -p /etc/php.d sudo chmod 777 /etc/php.d echo mysqli.default_socket=/var/run/mysqld/mysqld.sock > /etc/php.d/mysqli.ini echo pdo_mysql.default_socket=/var/run/mysqld/mysqld.sock > /etc/php.d/pdo_mysql.ini From e055e12a2ca37df46a2e5cf47ca0e7815a129fa5 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Wed, 23 Aug 2023 13:19:13 +0100 Subject: [PATCH 40/47] [skip ci] Mark test as XLEAK due to LSAN bug (#12018) This is due to apparently some combination of glibc and Clang 14 which we are unfortunately hitting --- sapi/cli/tests/ext_loading.phpt | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/sapi/cli/tests/ext_loading.phpt b/sapi/cli/tests/ext_loading.phpt index 220302da629df..f262b3e2ac3f7 100644 --- a/sapi/cli/tests/ext_loading.phpt +++ b/sapi/cli/tests/ext_loading.phpt @@ -2,6 +2,9 @@ Extension loading --SKIPIF-- --EXPECTF-- Only extension name: -Output: Done. +#####OUTPUT_BEGIN#### +Done. +######OUTPUT_END##### Name with file extension: -Output: Done. +#####OUTPUT_BEGIN#### +Done. +######OUTPUT_END##### Absolute path: -Output: Done. +#####OUTPUT_BEGIN#### +Done. +######OUTPUT_END##### Unknown extension name (unknown): -Output: +#####OUTPUT_BEGIN#### + Warning: Failed loading Zend extension 'unknown_ext' (tried: %s) in Unknown on line 0 Done. +######OUTPUT_END##### Name with file extension (unknown): -Output: +#####OUTPUT_BEGIN#### + Warning: Failed loading Zend extension '%Sunknown_ext%S' (tried: %s) in Unknown on line 0 Done. +######OUTPUT_END##### Absolute path (unknown): -Output: Failed loading %s +#####OUTPUT_BEGIN#### +Failed loading %s Done. +######OUTPUT_END##### From 94e26dc8952de5f22d8e17c6667fd26c3ff907cc Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 23 Aug 2023 18:01:20 +0200 Subject: [PATCH 41/47] [ci skip] bump zip version --- ext/zip/php_zip.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/zip/php_zip.h b/ext/zip/php_zip.h index f57006363917e..520b2599bacc4 100644 --- a/ext/zip/php_zip.h +++ b/ext/zip/php_zip.h @@ -39,7 +39,7 @@ extern zend_module_entry zip_module_entry; /* Additionnal flags not from libzip */ #define ZIP_FL_OPEN_FILE_NOW (1u<<30) -#define PHP_ZIP_VERSION "1.22.1" +#define PHP_ZIP_VERSION "1.22.2" #ifdef HAVE_LIBZIP_VERSION #define LIBZIP_VERSION_STR zip_libzip_version() From c9e5e1fc521d694eb5c2baf69eedfc1dc9452f7f Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 23 Aug 2023 12:58:32 +0200 Subject: [PATCH 42/47] Switch asan build to Ubuntu 23.04 in Docker Closes GH-12034 --- .github/actions/apt-x64/action.yml | 18 ++++++++++++++++-- .github/workflows/push.yml | 18 ++++++++++++++++-- sapi/cli/tests/ext_loading.phpt | 3 --- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/.github/actions/apt-x64/action.yml b/.github/actions/apt-x64/action.yml index 72bc3b3673b17..8f2f608278f07 100644 --- a/.github/actions/apt-x64/action.yml +++ b/.github/actions/apt-x64/action.yml @@ -6,8 +6,21 @@ runs: run: | set -x - sudo apt-get update - sudo apt-get install \ + export DEBIAN_FRONTEND=noninteractive + + # Install sudo in Docker for consistent actions + if ! type "sudo" > /dev/null; then + apt-get update -y | true + apt-get install -y sudo + fi + + sudo apt-get update -y | true + sudo apt-get install -y \ + autoconf \ + gcc \ + make \ + curl \ + unzip \ bison \ re2c \ locales \ @@ -20,6 +33,7 @@ runs: libtidy-dev \ libenchant-2-dev \ libaspell-dev \ + libbz2-dev \ libpspell-dev \ libsasl2-dev \ libxpm-dev \ diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index f2f44dc23a59a..048492913da39 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -37,12 +37,21 @@ env: jobs: LINUX_X64: services: + mysql: + image: mysql:8 + env: + MYSQL_DATABASE: test + MYSQL_ROOT_PASSWORD: root postgres: image: postgres env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: test + env: + MYSQL_TEST_HOST: mysql + PDO_MYSQL_TEST_DSN: mysql:host=mysql;dbname=test + PDO_MYSQL_TEST_HOST: mysql strategy: fail-fast: false matrix: @@ -55,18 +64,21 @@ jobs: asan: true name: "LINUX_X64_${{ matrix.debug && 'DEBUG' || 'RELEASE' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}${{ matrix.asan && '_ASAN' || '' }}" runs-on: ubuntu-22.04 + container: + image: ${{ matrix.asan && 'ubuntu:23.04' || null }} steps: - name: git checkout uses: actions/checkout@v3 + - name: apt + uses: ./.github/actions/apt-x64 - name: Create MSSQL container + if: ${{ !matrix.asan }} uses: ./.github/actions/setup-mssql - name: Create Oracle container if: ${{ !matrix.asan }} uses: ./.github/actions/setup-oracle - name: Setup Caddy server uses: ./.github/actions/setup-caddy - - name: apt - uses: ./.github/actions/apt-x64 - name: ccache uses: hendrikmuhs/ccache-action@v1.2 with: @@ -88,6 +100,7 @@ jobs: - name: make install uses: ./.github/actions/install-linux - name: Setup + if: ${{ !matrix.asan }} uses: ./.github/actions/setup-x64 - name: Test if: matrix.asan == false @@ -104,6 +117,7 @@ jobs: ${{ !matrix.asan && '-d opcache.jit_buffer_size=16M' || '' }} ${{ matrix.asan && '--asan -x' || '' }} - name: Verify generated files are up to date + if: ${{ !matrix.asan }} uses: ./.github/actions/verify-generated-files MACOS_DEBUG_NTS: runs-on: macos-11 diff --git a/sapi/cli/tests/ext_loading.phpt b/sapi/cli/tests/ext_loading.phpt index f262b3e2ac3f7..1769f1f657555 100644 --- a/sapi/cli/tests/ext_loading.phpt +++ b/sapi/cli/tests/ext_loading.phpt @@ -2,9 +2,6 @@ Extension loading --SKIPIF-- Date: Wed, 26 Jul 2023 15:17:32 +0200 Subject: [PATCH 43/47] Add more test coverage for ext/odbc --- ext/odbc/tests/odbc_close_all_001.phpt | 38 +++++ ext/odbc/tests/odbc_commit_001.phpt | 31 ++++ ext/odbc/tests/odbc_cursor_001.phpt | 29 ++++ ext/odbc/tests/odbc_fetch_array_001.phpt | 46 ++++++ ext/odbc/tests/odbc_fetch_into_001.phpt | 59 +++++++ ext/odbc/tests/odbc_fetch_object_001.phpt | 46 ++++++ ext/odbc/tests/odbc_fetch_row_001.phpt | 52 +++++++ ext/odbc/tests/odbc_field_len_001.phpt | 43 ++++++ ext/odbc/tests/odbc_field_name_001.phpt | 42 +++++ ext/odbc/tests/odbc_field_num_001.phpt | 34 ++++ ext/odbc/tests/odbc_field_precision_001.phpt | 42 +++++ ext/odbc/tests/odbc_field_scale_001.phpt | 42 +++++ ext/odbc/tests/odbc_field_type_001.phpt | 42 +++++ ext/odbc/tests/odbc_gettypeinfo_001.phpt | 154 +++++++++++++++++++ ext/odbc/tests/odbc_longreadlen_001.phpt | 31 ++++ ext/odbc/tests/odbc_num_fields_001.phpt | 35 +++++ ext/odbc/tests/odbc_num_rows_001.phpt | 37 +++++ ext/odbc/tests/odbc_primarykeys_001.phpt | 37 +++++ ext/odbc/tests/odbc_rollback_001.phpt | 31 ++++ ext/odbc/tests/odbc_setoption_001.phpt | 24 +++ ext/odbc/tests/odbc_setoption_002.phpt | 25 +++ 21 files changed, 920 insertions(+) create mode 100644 ext/odbc/tests/odbc_close_all_001.phpt create mode 100644 ext/odbc/tests/odbc_commit_001.phpt create mode 100644 ext/odbc/tests/odbc_cursor_001.phpt create mode 100644 ext/odbc/tests/odbc_fetch_array_001.phpt create mode 100644 ext/odbc/tests/odbc_fetch_into_001.phpt create mode 100644 ext/odbc/tests/odbc_fetch_object_001.phpt create mode 100644 ext/odbc/tests/odbc_fetch_row_001.phpt create mode 100644 ext/odbc/tests/odbc_field_len_001.phpt create mode 100644 ext/odbc/tests/odbc_field_name_001.phpt create mode 100644 ext/odbc/tests/odbc_field_num_001.phpt create mode 100644 ext/odbc/tests/odbc_field_precision_001.phpt create mode 100644 ext/odbc/tests/odbc_field_scale_001.phpt create mode 100644 ext/odbc/tests/odbc_field_type_001.phpt create mode 100644 ext/odbc/tests/odbc_gettypeinfo_001.phpt create mode 100644 ext/odbc/tests/odbc_longreadlen_001.phpt create mode 100644 ext/odbc/tests/odbc_num_fields_001.phpt create mode 100644 ext/odbc/tests/odbc_num_rows_001.phpt create mode 100644 ext/odbc/tests/odbc_primarykeys_001.phpt create mode 100644 ext/odbc/tests/odbc_rollback_001.phpt create mode 100644 ext/odbc/tests/odbc_setoption_001.phpt create mode 100644 ext/odbc/tests/odbc_setoption_002.phpt diff --git a/ext/odbc/tests/odbc_close_all_001.phpt b/ext/odbc/tests/odbc_close_all_001.phpt new file mode 100644 index 0000000000000..9641ac4bd7e4a --- /dev/null +++ b/ext/odbc/tests/odbc_close_all_001.phpt @@ -0,0 +1,38 @@ +--TEST-- +odbc_close_all(): Basic test +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +resource(5) of type (odbc link) +resource(7) of type (odbc link persistent) +resource(8) of type (odbc result) +resource(9) of type (odbc result) +resource(5) of type (Unknown) +resource(7) of type (Unknown) +resource(8) of type (Unknown) +resource(9) of type (Unknown) diff --git a/ext/odbc/tests/odbc_commit_001.phpt b/ext/odbc/tests/odbc_commit_001.phpt new file mode 100644 index 0000000000000..5db0b0627e5b8 --- /dev/null +++ b/ext/odbc/tests/odbc_commit_001.phpt @@ -0,0 +1,31 @@ +--TEST-- +odbc_commit(): Basic test for odbc_commit() +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECT-- +bool(true) +string(1) "1" diff --git a/ext/odbc/tests/odbc_cursor_001.phpt b/ext/odbc/tests/odbc_cursor_001.phpt new file mode 100644 index 0000000000000..b7111393d8341 --- /dev/null +++ b/ext/odbc/tests/odbc_cursor_001.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test odbc_cursor() +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECTF-- +string(%d) "SQL_CUR%s" diff --git a/ext/odbc/tests/odbc_fetch_array_001.phpt b/ext/odbc/tests/odbc_fetch_array_001.phpt new file mode 100644 index 0000000000000..b212adcb1f49b --- /dev/null +++ b/ext/odbc/tests/odbc_fetch_array_001.phpt @@ -0,0 +1,46 @@ +--TEST-- +odbc_fetch_array(): Getting data from query +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECTF-- +array(1) { + ["foo"]=> + string(1) "1" +} +array(1) { + ["foo"]=> + string(1) "2" +} +array(1) { + ["foo"]=> + string(1) "3" +} +bool(false) diff --git a/ext/odbc/tests/odbc_fetch_into_001.phpt b/ext/odbc/tests/odbc_fetch_into_001.phpt new file mode 100644 index 0000000000000..5053552ce5da7 --- /dev/null +++ b/ext/odbc/tests/odbc_fetch_into_001.phpt @@ -0,0 +1,59 @@ +--TEST-- +odbc_fetch_into(): Getting data from query +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECTF-- +int(1) +array(1) { + [0]=> + string(1) "1" +} +int(1) +array(1) { + [0]=> + string(1) "2" +} +int(1) +array(1) { + [0]=> + string(1) "3" +} +bool(false) +array(0) { +} diff --git a/ext/odbc/tests/odbc_fetch_object_001.phpt b/ext/odbc/tests/odbc_fetch_object_001.phpt new file mode 100644 index 0000000000000..4ea407562eb75 --- /dev/null +++ b/ext/odbc/tests/odbc_fetch_object_001.phpt @@ -0,0 +1,46 @@ +--TEST-- +odbc_fetch_object(): Getting data from query +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECTF-- +object(stdClass)#%d (%d) { + ["foo"]=> + string(1) "1" +} +object(stdClass)#%d (%d) { + ["foo"]=> + string(1) "2" +} +object(stdClass)#%d (%d) { + ["foo"]=> + string(1) "3" +} +bool(false) diff --git a/ext/odbc/tests/odbc_fetch_row_001.phpt b/ext/odbc/tests/odbc_fetch_row_001.phpt new file mode 100644 index 0000000000000..4cecb513a067f --- /dev/null +++ b/ext/odbc/tests/odbc_fetch_row_001.phpt @@ -0,0 +1,52 @@ +--TEST-- +odbc_fetch_row(): Getting data from query +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECTF-- +bool(false) +bool(true) +string(1) "1" +bool(true) +string(1) "2" +bool(true) +string(1) "3" +bool(false) diff --git a/ext/odbc/tests/odbc_field_len_001.phpt b/ext/odbc/tests/odbc_field_len_001.phpt new file mode 100644 index 0000000000000..b4697fff75f25 --- /dev/null +++ b/ext/odbc/tests/odbc_field_len_001.phpt @@ -0,0 +1,43 @@ +--TEST-- +odbc_field_len(): Getting the length of the field +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- +getMessage() . "\n"; +} +var_dump(odbc_field_len($res, 1)); +var_dump(odbc_field_len($res, 2)); +var_dump(odbc_field_len($res, 3)); +var_dump(odbc_field_len($res, 4)); + +odbc_close($conn); +?> +--CLEAN-- + +--EXPECTF-- +odbc_field_len(): Argument #2 ($field) must be greater than 0 +int(10) +int(2147483647) +int(50) + +Warning: odbc_field_len(): Field index larger than number of fields in %s on line %d +bool(false) diff --git a/ext/odbc/tests/odbc_field_name_001.phpt b/ext/odbc/tests/odbc_field_name_001.phpt new file mode 100644 index 0000000000000..da34da9e022e1 --- /dev/null +++ b/ext/odbc/tests/odbc_field_name_001.phpt @@ -0,0 +1,42 @@ +--TEST-- +odbc_field_name(): Getting the name of the field +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- +getMessage() . "\n"; +} +var_dump(odbc_field_name($res, 1)); +var_dump(odbc_field_name($res, 2)); +var_dump(odbc_field_name($res, 3)); +var_dump(odbc_field_name($res, 4)); + +?> +--CLEAN-- + +--EXPECTF-- +odbc_field_name(): Argument #2 ($field) must be greater than 0 +string(3) "foo" +string(3) "bar" +string(3) "baz" + +Warning: odbc_field_name(): Field index larger than number of fields in %s on line %d +bool(false) diff --git a/ext/odbc/tests/odbc_field_num_001.phpt b/ext/odbc/tests/odbc_field_num_001.phpt new file mode 100644 index 0000000000000..ab4bbcc4d00b3 --- /dev/null +++ b/ext/odbc/tests/odbc_field_num_001.phpt @@ -0,0 +1,34 @@ +--TEST-- +odbc_field_scale(): Getting the scale of the field +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECTF-- +int(1) +int(2) +int(3) +bool(false) diff --git a/ext/odbc/tests/odbc_field_precision_001.phpt b/ext/odbc/tests/odbc_field_precision_001.phpt new file mode 100644 index 0000000000000..609401ba263c9 --- /dev/null +++ b/ext/odbc/tests/odbc_field_precision_001.phpt @@ -0,0 +1,42 @@ +--TEST-- +odbc_field_precision(): Getting the precision of the field +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- +getMessage() . "\n"; +} +var_dump(odbc_field_precision($res, 1)); +var_dump(odbc_field_precision($res, 2)); +var_dump(odbc_field_precision($res, 3)); +var_dump(odbc_field_precision($res, 4)); + +?> +--CLEAN-- + +--EXPECTF-- +odbc_field_precision(): Argument #2 ($field) must be greater than 0 +int(10) +int(7) +int(50) + +Warning: odbc_field_precision(): Field index larger than number of fields in %s on line %d +bool(false) diff --git a/ext/odbc/tests/odbc_field_scale_001.phpt b/ext/odbc/tests/odbc_field_scale_001.phpt new file mode 100644 index 0000000000000..3bb2da8e1c2cb --- /dev/null +++ b/ext/odbc/tests/odbc_field_scale_001.phpt @@ -0,0 +1,42 @@ +--TEST-- +odbc_field_scale(): Getting the scale of the field +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- +getMessage() . "\n"; +} +var_dump(odbc_field_scale($res, 1)); +var_dump(odbc_field_scale($res, 2)); +var_dump(odbc_field_scale($res, 3)); +var_dump(odbc_field_scale($res, 4)); + +?> +--CLEAN-- + +--EXPECTF-- +odbc_field_scale(): Argument #2 ($field) must be greater than 0 +int(0) +int(0) +int(0) + +Warning: odbc_field_scale(): Field index larger than number of fields in %s on line %d +bool(false) diff --git a/ext/odbc/tests/odbc_field_type_001.phpt b/ext/odbc/tests/odbc_field_type_001.phpt new file mode 100644 index 0000000000000..81774c7dbc0ed --- /dev/null +++ b/ext/odbc/tests/odbc_field_type_001.phpt @@ -0,0 +1,42 @@ +--TEST-- +odbc_field_type(): Getting the type of the field +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- +getMessage() . "\n"; +} +var_dump(odbc_field_type($res, 1)); +var_dump(odbc_field_type($res, 2)); +var_dump(odbc_field_type($res, 3)); +var_dump(odbc_field_type($res, 4)); + +?> +--CLEAN-- + +--EXPECTF-- +odbc_field_type(): Argument #2 ($field) must be greater than 0 +string(3) "int" +string(4) "text" +string(9) "varbinary" + +Warning: odbc_field_type(): Field index larger than number of fields in %s on line %d +bool(false) diff --git a/ext/odbc/tests/odbc_gettypeinfo_001.phpt b/ext/odbc/tests/odbc_gettypeinfo_001.phpt new file mode 100644 index 0000000000000..76b06089181fa --- /dev/null +++ b/ext/odbc/tests/odbc_gettypeinfo_001.phpt @@ -0,0 +1,154 @@ +--TEST-- +odbc_gettypeinfo(): Getting info about data types +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +array(20) { + ["TYPE_NAME"]=> + string(14) "datetimeoffset" + ["DATA_TYPE"]=> + string(4) "-155" + ["COLUMN_SIZE"]=> + string(2) "34" + ["LITERAL_PREFIX"]=> + string(1) "'" + ["LITERAL_SUFFIX"]=> + string(1) "'" + ["CREATE_PARAMS"]=> + string(5) "scale" + ["NULLABLE"]=> + string(1) "1" + ["CASE_SENSITIVE"]=> + string(1) "0" + ["SEARCHABLE"]=> + string(1) "3" + ["UNSIGNED_ATTRIBUTE"]=> + NULL + ["FIXED_PREC_SCALE"]=> + string(1) "0" + ["AUTO_UNIQUE_VALUE"]=> + NULL + ["LOCAL_TYPE_NAME"]=> + string(14) "datetimeoffset" + ["MINIMUM_SCALE"]=> + string(1) "0" + ["MAXIMUM_SCALE"]=> + string(1) "7" + ["SQL_DATA_TYPE"]=> + string(4) "-155" + ["SQL_DATETIME_SUB"]=> + string(1) "0" + ["NUM_PREC_RADIX"]=> + NULL + ["INTERVAL_PRECISION"]=> + NULL + ["USERTYPE"]=> + string(1) "0" +} +array(20) { + ["TYPE_NAME"]=> + string(4) "char" + ["DATA_TYPE"]=> + string(1) "1" + ["COLUMN_SIZE"]=> + string(4) "8000" + ["LITERAL_PREFIX"]=> + string(1) "'" + ["LITERAL_SUFFIX"]=> + string(1) "'" + ["CREATE_PARAMS"]=> + string(6) "length" + ["NULLABLE"]=> + string(1) "1" + ["CASE_SENSITIVE"]=> + string(1) "0" + ["SEARCHABLE"]=> + string(1) "3" + ["UNSIGNED_ATTRIBUTE"]=> + NULL + ["FIXED_PREC_SCALE"]=> + string(1) "0" + ["AUTO_UNIQUE_VALUE"]=> + NULL + ["LOCAL_TYPE_NAME"]=> + string(4) "char" + ["MINIMUM_SCALE"]=> + NULL + ["MAXIMUM_SCALE"]=> + NULL + ["SQL_DATA_TYPE"]=> + string(1) "1" + ["SQL_DATETIME_SUB"]=> + NULL + ["NUM_PREC_RADIX"]=> + NULL + ["INTERVAL_PRECISION"]=> + NULL + ["USERTYPE"]=> + string(1) "1" +} +array(20) { + ["TYPE_NAME"]=> + string(7) "numeric" + ["DATA_TYPE"]=> + string(1) "2" + ["COLUMN_SIZE"]=> + string(2) "38" + ["LITERAL_PREFIX"]=> + NULL + ["LITERAL_SUFFIX"]=> + NULL + ["CREATE_PARAMS"]=> + string(15) "precision,scale" + ["NULLABLE"]=> + string(1) "1" + ["CASE_SENSITIVE"]=> + string(1) "0" + ["SEARCHABLE"]=> + string(1) "2" + ["UNSIGNED_ATTRIBUTE"]=> + string(1) "0" + ["FIXED_PREC_SCALE"]=> + string(1) "0" + ["AUTO_UNIQUE_VALUE"]=> + string(1) "0" + ["LOCAL_TYPE_NAME"]=> + string(7) "numeric" + ["MINIMUM_SCALE"]=> + string(1) "0" + ["MAXIMUM_SCALE"]=> + string(2) "38" + ["SQL_DATA_TYPE"]=> + string(1) "2" + ["SQL_DATETIME_SUB"]=> + NULL + ["NUM_PREC_RADIX"]=> + string(2) "10" + ["INTERVAL_PRECISION"]=> + NULL + ["USERTYPE"]=> + string(2) "10" +} diff --git a/ext/odbc/tests/odbc_longreadlen_001.phpt b/ext/odbc/tests/odbc_longreadlen_001.phpt new file mode 100644 index 0000000000000..d27fa8bf9581a --- /dev/null +++ b/ext/odbc/tests/odbc_longreadlen_001.phpt @@ -0,0 +1,31 @@ +--TEST-- +Test odbc_longreadlen() +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECT-- +string(4) "what" diff --git a/ext/odbc/tests/odbc_num_fields_001.phpt b/ext/odbc/tests/odbc_num_fields_001.phpt new file mode 100644 index 0000000000000..b815c971e01cd --- /dev/null +++ b/ext/odbc/tests/odbc_num_fields_001.phpt @@ -0,0 +1,35 @@ +--TEST-- +odbc_num_rows(): Getting the number of fields +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECT-- +int(3) +int(3) diff --git a/ext/odbc/tests/odbc_num_rows_001.phpt b/ext/odbc/tests/odbc_num_rows_001.phpt new file mode 100644 index 0000000000000..8c2af1a1f147f --- /dev/null +++ b/ext/odbc/tests/odbc_num_rows_001.phpt @@ -0,0 +1,37 @@ +--TEST-- +odbc_num_rows(): Getting the number of rows +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECT-- +int(3) +int(0) diff --git a/ext/odbc/tests/odbc_primarykeys_001.phpt b/ext/odbc/tests/odbc_primarykeys_001.phpt new file mode 100644 index 0000000000000..17b42c6ff51eb --- /dev/null +++ b/ext/odbc/tests/odbc_primarykeys_001.phpt @@ -0,0 +1,37 @@ +--TEST-- +odbc_primarykeys(): Basic test for odbc_primarykeys() +--EXTENSIONS-- +odbc +--SKIPIF-- + +--XFAIL-- +Doesn't work with MS SQL +--FILE-- + +--CLEAN-- + +--EXPECT-- +bool(false) diff --git a/ext/odbc/tests/odbc_rollback_001.phpt b/ext/odbc/tests/odbc_rollback_001.phpt new file mode 100644 index 0000000000000..6f4cf3d6926d7 --- /dev/null +++ b/ext/odbc/tests/odbc_rollback_001.phpt @@ -0,0 +1,31 @@ +--TEST-- +odbc_rollback(): Basic test for odbc_rollback() +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECT-- +bool(true) +bool(false) diff --git a/ext/odbc/tests/odbc_setoption_001.phpt b/ext/odbc/tests/odbc_setoption_001.phpt new file mode 100644 index 0000000000000..3f2202100aa64 --- /dev/null +++ b/ext/odbc/tests/odbc_setoption_001.phpt @@ -0,0 +1,24 @@ +--TEST-- +odbc_setoption(): Basic test for odbc_setoption() +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) +int(0) diff --git a/ext/odbc/tests/odbc_setoption_002.phpt b/ext/odbc/tests/odbc_setoption_002.phpt new file mode 100644 index 0000000000000..e93d45b6fb414 --- /dev/null +++ b/ext/odbc/tests/odbc_setoption_002.phpt @@ -0,0 +1,25 @@ +--TEST-- +odbc_setoption(): Test for odbc_setoption() with persistent connection +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Warning: odbc_setoption(): Unable to set option for persistent connection in %s on line %d +bool(false) +int(1) From 985511e9682316051dde66415ec604efc5464567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Mon, 7 Aug 2023 09:55:28 +0200 Subject: [PATCH 44/47] Enable ext/odbc and ext/pdo_odbc tests on Linux in GitHub CI --- .github/actions/setup-x64/action.yml | 2 ++ .github/actions/test-linux/action.yml | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/.github/actions/setup-x64/action.yml b/.github/actions/setup-x64/action.yml index 91cf7ea51c356..bec213375b7c5 100644 --- a/.github/actions/setup-x64/action.yml +++ b/.github/actions/setup-x64/action.yml @@ -12,6 +12,8 @@ runs: # Ensure local_infile tests can run. mysql -uroot -proot -e "SET GLOBAL local_infile = true" docker exec sql1 /opt/mssql-tools/bin/sqlcmd -S 127.0.0.1 -U SA -P "" -Q "create login pdo_test with password='password', check_policy=off; create user pdo_test for login pdo_test; grant alter, control to pdo_test;" + docker exec sql1 /opt/mssql-tools/bin/sqlcmd -S 127.0.0.1 -U SA -P "" -Q "create login odbc_test with password='password', check_policy=off; create user odbc_test for login odbc_test; grant alter, control, delete to odbc_test;" + docker exec sql1 /opt/mssql-tools/bin/sqlcmd -S 127.0.0.1 -U SA -P "" -Q "ALTER SERVER ROLE sysadmin ADD MEMBER odbc_test;" sudo locale-gen de_DE ./.github/scripts/setup-slapd.sh diff --git a/.github/actions/test-linux/action.yml b/.github/actions/test-linux/action.yml index 9b7d0d100608f..7657ff8c85ad2 100644 --- a/.github/actions/test-linux/action.yml +++ b/.github/actions/test-linux/action.yml @@ -30,6 +30,10 @@ runs: export PDO_OCI_TEST_DSN="oci:dbname=localhost/XEPDB1;charset=AL32UTF8" export PGSQL_TEST_CONNSTR="host=postgres dbname=test port=5432 user=postgres password=postgres" export PDO_PGSQL_TEST_DSN="host=postgres dbname=test port=5432 user=postgres password=postgres" + export ODBC_TEST_USER="odbc_test" + export ODBC_TEST_PASS="password" + export ODBC_TEST_DSN="Driver={ODBC Driver 17 for SQL Server};Server=127.0.0.1;Database=master;uid=$ODBC_TEST_USER;pwd=$ODBC_TEST_PASS" + export PDO_ODBC_TEST_DSN="odbc:$ODBC_TEST_DSN" export SKIP_IO_CAPTURE_TESTS=1 export TEST_PHP_JUNIT=junit.out.xml export STACK_LIMIT_DEFAULTS_CHECK=1 From 8726ae0601d69b3858d0c405830f0d72b261346e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 8 Aug 2023 08:48:50 +0200 Subject: [PATCH 45/47] Improve and fix ext/odbc tests Some test failures are fixed, parallelization is enabled, section order is fixed. --- ext/odbc/tests/CONFLICTS | 1 - ext/odbc/tests/bug44618.phpt | 8 ++- ext/odbc/tests/bug47803.phpt | 32 +++++---- ext/odbc/tests/bug60616.phpt | 42 +++++++----- ext/odbc/tests/bug68087.phpt | 18 ++---- ext/odbc/tests/bug69354.phpt | 19 +++--- ext/odbc/tests/bug69975.phpt | 21 +++--- ext/odbc/tests/bug71171.phpt | 21 +++--- ext/odbc/tests/bug73448.phpt | 2 + ext/odbc/tests/bug73725.phpt | 27 ++++---- ext/odbc/tests/odbc_columns_001.phpt | 9 +++ ext/odbc/tests/odbc_data_source_001.phpt | 8 +-- ext/odbc/tests/odbc_exec_001.phpt | 26 ++------ ext/odbc/tests/odbc_exec_002.phpt | 13 ++-- ext/odbc/tests/odbc_fetch_array_001.phpt | 10 ++- ext/odbc/tests/odbc_fetch_into_001.phpt | 12 ++-- ext/odbc/tests/odbc_fetch_object_001.phpt | 8 +-- ext/odbc/tests/odbc_fetch_row_001.phpt | 8 +-- ext/odbc/tests/odbc_field_len_001.phpt | 1 - ext/odbc/tests/odbc_field_name_001.phpt | 1 - ext/odbc/tests/odbc_field_num_001.phpt | 1 - ext/odbc/tests/odbc_field_precision_001.phpt | 1 - ext/odbc/tests/odbc_field_scale_001.phpt | 1 - ext/odbc/tests/odbc_field_type_001.phpt | 1 - ext/odbc/tests/odbc_free_result_001.phpt | 13 ++-- ext/odbc/tests/odbc_num_fields_001.phpt | 2 +- ext/odbc/tests/odbc_num_rows_001.phpt | 4 +- ext/odbc/tests/odbc_primarykeys_001.phpt | 68 ++++++++++++++++++-- ext/pdo/tests/bug_36798.phpt | 2 + ext/pdo_odbc/tests/bug80783a.phpt | 4 ++ 30 files changed, 207 insertions(+), 177 deletions(-) delete mode 100644 ext/odbc/tests/CONFLICTS diff --git a/ext/odbc/tests/CONFLICTS b/ext/odbc/tests/CONFLICTS deleted file mode 100644 index a3722b55c518a..0000000000000 --- a/ext/odbc/tests/CONFLICTS +++ /dev/null @@ -1 +0,0 @@ -odbc diff --git a/ext/odbc/tests/bug44618.phpt b/ext/odbc/tests/bug44618.phpt index efead0e995189..ee7b10a7e908e 100644 --- a/ext/odbc/tests/bug44618.phpt +++ b/ext/odbc/tests/bug44618.phpt @@ -3,7 +3,13 @@ Bug #44618 (Fetching may rely on uninitialized data) --EXTENSIONS-- odbc --SKIPIF-- - + --FILE-- 7, 'name'=>'test 7'), array('id'=>6, 'name'=>'test 6'), ); -$sql = "UPDATE FOO +$sql = "UPDATE bug47803 SET [PAR_CHR] = ? WHERE [PAR_ID] = ?"; $result = odbc_prepare($link, $sql); @@ -69,7 +68,7 @@ foreach ($upd_params as &$k) { } odbc_free_result($result); -$sql = "SELECT * FROM FOO WHERE [PAR_ID] = ?"; +$sql = "SELECT * FROM bug47803 WHERE [PAR_ID] = ?"; $result = odbc_prepare($link, $sql); if (!$result) { print ('[sql] prep: '.$sql); @@ -89,6 +88,17 @@ out: if ($result) odbc_free_result($result); odbc_close($link); +?> +--CLEAN-- + --EXPECT-- array(3) { @@ -171,15 +181,3 @@ array(3) { ["PAR_CHR"]=> string(6) "test 7" } ---CLEAN-- - diff --git a/ext/odbc/tests/bug60616.phpt b/ext/odbc/tests/bug60616.phpt index 468d3d5234a42..9c060ebc39f36 100644 --- a/ext/odbc/tests/bug60616.phpt +++ b/ext/odbc/tests/bug60616.phpt @@ -2,12 +2,23 @@ odbc_exec(): Getting accurate unicode data from query --EXTENSIONS-- odbc +mbstring --SKIPIF-- - --FILE-- ---EXPECT-- -EUC-JP matched -ASCII matched --CLEAN-- +--EXPECT-- +EUC-JP matched +ASCII matched + diff --git a/ext/odbc/tests/bug68087.phpt b/ext/odbc/tests/bug68087.phpt index 6d4d4cc0b2bd6..250df2ef5acee 100644 --- a/ext/odbc/tests/bug68087.phpt +++ b/ext/odbc/tests/bug68087.phpt @@ -14,14 +14,11 @@ $id_2_date = '2014-09-24'; $conn = odbc_connect($dsn, $user, $pass); -@odbc_exec($conn, 'CREATE DATABASE odbcTEST'); +odbc_exec($conn, 'CREATE TABLE bug68087 (ID INT, VARCHAR_COL VARCHAR(100), DATE_COL DATE)'); -odbc_exec($conn, 'CREATE TABLE FOO (ID INT, VARCHAR_COL VARCHAR(100), DATE_COL DATE)'); +odbc_exec($conn, "INSERT INTO bug68087(ID, VARCHAR_COL, DATE_COL) VALUES (1, 'hello', '$id_1_date'), (2, 'helloagain', '$id_2_date')"); -odbc_exec($conn, "INSERT INTO FOO(ID, VARCHAR_COL, DATE_COL) VALUES (1, 'hello', '$id_1_date')"); -odbc_exec($conn, "INSERT INTO FOO(ID, VARCHAR_COL, DATE_COL) VALUES (2, 'helloagain', '$id_2_date')"); - -$res = odbc_exec($conn, 'SELECT * FROM FOO ORDER BY ID ASC'); +$res = odbc_exec($conn, 'SELECT * FROM bug68087 ORDER BY ID ASC'); while(odbc_fetch_row($res)) { $id = odbc_result($res, "ID"); @@ -44,16 +41,15 @@ while(odbc_fetch_row($res)) { } ?> ---EXPECT-- -Date_1 matched -Date_2 matched --CLEAN-- +--EXPECT-- +Date_1 matched +Date_2 matched diff --git a/ext/odbc/tests/bug69354.phpt b/ext/odbc/tests/bug69354.phpt index 3aab7aa4e9393..47933576130ab 100644 --- a/ext/odbc/tests/bug69354.phpt +++ b/ext/odbc/tests/bug69354.phpt @@ -11,13 +11,11 @@ include 'config.inc'; $conn = odbc_connect($dsn, $user, $pass); -@odbc_exec($conn, 'CREATE DATABASE odbcTEST'); +odbc_exec($conn, 'CREATE TABLE bug69354 (ID INT, VARCHAR_COL VARCHAR(100))'); -odbc_exec($conn, 'CREATE TABLE FOO (ID INT, VARCHAR_COL VARCHAR(100))'); +odbc_exec($conn, "INSERT INTO bug69354(ID, VARCHAR_COL) VALUES (1, '" . str_repeat("a", 100) . "')"); -odbc_exec($conn, "INSERT INTO FOO(ID, VARCHAR_COL) VALUES (1, '" . str_repeat("a", 100) . "')"); - -$res = odbc_exec($conn,"select VARCHAR_COL from FOO"); +$res = odbc_exec($conn,"SELECT VARCHAR_COL FROM bug69354"); if ($res) { if (odbc_fetch_row($res)) { $ret = odbc_result($res,'varchar_col'); @@ -27,17 +25,16 @@ if ($res) { } } ?> ---EXPECT-- -100 -a -a --CLEAN-- +--EXPECT-- +100 +a +a diff --git a/ext/odbc/tests/bug69975.phpt b/ext/odbc/tests/bug69975.phpt index 3617ee7198062..4ab0613eae947 100644 --- a/ext/odbc/tests/bug69975.phpt +++ b/ext/odbc/tests/bug69975.phpt @@ -9,26 +9,25 @@ odbc include 'config.inc'; $conn = odbc_connect($dsn, $user, $pass); -@odbc_exec($conn, 'CREATE DATABASE odbcTEST'); -odbc_exec($conn, 'CREATE TABLE FOO (ID INT, VARCHAR_COL NVARCHAR(MAX))'); -odbc_exec($conn, "INSERT INTO FOO VALUES (1, 'foo')"); +odbc_exec($conn, 'CREATE TABLE bug69975 (ID INT, VARCHAR_COL NVARCHAR(MAX))'); +odbc_exec($conn, "INSERT INTO bug69975 VALUES (1, 'foo')"); -$result = odbc_exec($conn, "SELECT VARCHAR_COL FROM FOO"); +$result = odbc_exec($conn, "SELECT VARCHAR_COL FROM bug69975"); var_dump(odbc_fetch_array($result)); echo "ready"; ?> +--CLEAN-- + --EXPECT-- array(1) { ["VARCHAR_COL"]=> string(3) "foo" } ready ---CLEAN-- - diff --git a/ext/odbc/tests/bug71171.phpt b/ext/odbc/tests/bug71171.phpt index d2cef550e3b31..029c31962f6f4 100644 --- a/ext/odbc/tests/bug71171.phpt +++ b/ext/odbc/tests/bug71171.phpt @@ -11,33 +11,30 @@ include 'config.inc'; $conn = odbc_connect($dsn, $user, $pass); -@odbc_exec($conn, 'CREATE DATABASE odbcTEST'); +odbc_exec($conn, 'CREATE TABLE bug71171 (ID INT, VARCHAR_COL NVARCHAR(40))'); -odbc_exec($conn, 'CREATE TABLE FOO (ID INT, VARCHAR_COL NVARCHAR(40))'); +odbc_exec($conn, "INSERT INTO bug71171(ID, VARCHAR_COL) VALUES (1, '" . chr(0x81) . "')"); -odbc_exec($conn, "INSERT INTO FOO(ID, VARCHAR_COL) VALUES (1, '" . chr(0x81) . "')"); - -$res = odbc_exec($conn,"SELECT ID FROM FOO WHERE VARCHAR_COL = '" . chr(0x81) . "'"); +$res = odbc_exec($conn,"SELECT ID FROM bug71171 WHERE VARCHAR_COL = '" . chr(0x81) . "'"); if ($res) { while($record = odbc_fetch_array($res)) var_dump($record); } odbc_close($conn); ?> ---EXPECT-- -array(1) { - ["ID"]=> - string(1) "1" -} --CLEAN-- +--EXPECT-- +array(1) { + ["ID"]=> + string(1) "1" +} diff --git a/ext/odbc/tests/bug73448.phpt b/ext/odbc/tests/bug73448.phpt index eae5aa8ec0e98..8ec4456323214 100644 --- a/ext/odbc/tests/bug73448.phpt +++ b/ext/odbc/tests/bug73448.phpt @@ -4,6 +4,8 @@ Bug #73448 odbc_errormsg returns trash, always 513 bytes odbc --SKIPIF-- +--CONFLICTS-- +odbc --FILE-- +--CLEAN-- + --EXPECT-- array(3) { @@ -38,18 +48,7 @@ array(3) { ["i"]=> string(3) "102" ["txt"]=> - string(12) "Müsliriegel" + string(17) "Lorem ipsum dolor" ["k"]=> string(2) "34" } ---CLEAN-- - diff --git a/ext/odbc/tests/odbc_columns_001.phpt b/ext/odbc/tests/odbc_columns_001.phpt index 489d2ccd84ef0..920735b791003 100644 --- a/ext/odbc/tests/odbc_columns_001.phpt +++ b/ext/odbc/tests/odbc_columns_001.phpt @@ -4,6 +4,8 @@ odbc_columns(): Basic test odbc --SKIPIF-- +--CONFLICTS-- +odbc --FILE-- +--CLEAN-- + --EXPECTF-- resource(%d) of type (odbc result) diff --git a/ext/odbc/tests/odbc_data_source_001.phpt b/ext/odbc/tests/odbc_data_source_001.phpt index b546de2cd9d8c..be3c1226b68b9 100644 --- a/ext/odbc/tests/odbc_data_source_001.phpt +++ b/ext/odbc/tests/odbc_data_source_001.phpt @@ -4,10 +4,10 @@ odbc_data_source(): Basic test odbc --SKIPIF-- --FILE-- --FILE-- --EXPECTF-- -Warning: odbc_exec(): Argument #3 must be of type int, string given in %s on line %d - -Warning: odbc_exec(): SQL error: %s in %s on line %d - -Warning: odbc_exec(): Argument #3 must be of type int, string given in %s on line %d - -Warning: odbc_exec(): SQL error: %s in %s on line %d - -Warning: odbc_exec(): SQL error: %s in %s on line %d - -Warning: odbc_exec(): SQL error: %s in %s on line %d - Warning: odbc_exec(): SQL error: %s in %s on line %d Warning: odbc_exec(): SQL error: %s in %s on line %d diff --git a/ext/odbc/tests/odbc_exec_002.phpt b/ext/odbc/tests/odbc_exec_002.phpt index 44c1baad5ac8e..347d633905384 100644 --- a/ext/odbc/tests/odbc_exec_002.phpt +++ b/ext/odbc/tests/odbc_exec_002.phpt @@ -11,14 +11,10 @@ include 'config.inc'; $conn = odbc_connect($dsn, $user, $pass); -odbc_exec($conn, 'CREATE DATABASE odbcTEST'); +odbc_exec($conn, 'CREATE TABLE exec2 (TEST INT)'); +odbc_exec($conn, 'INSERT INTO exec2 VALUES (1), (2)'); -odbc_exec($conn, 'CREATE TABLE FOO (TEST INT)'); - -odbc_exec($conn, 'INSERT INTO FOO VALUES (1)'); -odbc_exec($conn, 'INSERT INTO FOO VALUES (2)'); - -$res = odbc_exec($conn, 'SELECT * FROM FOO'); +$res = odbc_exec($conn, 'SELECT * FROM exec2'); var_dump(odbc_fetch_row($res)); var_dump(odbc_result($res, 'test')); @@ -28,8 +24,7 @@ var_dump(odbc_fetch_array($res)); --EXPECT-- bool(true) diff --git a/ext/odbc/tests/odbc_fetch_array_001.phpt b/ext/odbc/tests/odbc_fetch_array_001.phpt index b212adcb1f49b..bf13ed25e4d64 100644 --- a/ext/odbc/tests/odbc_fetch_array_001.phpt +++ b/ext/odbc/tests/odbc_fetch_array_001.phpt @@ -12,15 +12,13 @@ include 'config.inc'; $conn = odbc_connect($dsn, $user, $pass); odbc_exec($conn, 'CREATE TABLE fetch_array (foo INT)'); -odbc_exec($conn, 'INSERT INTO fetch_array VALUES (1)'); -odbc_exec($conn, 'INSERT INTO fetch_array VALUES (2)'); -odbc_exec($conn, 'INSERT INTO fetch_array VALUES (3)'); +odbc_exec($conn, 'INSERT INTO fetch_array VALUES (1), (2)'); $res = odbc_exec($conn, 'SELECT * FROM fetch_array'); -var_dump(odbc_fetch_array($res, 1)); var_dump(odbc_fetch_array($res)); -var_dump(odbc_fetch_array($res, 3)); +var_dump(odbc_fetch_array($res, 0)); +var_dump(odbc_fetch_array($res, 2)); var_dump(odbc_fetch_array($res, 4)); ?> @@ -41,6 +39,6 @@ array(1) { } array(1) { ["foo"]=> - string(1) "3" + string(1) "2" } bool(false) diff --git a/ext/odbc/tests/odbc_fetch_into_001.phpt b/ext/odbc/tests/odbc_fetch_into_001.phpt index 5053552ce5da7..19f7f79268c58 100644 --- a/ext/odbc/tests/odbc_fetch_into_001.phpt +++ b/ext/odbc/tests/odbc_fetch_into_001.phpt @@ -12,15 +12,10 @@ include 'config.inc'; $conn = odbc_connect($dsn, $user, $pass); odbc_exec($conn, 'CREATE TABLE fetch_into (foo INT)'); -odbc_exec($conn, 'INSERT INTO fetch_into VALUES (1)'); -odbc_exec($conn, 'INSERT INTO fetch_into VALUES (2)'); -odbc_exec($conn, 'INSERT INTO fetch_into VALUES (3)'); +odbc_exec($conn, 'INSERT INTO fetch_into VALUES (1), (2)'); $res = odbc_exec($conn, 'SELECT * FROM fetch_into'); -$arr = []; -var_dump(odbc_fetch_into($res, $arr, 1)); -var_dump($arr); $arr = []; var_dump(odbc_fetch_into($res, $arr)); var_dump($arr); @@ -28,6 +23,9 @@ $arr = []; var_dump(odbc_fetch_into($res, $arr, 0)); var_dump($arr); $arr = []; +var_dump(odbc_fetch_into($res, $arr, 2)); +var_dump($arr); +$arr = []; var_dump(odbc_fetch_into($res, $arr, 4)); var_dump($arr); @@ -52,7 +50,7 @@ array(1) { int(1) array(1) { [0]=> - string(1) "3" + string(1) "2" } bool(false) array(0) { diff --git a/ext/odbc/tests/odbc_fetch_object_001.phpt b/ext/odbc/tests/odbc_fetch_object_001.phpt index 4ea407562eb75..62a8fc3b2c7e5 100644 --- a/ext/odbc/tests/odbc_fetch_object_001.phpt +++ b/ext/odbc/tests/odbc_fetch_object_001.phpt @@ -12,15 +12,13 @@ include 'config.inc'; $conn = odbc_connect($dsn, $user, $pass); odbc_exec($conn, 'CREATE TABLE fetch_object (foo INT)'); -odbc_exec($conn, 'INSERT INTO fetch_object VALUES (1)'); -odbc_exec($conn, 'INSERT INTO fetch_object VALUES (2)'); -odbc_exec($conn, 'INSERT INTO fetch_object VALUES (3)'); +odbc_exec($conn, 'INSERT INTO fetch_object VALUES (1), (2)'); $res = odbc_exec($conn, 'SELECT * FROM fetch_object'); -var_dump(odbc_fetch_object($res, 1)); var_dump(odbc_fetch_object($res)); var_dump(odbc_fetch_object($res, 0)); +var_dump(odbc_fetch_object($res, 2)); var_dump(odbc_fetch_object($res, 4)); ?> @@ -41,6 +39,6 @@ object(stdClass)#%d (%d) { } object(stdClass)#%d (%d) { ["foo"]=> - string(1) "3" + string(1) "2" } bool(false) diff --git a/ext/odbc/tests/odbc_fetch_row_001.phpt b/ext/odbc/tests/odbc_fetch_row_001.phpt index 4cecb513a067f..4e5e6b69d5b88 100644 --- a/ext/odbc/tests/odbc_fetch_row_001.phpt +++ b/ext/odbc/tests/odbc_fetch_row_001.phpt @@ -13,9 +13,7 @@ $conn = odbc_connect($dsn, $user, $pass); odbc_exec($conn, 'CREATE TABLE fetch_row (test INT)'); -odbc_exec($conn, 'INSERT INTO fetch_row VALUES (1)'); -odbc_exec($conn, 'INSERT INTO fetch_row VALUES (2)'); -odbc_exec($conn, 'INSERT INTO fetch_row VALUES (3)'); +odbc_exec($conn, 'INSERT INTO fetch_row VALUES (1), (2)'); $res = odbc_exec($conn, 'SELECT * FROM fetch_row'); @@ -27,7 +25,7 @@ var_dump(odbc_result($res, 'test')); var_dump(odbc_fetch_row($res, null)); var_dump(odbc_result($res, 'test')); -var_dump(odbc_fetch_row($res, 3)); +var_dump(odbc_fetch_row($res, 2)); var_dump(odbc_result($res, 'test')); var_dump(odbc_fetch_row($res, 4)); @@ -48,5 +46,5 @@ string(1) "1" bool(true) string(1) "2" bool(true) -string(1) "3" +string(1) "2" bool(false) diff --git a/ext/odbc/tests/odbc_field_len_001.phpt b/ext/odbc/tests/odbc_field_len_001.phpt index b4697fff75f25..81b6fe9344fd8 100644 --- a/ext/odbc/tests/odbc_field_len_001.phpt +++ b/ext/odbc/tests/odbc_field_len_001.phpt @@ -12,7 +12,6 @@ include 'config.inc'; $conn = odbc_connect($dsn, $user, $pass); odbc_exec($conn, 'CREATE TABLE field_len (foo INT, bar TEXT, baz VARBINARY(50))'); -odbc_exec($conn, "INSERT INTO field_len VALUES (1, 'bar', CONVERT(VARBINARY(50), 'baz'))"); $res = odbc_exec($conn, 'SELECT * FROM field_len'); try { diff --git a/ext/odbc/tests/odbc_field_name_001.phpt b/ext/odbc/tests/odbc_field_name_001.phpt index da34da9e022e1..f26e9cd205629 100644 --- a/ext/odbc/tests/odbc_field_name_001.phpt +++ b/ext/odbc/tests/odbc_field_name_001.phpt @@ -12,7 +12,6 @@ include 'config.inc'; $conn = odbc_connect($dsn, $user, $pass); odbc_exec($conn, 'CREATE TABLE field_name (foo INT, bar INT, baz INT)'); -odbc_exec($conn, 'INSERT INTO field_name VALUES (1, 2, 3)'); $res = odbc_exec($conn, 'SELECT * FROM field_name'); try { diff --git a/ext/odbc/tests/odbc_field_num_001.phpt b/ext/odbc/tests/odbc_field_num_001.phpt index ab4bbcc4d00b3..3191f939fc243 100644 --- a/ext/odbc/tests/odbc_field_num_001.phpt +++ b/ext/odbc/tests/odbc_field_num_001.phpt @@ -12,7 +12,6 @@ include 'config.inc'; $conn = odbc_connect($dsn, $user, $pass); odbc_exec($conn, 'CREATE TABLE field_num (foo INT, bar REAL, baz VARBINARY(50))'); -odbc_exec($conn, "INSERT INTO field_num VALUES (1, 3.14, CONVERT(VARBINARY(50), 'baz'))"); $res = odbc_exec($conn, 'SELECT * FROM field_num'); var_dump(odbc_field_num($res, "foo")); diff --git a/ext/odbc/tests/odbc_field_precision_001.phpt b/ext/odbc/tests/odbc_field_precision_001.phpt index 609401ba263c9..ccbe851f1d6b2 100644 --- a/ext/odbc/tests/odbc_field_precision_001.phpt +++ b/ext/odbc/tests/odbc_field_precision_001.phpt @@ -12,7 +12,6 @@ include 'config.inc'; $conn = odbc_connect($dsn, $user, $pass); odbc_exec($conn, 'CREATE TABLE field_precision (foo INT, bar REAL, baz VARBINARY(50))'); -odbc_exec($conn, "INSERT INTO field_precision VALUES (1, 3.14, CONVERT(VARBINARY(50), 'baz'))"); $res = odbc_exec($conn, 'SELECT * FROM field_precision'); try { diff --git a/ext/odbc/tests/odbc_field_scale_001.phpt b/ext/odbc/tests/odbc_field_scale_001.phpt index 3bb2da8e1c2cb..5fef7c2d9e6a1 100644 --- a/ext/odbc/tests/odbc_field_scale_001.phpt +++ b/ext/odbc/tests/odbc_field_scale_001.phpt @@ -12,7 +12,6 @@ include 'config.inc'; $conn = odbc_connect($dsn, $user, $pass); odbc_exec($conn, 'CREATE TABLE field_scale (foo INT, bar REAL, baz VARBINARY(50))'); -odbc_exec($conn, "INSERT INTO field_scale VALUES (1, 3.14, CONVERT(VARBINARY(50), 'baz'))"); $res = odbc_exec($conn, 'SELECT * FROM field_scale'); try { diff --git a/ext/odbc/tests/odbc_field_type_001.phpt b/ext/odbc/tests/odbc_field_type_001.phpt index 81774c7dbc0ed..374136c4e47b2 100644 --- a/ext/odbc/tests/odbc_field_type_001.phpt +++ b/ext/odbc/tests/odbc_field_type_001.phpt @@ -12,7 +12,6 @@ include 'config.inc'; $conn = odbc_connect($dsn, $user, $pass); odbc_exec($conn, 'CREATE TABLE field_type (foo INT, bar TEXT, baz VARBINARY(50))'); -odbc_exec($conn, "INSERT INTO field_type VALUES (1, 'bar', CONVERT(VARBINARY(50), 'baz'))"); $res = odbc_exec($conn, 'SELECT * FROM field_type'); try { diff --git a/ext/odbc/tests/odbc_free_result_001.phpt b/ext/odbc/tests/odbc_free_result_001.phpt index 653e5a0d9facb..8fc4075581a2f 100644 --- a/ext/odbc/tests/odbc_free_result_001.phpt +++ b/ext/odbc/tests/odbc_free_result_001.phpt @@ -11,15 +11,11 @@ include 'config.inc'; $conn = odbc_connect($dsn, $user, $pass); -odbc_exec($conn, 'CREATE DATABASE odbcTEST'); +odbc_exec($conn, 'CREATE TABLE free_result (TEST INT NOT NULL)'); -odbc_exec($conn, 'CREATE TABLE FOO (TEST INT NOT NULL)'); -odbc_exec($conn, 'ALTER TABLE FOO ADD PRIMARY KEY (TEST)'); +odbc_exec($conn, 'INSERT INTO free_result VALUES (1), (2)'); -odbc_exec($conn, 'INSERT INTO FOO VALUES (1)'); -odbc_exec($conn, 'INSERT INTO FOO VALUES (2)'); - -$res = odbc_exec($conn, 'SELECT * FROM FOO'); +$res = odbc_exec($conn, 'SELECT * FROM free_result'); var_dump(odbc_fetch_row($res)); var_dump(odbc_result($res, 'test')); @@ -44,8 +40,7 @@ try { --EXPECT-- bool(true) diff --git a/ext/odbc/tests/odbc_num_fields_001.phpt b/ext/odbc/tests/odbc_num_fields_001.phpt index b815c971e01cd..f5c55d583a2dd 100644 --- a/ext/odbc/tests/odbc_num_fields_001.phpt +++ b/ext/odbc/tests/odbc_num_fields_001.phpt @@ -1,5 +1,5 @@ --TEST-- -odbc_num_rows(): Getting the number of fields +odbc_num_fields(): Getting the number of fields --EXTENSIONS-- odbc --SKIPIF-- diff --git a/ext/odbc/tests/odbc_num_rows_001.phpt b/ext/odbc/tests/odbc_num_rows_001.phpt index 8c2af1a1f147f..342762f920f7c 100644 --- a/ext/odbc/tests/odbc_num_rows_001.phpt +++ b/ext/odbc/tests/odbc_num_rows_001.phpt @@ -12,9 +12,7 @@ include 'config.inc'; $conn = odbc_connect($dsn, $user, $pass); odbc_exec($conn, 'CREATE TABLE num_rows (test INT)'); -odbc_exec($conn, 'INSERT INTO num_rows VALUES (1)'); -odbc_exec($conn, 'INSERT INTO num_rows VALUES (2)'); -odbc_exec($conn, 'INSERT INTO num_rows VALUES (3)'); +odbc_exec($conn, 'INSERT INTO num_rows VALUES (1), (2), (3)'); $res = odbc_exec($conn, 'SELECT * FROM num_rows'); var_dump(odbc_num_rows($res)); diff --git a/ext/odbc/tests/odbc_primarykeys_001.phpt b/ext/odbc/tests/odbc_primarykeys_001.phpt index 17b42c6ff51eb..543a22733da71 100644 --- a/ext/odbc/tests/odbc_primarykeys_001.phpt +++ b/ext/odbc/tests/odbc_primarykeys_001.phpt @@ -3,9 +3,20 @@ odbc_primarykeys(): Basic test for odbc_primarykeys() --EXTENSIONS-- odbc --SKIPIF-- - ---XFAIL-- -Doesn't work with MS SQL + --FILE-- ---EXPECT-- +--EXPECTF-- +bool(false) +bool(false) bool(false) +array(%d) { + ["TABLE_CAT"]=> + string(%d) "PrimarykeysTest" + ["TABLE_SCHEM"]=> + string(%d) "dbo" + ["TABLE_NAME"]=> + string(%d) "primarykeys" + ["COLUMN_NAME"]=> + string(%d) "test" + ["KEY_SEQ"]=> + string(%d) "1" + ["PK_NAME"]=> + string(%d) "primarykeys_pk" +} +array(%d) { + ["TABLE_CAT"]=> + string(%d) "PrimarykeysTest" + ["TABLE_SCHEM"]=> + string(%d) "dbo" + ["TABLE_NAME"]=> + string(%d) "primarykeys" + ["COLUMN_NAME"]=> + string(%d) "test" + ["KEY_SEQ"]=> + string(%d) "1" + ["PK_NAME"]=> + string(%d) "primarykeys_pk" +} diff --git a/ext/pdo/tests/bug_36798.phpt b/ext/pdo/tests/bug_36798.phpt index dea1a5b92a23c..da258bc87d34d 100644 --- a/ext/pdo/tests/bug_36798.phpt +++ b/ext/pdo/tests/bug_36798.phpt @@ -13,6 +13,8 @@ if (!strncasecmp(getenv('PDOTEST_DSN'), 'oci', strlen('oci'))){ if (!strpos(strtolower(getenv('PDOTEST_DSN')), 'charset=we8mswin1252')) die('skip expected output valid for Oracle with WE8MSWIN1252 character set'); } elseif (!strncasecmp(getenv('PDOTEST_DSN'), 'dblib', strlen('dblib'))) { die('skip not for pdo_dblib'); +} elseif (!strncasecmp(getenv('PDOTEST_DSN'), 'odbc', strlen('odbc'))) { + die('skip not for pdo_odbc'); } ?> diff --git a/ext/pdo_odbc/tests/bug80783a.phpt b/ext/pdo_odbc/tests/bug80783a.phpt index e070cd30c3f36..ba2c3e960d9fb 100644 --- a/ext/pdo_odbc/tests/bug80783a.phpt +++ b/ext/pdo_odbc/tests/bug80783a.phpt @@ -6,6 +6,10 @@ pdo_odbc --FILE-- Date: Tue, 22 Aug 2023 11:01:28 +0200 Subject: [PATCH 46/47] Expose PDO_ODBC_TYPE to userland --- UPGRADING | 3 +++ ext/pdo_odbc/pdo_odbc.c | 3 +++ ext/pdo_odbc/pdo_odbc.stub.php | 9 +++++++++ ext/pdo_odbc/pdo_odbc_arginfo.h | 9 +++++++++ ext/pdo_odbc/tests/bug80783a.phpt | 3 ++- 5 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 ext/pdo_odbc/pdo_odbc.stub.php create mode 100644 ext/pdo_odbc/pdo_odbc_arginfo.h diff --git a/UPGRADING b/UPGRADING index 3e8f694c801eb..5696920d96a7a 100644 --- a/UPGRADING +++ b/UPGRADING @@ -497,6 +497,9 @@ PHP 8.3 UPGRADE NOTES - PCNTL: . SIGINFO +- PDO_ODBC + . PDO_ODBC_TYPE + - PGSQL: . PGSQL_TRACE_SUPPRESS_TIMESTAMPS . PGSQL_TRACE_REGRESS_MODE diff --git a/ext/pdo_odbc/pdo_odbc.c b/ext/pdo_odbc/pdo_odbc.c index 98e684bc5a95b..8e090a397f061 100644 --- a/ext/pdo_odbc/pdo_odbc.c +++ b/ext/pdo_odbc/pdo_odbc.c @@ -25,6 +25,7 @@ #include "pdo/php_pdo_driver.h" #include "php_pdo_odbc.h" #include "php_pdo_odbc_int.h" +#include "pdo_odbc_arginfo.h" /* {{{ pdo_odbc_deps[] */ static const zend_module_dep pdo_odbc_deps[] = { @@ -96,6 +97,8 @@ PHP_MINIT_FUNCTION(pdo_odbc) } #endif + register_pdo_odbc_symbols(module_number); + REGISTER_PDO_CLASS_CONST_LONG("ODBC_ATTR_USE_CURSOR_LIBRARY", PDO_ODBC_ATTR_USE_CURSOR_LIBRARY); REGISTER_PDO_CLASS_CONST_LONG("ODBC_ATTR_ASSUME_UTF8", PDO_ODBC_ATTR_ASSUME_UTF8); REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_IF_NEEDED", SQL_CUR_USE_IF_NEEDED); diff --git a/ext/pdo_odbc/pdo_odbc.stub.php b/ext/pdo_odbc/pdo_odbc.stub.php new file mode 100644 index 0000000000000..745be283375da --- /dev/null +++ b/ext/pdo_odbc/pdo_odbc.stub.php @@ -0,0 +1,9 @@ + From de3adc047115344cfacd2189b950c9dc5a3ef466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Wed, 19 May 2021 14:21:19 +0200 Subject: [PATCH 47/47] Migrate ext/odbc resources to opaque objects --- Zend/Optimizer/zend_func_infos.h | 25 - ext/odbc/odbc.stub.php | 956 ++++++++++++++----------------- ext/odbc/odbc_arginfo.h | 134 +++-- ext/odbc/php_odbc.c | 893 +++++++++++++++-------------- ext/odbc/php_odbc_includes.h | 16 +- ext/odbc/tests/bug78473.phpt | 6 +- 6 files changed, 989 insertions(+), 1041 deletions(-) diff --git a/Zend/Optimizer/zend_func_infos.h b/Zend/Optimizer/zend_func_infos.h index 6dc947e3cc839..30a63a9d06869 100644 --- a/Zend/Optimizer/zend_func_infos.h +++ b/Zend/Optimizer/zend_func_infos.h @@ -316,31 +316,6 @@ static const func_info_t func_infos[] = { FN("oci_get_implicit_resultset", MAY_BE_RESOURCE|MAY_BE_FALSE), FN("oci_password_change", MAY_BE_RESOURCE|MAY_BE_BOOL), FN("oci_new_cursor", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_prepare", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_exec", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_connect", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_pconnect", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_tables", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_columns", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_gettypeinfo", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_primarykeys", MAY_BE_RESOURCE|MAY_BE_FALSE), -#if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35) - FN("odbc_procedurecolumns", MAY_BE_RESOURCE|MAY_BE_FALSE), -#endif -#if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35) - FN("odbc_procedures", MAY_BE_RESOURCE|MAY_BE_FALSE), -#endif -#if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35) - FN("odbc_foreignkeys", MAY_BE_RESOURCE|MAY_BE_FALSE), -#endif - FN("odbc_specialcolumns", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_statistics", MAY_BE_RESOURCE|MAY_BE_FALSE), -#if !defined(HAVE_DBMAKER) && !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) &&!defined(HAVE_SOLID_35) - FN("odbc_tableprivileges", MAY_BE_RESOURCE|MAY_BE_FALSE), -#endif -#if !defined(HAVE_DBMAKER) && !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) &&!defined(HAVE_SOLID_35) - FN("odbc_columnprivileges", MAY_BE_RESOURCE|MAY_BE_FALSE), -#endif F1("opcache_get_status", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_FALSE), F1("opcache_get_configuration", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_FALSE), F1("openssl_x509_parse", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_FALSE), diff --git a/ext/odbc/odbc.stub.php b/ext/odbc/odbc.stub.php index 9606a38633f34..3c03090ed08e5 100644 --- a/ext/odbc/odbc.stub.php +++ b/ext/odbc/odbc.stub.php @@ -2,520 +2,442 @@ /** @generate-class-entries */ -/** - * @var string - * @cvalue PHP_ODBC_TYPE - */ -const ODBC_TYPE = UNKNOWN; -/** - * @var int - * @cvalue PHP_ODBC_BINMODE_PASSTHRU - */ -const ODBC_BINMODE_PASSTHRU = UNKNOWN; -/** - * @var int - * @cvalue PHP_ODBC_BINMODE_RETURN - */ -const ODBC_BINMODE_RETURN = UNKNOWN; -/** - * @var int - * @cvalue PHP_ODBC_BINMODE_CONVERT - */ -const ODBC_BINMODE_CONVERT = UNKNOWN; - -/* Define Constants for options. These Constants are defined in */ - -/** - * @var int - * @cvalue SQL_ODBC_CURSORS - */ -const SQL_ODBC_CURSORS = UNKNOWN; -/** - * @var int - * @cvalue SQL_CUR_USE_DRIVER - */ -const SQL_CUR_USE_DRIVER = UNKNOWN; -/** - * @var int - * @cvalue SQL_CUR_USE_IF_NEEDED - */ -const SQL_CUR_USE_IF_NEEDED = UNKNOWN; -/** - * @var int - * @cvalue SQL_CUR_USE_ODBC - */ -const SQL_CUR_USE_ODBC = UNKNOWN; - -/** - * @var int - * @cvalue SQL_CONCURRENCY - */ -const SQL_CONCURRENCY = UNKNOWN; -/** - * @var int - * @cvalue SQL_CONCUR_READ_ONLY - */ -const SQL_CONCUR_READ_ONLY = UNKNOWN; -/** - * @var int - * @cvalue SQL_CONCUR_LOCK - */ -const SQL_CONCUR_LOCK = UNKNOWN; -/** - * @var int - * @cvalue SQL_CONCUR_ROWVER - */ -const SQL_CONCUR_ROWVER = UNKNOWN; -/** - * @var int - * @cvalue SQL_CONCUR_VALUES - */ -const SQL_CONCUR_VALUES = UNKNOWN; - -/** - * @var int - * @cvalue SQL_CURSOR_TYPE - */ -const SQL_CURSOR_TYPE = UNKNOWN; -/** - * @var int - * @cvalue SQL_CURSOR_FORWARD_ONLY - */ -const SQL_CURSOR_FORWARD_ONLY = UNKNOWN; -/** - * @var int - * @cvalue SQL_CURSOR_KEYSET_DRIVEN - */ -const SQL_CURSOR_KEYSET_DRIVEN = UNKNOWN; -/** - * @var int - * @cvalue SQL_CURSOR_DYNAMIC - */ -const SQL_CURSOR_DYNAMIC = UNKNOWN; -/** - * @var int - * @cvalue SQL_CURSOR_STATIC - */ -const SQL_CURSOR_STATIC = UNKNOWN; - -/** - * @var int - * @cvalue SQL_KEYSET_SIZE - */ -const SQL_KEYSET_SIZE = UNKNOWN; - -/* these are for the Data Source type */ - -/** - * @var int - * @cvalue SQL_FETCH_FIRST - */ -const SQL_FETCH_FIRST = UNKNOWN; -/** - * @var int - * @cvalue SQL_FETCH_NEXT - */ -const SQL_FETCH_NEXT = UNKNOWN; - -/* register the standard data types */ - -/** - * @var int - * @cvalue SQL_CHAR - */ -const SQL_CHAR = UNKNOWN; -/** - * @var int - * @cvalue SQL_VARCHAR - */ -const SQL_VARCHAR = UNKNOWN; -/** - * @var int - * @cvalue SQL_LONGVARCHAR - */ -const SQL_LONGVARCHAR = UNKNOWN; -/** - * @var int - * @cvalue SQL_DECIMAL - */ -const SQL_DECIMAL = UNKNOWN; -/** - * @var int - * @cvalue SQL_NUMERIC - */ -const SQL_NUMERIC = UNKNOWN; -/** - * @var int - * @cvalue SQL_BIT - */ -const SQL_BIT = UNKNOWN; -/** - * @var int - * @cvalue SQL_TINYINT - */ -const SQL_TINYINT = UNKNOWN; -/** - * @var int - * @cvalue SQL_SMALLINT - */ -const SQL_SMALLINT = UNKNOWN; -/** - * @var int - * @cvalue SQL_INTEGER - */ -const SQL_INTEGER = UNKNOWN; -/** - * @var int - * @cvalue SQL_BIGINT - */ -const SQL_BIGINT = UNKNOWN; -/** - * @var int - * @cvalue SQL_REAL - */ -const SQL_REAL = UNKNOWN; -/** - * @var int - * @cvalue SQL_FLOAT - */ -const SQL_FLOAT = UNKNOWN; -/** - * @var int - * @cvalue SQL_DOUBLE - */ -const SQL_DOUBLE = UNKNOWN; -/** - * @var int - * @cvalue SQL_BINARY - */ -const SQL_BINARY = UNKNOWN; -/** - * @var int - * @cvalue SQL_VARBINARY - */ -const SQL_VARBINARY = UNKNOWN; -/** - * @var int - * @cvalue SQL_LONGVARBINARY - */ -const SQL_LONGVARBINARY = UNKNOWN; -/** - * @var int - * @cvalue SQL_DATE - */ -const SQL_DATE = UNKNOWN; -/** - * @var int - * @cvalue SQL_TIME - */ -const SQL_TIME = UNKNOWN; -/** - * @var int - * @cvalue SQL_TIMESTAMP - */ -const SQL_TIMESTAMP = UNKNOWN; - -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) -/** - * @var int - * @cvalue SQL_TYPE_DATE - */ -const SQL_TYPE_DATE = UNKNOWN; -/** - * @var int - * @cvalue SQL_TYPE_TIME - */ -const SQL_TYPE_TIME = UNKNOWN; -/** - * @var int - * @cvalue SQL_TYPE_TIMESTAMP - */ -const SQL_TYPE_TIMESTAMP = UNKNOWN; -/** - * @var int - * @cvalue SQL_WCHAR - */ -const SQL_WCHAR = UNKNOWN; -/** - * @var int - * @cvalue SQL_WVARCHAR - */ -const SQL_WVARCHAR = UNKNOWN; -/** - * @var int - * @cvalue SQL_WLONGVARCHAR - */ -const SQL_WLONGVARCHAR = UNKNOWN; - -/* SQLSpecialColumns values */ - -/** - * @var int - * @cvalue SQL_BEST_ROWID - */ -const SQL_BEST_ROWID = UNKNOWN; -/** - * @var int - * @cvalue SQL_ROWVER - */ -const SQL_ROWVER = UNKNOWN; -/** - * @var int - * @cvalue SQL_SCOPE_CURROW - */ -const SQL_SCOPE_CURROW = UNKNOWN; -/** - * @var int - * @cvalue SQL_SCOPE_TRANSACTION - */ -const SQL_SCOPE_TRANSACTION = UNKNOWN; -/** - * @var int - * @cvalue SQL_SCOPE_SESSION - */ -const SQL_SCOPE_SESSION = UNKNOWN; -/** - * @var int - * @cvalue SQL_NO_NULLS - */ -const SQL_NO_NULLS = UNKNOWN; -/** - * @var int - * @cvalue SQL_NULLABLE - */ -const SQL_NULLABLE = UNKNOWN; - -/* SQLStatistics values */ - -/** - * @var int - * @cvalue SQL_INDEX_UNIQUE - */ -const SQL_INDEX_UNIQUE = UNKNOWN; -/** - * @var int - * @cvalue SQL_INDEX_ALL - */ -const SQL_INDEX_ALL = UNKNOWN; -/** - * @var int - * @cvalue SQL_ENSURE - */ -const SQL_ENSURE = UNKNOWN; -/** - * @var int - * @cvalue SQL_QUICK - */ -const SQL_QUICK = UNKNOWN; - -#endif - - - -function odbc_close_all(): void {} - -/** @param resource $statement */ -function odbc_binmode($statement, int $mode): bool {} - -/** @param resource $statement */ -function odbc_longreadlen($statement, int $length): bool {} - -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_prepare($odbc, string $query) {} - -/** @param resource $statement */ -function odbc_execute($statement, array $params = []): bool {} - -/** @param resource $statement */ -function odbc_cursor($statement): string|false {} - -#ifdef HAVE_SQLDATASOURCES -/** @param resource $odbc */ -function odbc_data_source($odbc, int $fetch_type): array|null|false {} -#endif - -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_exec($odbc, string $query) {} - -/** - * @param resource $odbc - * @return resource|false - * @alias odbc_exec - */ -function odbc_do($odbc, string $query) {} - -#ifdef PHP_ODBC_HAVE_FETCH_HASH -/** @param resource $statement */ -function odbc_fetch_object($statement, int $row = -1): stdClass|false {} - -/** @param resource $statement */ -function odbc_fetch_array($statement, int $row = -1): array|false {} -#endif - -/** - * @param resource $statement - * @param array $array - */ -function odbc_fetch_into($statement, &$array, int $row = 0): int|false {} - -/** @param resource $statement */ -function odbc_fetch_row($statement, ?int $row = null): bool {} - -/** @param resource $statement */ -function odbc_result($statement, string|int $field): string|bool|null {} - -/** - * @param resource $statement - * @deprecated - */ -function odbc_result_all($statement, string $format = ""): int|false {} - -/** @param resource $statement */ -function odbc_free_result($statement): bool {} - -/** - * @return resource|false - */ -function odbc_connect(string $dsn, string $user, #[\SensitiveParameter] string $password, int $cursor_option = SQL_CUR_USE_DRIVER) {} - -/** - * @return resource|false - */ -function odbc_pconnect(string $dsn, string $user, #[\SensitiveParameter] string $password, int $cursor_option = SQL_CUR_USE_DRIVER) {} - -/** @param resource $odbc */ -function odbc_close($odbc): void {} - -/** @param resource $statement */ -function odbc_num_rows($statement): int {} - -#if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) -/** @param resource $statement */ -function odbc_next_result($statement): bool {} -#endif - -/** @param resource $statement */ -function odbc_num_fields($statement): int {} - -/** @param resource $statement */ -function odbc_field_name($statement, int $field): string|false {} - -/** @param resource $statement */ -function odbc_field_type($statement, int $field): string|false {} - -/** @param resource $statement */ -function odbc_field_len($statement, int $field): int|false {} - -/** - * @param resource $statement - * @alias odbc_field_len - */ -function odbc_field_precision($statement, int $field): int|false {} - -/** @param resource $statement */ -function odbc_field_scale($statement, int $field): int|false {} - -/** @param resource $statement */ -function odbc_field_num($statement, string $field): int|false {} - -/** @param resource $odbc */ -function odbc_autocommit($odbc, ?bool $enable = null): int|bool {} - -/** @param resource $odbc */ -function odbc_commit($odbc): bool {} - -/** @param resource $odbc */ -function odbc_rollback($odbc): bool {} - -/** @param resource|null $odbc */ -function odbc_error($odbc = null): string {} - -/** @param resource|null $odbc */ -function odbc_errormsg($odbc = null): string {} - -/** @param resource $odbc */ -function odbc_setoption($odbc, int $which, int $option, int $value): bool {} - -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_tables($odbc, ?string $catalog = null, ?string $schema = null, ?string $table = null, ?string $types = null) {} - -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_columns($odbc, ?string $catalog = null, ?string $schema = null, ?string $table = null, ?string $column = null) {} - -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_gettypeinfo($odbc, int $data_type = 0) {} - -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_primarykeys($odbc, ?string $catalog, string $schema, string $table) {} - -#if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35) -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_procedurecolumns($odbc, ?string $catalog = null, ?string $schema = null, ?string $procedure = null, ?string $column = null) {} - -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_procedures($odbc, ?string $catalog = null, ?string $schema = null, ?string $procedure = null) {} - -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_foreignkeys($odbc, ?string $pk_catalog, string $pk_schema, string $pk_table, string $fk_catalog, string $fk_schema, string $fk_table) {} -#endif - -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_specialcolumns($odbc, int $type, ?string $catalog, string $schema, string $table, int $scope, int $nullable) {} - -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_statistics($odbc, ?string $catalog, string $schema, string $table, int $unique, int $accuracy) {} - -#if !defined(HAVE_DBMAKER) && !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) &&!defined(HAVE_SOLID_35) -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_tableprivileges($odbc, ?string $catalog, string $schema, string $table) {} - -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_columnprivileges($odbc, ?string $catalog, string $schema, string $table, string $column) {} -#endif - -/* odbc_utils.c */ - -function odbc_connection_string_is_quoted(string $str): bool {} - -function odbc_connection_string_should_quote(string $str): bool {} - -function odbc_connection_string_quote(string $str): string {} +namespace ODBC { + /** + * @strict-properties + * @not-serializable + */ + class Connection + { + } + + /** + * @strict-properties + * @not-serializable + */ + class Result + { + } +} + +namespace { + /** + * @var string + * @cvalue PHP_ODBC_TYPE + */ + const ODBC_TYPE = UNKNOWN; + /** + * @var int + * @cvalue PHP_ODBC_BINMODE_PASSTHRU + */ + const ODBC_BINMODE_PASSTHRU = UNKNOWN; + /** + * @var int + * @cvalue PHP_ODBC_BINMODE_RETURN + */ + const ODBC_BINMODE_RETURN = UNKNOWN; + /** + * @var int + * @cvalue PHP_ODBC_BINMODE_CONVERT + */ + const ODBC_BINMODE_CONVERT = UNKNOWN; + + /* Define Constants for options. These Constants are defined in */ + + /** + * @var int + * @cvalue SQL_ODBC_CURSORS + */ + const SQL_ODBC_CURSORS = UNKNOWN; + /** + * @var int + * @cvalue SQL_CUR_USE_DRIVER + */ + const SQL_CUR_USE_DRIVER = UNKNOWN; + /** + * @var int + * @cvalue SQL_CUR_USE_IF_NEEDED + */ + const SQL_CUR_USE_IF_NEEDED = UNKNOWN; + /** + * @var int + * @cvalue SQL_CUR_USE_ODBC + */ + const SQL_CUR_USE_ODBC = UNKNOWN; + + /** + * @var int + * @cvalue SQL_CONCURRENCY + */ + const SQL_CONCURRENCY = UNKNOWN; + /** + * @var int + * @cvalue SQL_CONCUR_READ_ONLY + */ + const SQL_CONCUR_READ_ONLY = UNKNOWN; + /** + * @var int + * @cvalue SQL_CONCUR_LOCK + */ + const SQL_CONCUR_LOCK = UNKNOWN; + /** + * @var int + * @cvalue SQL_CONCUR_ROWVER + */ + const SQL_CONCUR_ROWVER = UNKNOWN; + /** + * @var int + * @cvalue SQL_CONCUR_VALUES + */ + const SQL_CONCUR_VALUES = UNKNOWN; + + /** + * @var int + * @cvalue SQL_CURSOR_TYPE + */ + const SQL_CURSOR_TYPE = UNKNOWN; + /** + * @var int + * @cvalue SQL_CURSOR_FORWARD_ONLY + */ + const SQL_CURSOR_FORWARD_ONLY = UNKNOWN; + /** + * @var int + * @cvalue SQL_CURSOR_KEYSET_DRIVEN + */ + const SQL_CURSOR_KEYSET_DRIVEN = UNKNOWN; + /** + * @var int + * @cvalue SQL_CURSOR_DYNAMIC + */ + const SQL_CURSOR_DYNAMIC = UNKNOWN; + /** + * @var int + * @cvalue SQL_CURSOR_STATIC + */ + const SQL_CURSOR_STATIC = UNKNOWN; + + /** + * @var int + * @cvalue SQL_KEYSET_SIZE + */ + const SQL_KEYSET_SIZE = UNKNOWN; + + /* these are for the Data Source type */ + + /** + * @var int + * @cvalue SQL_FETCH_FIRST + */ + const SQL_FETCH_FIRST = UNKNOWN; + /** + * @var int + * @cvalue SQL_FETCH_NEXT + */ + const SQL_FETCH_NEXT = UNKNOWN; + + /* register the standard data types */ + + /** + * @var int + * @cvalue SQL_CHAR + */ + const SQL_CHAR = UNKNOWN; + /** + * @var int + * @cvalue SQL_VARCHAR + */ + const SQL_VARCHAR = UNKNOWN; + /** + * @var int + * @cvalue SQL_LONGVARCHAR + */ + const SQL_LONGVARCHAR = UNKNOWN; + /** + * @var int + * @cvalue SQL_DECIMAL + */ + const SQL_DECIMAL = UNKNOWN; + /** + * @var int + * @cvalue SQL_NUMERIC + */ + const SQL_NUMERIC = UNKNOWN; + /** + * @var int + * @cvalue SQL_BIT + */ + const SQL_BIT = UNKNOWN; + /** + * @var int + * @cvalue SQL_TINYINT + */ + const SQL_TINYINT = UNKNOWN; + /** + * @var int + * @cvalue SQL_SMALLINT + */ + const SQL_SMALLINT = UNKNOWN; + /** + * @var int + * @cvalue SQL_INTEGER + */ + const SQL_INTEGER = UNKNOWN; + /** + * @var int + * @cvalue SQL_BIGINT + */ + const SQL_BIGINT = UNKNOWN; + /** + * @var int + * @cvalue SQL_REAL + */ + const SQL_REAL = UNKNOWN; + /** + * @var int + * @cvalue SQL_FLOAT + */ + const SQL_FLOAT = UNKNOWN; + /** + * @var int + * @cvalue SQL_DOUBLE + */ + const SQL_DOUBLE = UNKNOWN; + /** + * @var int + * @cvalue SQL_BINARY + */ + const SQL_BINARY = UNKNOWN; + /** + * @var int + * @cvalue SQL_VARBINARY + */ + const SQL_VARBINARY = UNKNOWN; + /** + * @var int + * @cvalue SQL_LONGVARBINARY + */ + const SQL_LONGVARBINARY = UNKNOWN; + /** + * @var int + * @cvalue SQL_DATE + */ + const SQL_DATE = UNKNOWN; + /** + * @var int + * @cvalue SQL_TIME + */ + const SQL_TIME = UNKNOWN; + /** + * @var int + * @cvalue SQL_TIMESTAMP + */ + const SQL_TIMESTAMP = UNKNOWN; + + #if (defined(ODBCVER) && (ODBCVER >= 0x0300)) + /** + * @var int + * @cvalue SQL_TYPE_DATE + */ + const SQL_TYPE_DATE = UNKNOWN; + /** + * @var int + * @cvalue SQL_TYPE_TIME + */ + const SQL_TYPE_TIME = UNKNOWN; + /** + * @var int + * @cvalue SQL_TYPE_TIMESTAMP + */ + const SQL_TYPE_TIMESTAMP = UNKNOWN; + /** + * @var int + * @cvalue SQL_WCHAR + */ + const SQL_WCHAR = UNKNOWN; + /** + * @var int + * @cvalue SQL_WVARCHAR + */ + const SQL_WVARCHAR = UNKNOWN; + /** + * @var int + * @cvalue SQL_WLONGVARCHAR + */ + const SQL_WLONGVARCHAR = UNKNOWN; + + /* SQLSpecialColumns values */ + + /** + * @var int + * @cvalue SQL_BEST_ROWID + */ + const SQL_BEST_ROWID = UNKNOWN; + /** + * @var int + * @cvalue SQL_ROWVER + */ + const SQL_ROWVER = UNKNOWN; + /** + * @var int + * @cvalue SQL_SCOPE_CURROW + */ + const SQL_SCOPE_CURROW = UNKNOWN; + /** + * @var int + * @cvalue SQL_SCOPE_TRANSACTION + */ + const SQL_SCOPE_TRANSACTION = UNKNOWN; + /** + * @var int + * @cvalue SQL_SCOPE_SESSION + */ + const SQL_SCOPE_SESSION = UNKNOWN; + /** + * @var int + * @cvalue SQL_NO_NULLS + */ + const SQL_NO_NULLS = UNKNOWN; + /** + * @var int + * @cvalue SQL_NULLABLE + */ + const SQL_NULLABLE = UNKNOWN; + + /* SQLStatistics values */ + + /** + * @var int + * @cvalue SQL_INDEX_UNIQUE + */ + const SQL_INDEX_UNIQUE = UNKNOWN; + /** + * @var int + * @cvalue SQL_INDEX_ALL + */ + const SQL_INDEX_ALL = UNKNOWN; + /** + * @var int + * @cvalue SQL_ENSURE + */ + const SQL_ENSURE = UNKNOWN; + /** + * @var int + * @cvalue SQL_QUICK + */ + const SQL_QUICK = UNKNOWN; + + #endif + + function odbc_close_all(): void {} + + function odbc_binmode(ODBC\Result $statement, int $mode): bool {} + + function odbc_longreadlen(ODBC\Result $statement, int $length): bool {} + + function odbc_prepare(ODBC\Connection $odbc, string $query): ODBC\Result|false {} + + function odbc_execute(ODBC\Result $statement, array $params = []): bool {} + + function odbc_cursor(ODBC\Result $statement): string|false {} + + #ifdef HAVE_SQLDATASOURCES + function odbc_data_source(ODBC\Connection $odbc, int $fetch_type): array|null|false {} + #endif + + function odbc_exec(ODBC\Connection $odbc, string $query): ODBC\Result|false {} + + /** @alias odbc_exec */ + function odbc_do(ODBC\Connection $odbc, string $query): ODBC\Result|false {} + + #ifdef PHP_ODBC_HAVE_FETCH_HASH + function odbc_fetch_object(ODBC\Result $statement, ?int $row = null): stdClass|false {} + + function odbc_fetch_array(ODBC\Result $statement, ?int $row = null): array|false {} + #endif + + /** @param array $array */ + function odbc_fetch_into(ODBC\Result $statement, &$array, ?int $row = null): int|false {} + + function odbc_fetch_row(ODBC\Result $statement, ?int $row = null): bool {} + + function odbc_result(ODBC\Result $statement, string|int $field): string|bool|null {} + + /** @deprecated */ + function odbc_result_all(ODBC\Result $statement, string $format = ""): int|false {} + + function odbc_free_result(ODBC\Result $statement): bool {} + + function odbc_connect(string $dsn, string $user, #[\SensitiveParameter] string $password, int $cursor_option = SQL_CUR_USE_DRIVER): ODBC\Connection|false {} + + function odbc_pconnect(string $dsn, string $user, #[\SensitiveParameter] string $password, int $cursor_option = SQL_CUR_USE_DRIVER): ODBC\Connection|false {} + + function odbc_close(ODBC\Connection $odbc): void {} + + function odbc_num_rows(ODBC\Result $statement): int {} + + #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) + function odbc_next_result(ODBC\Result $statement): bool {} + #endif + + function odbc_num_fields(ODBC\Result $statement): int {} + + function odbc_field_name(ODBC\Result $statement, int $field): string|false {} + + function odbc_field_type(ODBC\Result $statement, int $field): string|false {} + + function odbc_field_len(ODBC\Result $statement, int $field): int|false {} + + /** @alias odbc_field_len */ + function odbc_field_precision(ODBC\Result $statement, int $field): int|false {} + + function odbc_field_scale(ODBC\Result $statement, int $field): int|false {} + + function odbc_field_num(ODBC\Result $statement, string $field): int|false {} + + function odbc_autocommit(ODBC\Connection $odbc, ?bool $enable = null): int|bool {} + + function odbc_commit(ODBC\Connection $odbc): bool {} + + function odbc_rollback(ODBC\Connection $odbc): bool {} + + function odbc_error(?ODBC\Connection $odbc = null): string {} + + function odbc_errormsg(?ODBC\Connection $odbc = null): string {} + + function odbc_setoption(ODBC\Connection|ODBC\Result $odbc, int $which, int $option, int $value): bool {} + + function odbc_tables(ODBC\Connection $odbc, ?string $catalog = null, ?string $schema = null, ?string $table = null, ?string $types = null): ODBC\Result|false {} + + function odbc_columns(ODBC\Connection $odbc, ?string $catalog = null, ?string $schema = null, ?string $table = null, ?string $column = null): ODBC\Result|false {} + + function odbc_gettypeinfo(ODBC\Connection $odbc, int $data_type = 0): ODBC\Result|false {} + + function odbc_primarykeys(ODBC\Connection $odbc, ?string $catalog, string $schema, string $table): ODBC\Result|false {} + + #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35) + function odbc_procedurecolumns(ODBC\Connection $odbc, ?string $catalog = null, ?string $schema = null, ?string $procedure = null, ?string $column = null): ODBC\Result|false {} + + function odbc_procedures(ODBC\Connection $odbc, ?string $catalog = null, ?string $schema = null, ?string $procedure = null): ODBC\Result|false {} + + function odbc_foreignkeys(ODBC\Connection $odbc, ?string $pk_catalog, string $pk_schema, string $pk_table, string $fk_catalog, string $fk_schema, string $fk_table): ODBC\Result|false {} + #endif + + function odbc_specialcolumns(ODBC\Connection $odbc, int $type, ?string $catalog, string $schema, string $table, int $scope, int $nullable): ODBC\Result|false {} + + function odbc_statistics(ODBC\Connection $odbc, ?string $catalog, string $schema, string $table, int $unique, int $accuracy): ODBC\Result|false {} + + #if !defined(HAVE_DBMAKER) && !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) &&!defined(HAVE_SOLID_35) + function odbc_tableprivileges(ODBC\Connection $odbc, ?string $catalog, string $schema, string $table): ODBC\Result|false {} + + function odbc_columnprivileges(ODBC\Connection $odbc, ?string $catalog, string $schema, string $table, string $column): ODBC\Result|false {} + #endif + + /* odbc_utils.c */ + + function odbc_connection_string_is_quoted(string $str): bool {} + + function odbc_connection_string_should_quote(string $str): bool {} + + function odbc_connection_string_quote(string $str): string {} +} diff --git a/ext/odbc/odbc_arginfo.h b/ext/odbc/odbc_arginfo.h index 574d829daad42..b2f1619ce2d96 100644 --- a/ext/odbc/odbc_arginfo.h +++ b/ext/odbc/odbc_arginfo.h @@ -1,36 +1,36 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 0417b68a519527b0ee916bad75116ffe4a3ad304 */ + * Stub hash: 4d41f8189da5268979026fa5f54d6acacc17e5be */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_close_all, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_binmode, 0, 2, _IS_BOOL, 0) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) ZEND_ARG_TYPE_INFO(0, mode, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_longreadlen, 0, 2, _IS_BOOL, 0) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_prepare, 0, 0, 2) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_prepare, 0, 2, ODBC\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_ARG_TYPE_INFO(0, query, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_execute, 0, 1, _IS_BOOL, 0) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, params, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_cursor, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) ZEND_END_ARG_INFO() #if defined(HAVE_SQLDATASOURCES) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_data_source, 0, 2, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE) - ZEND_ARG_INFO(0, odbc) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_ARG_TYPE_INFO(0, fetch_type, IS_LONG, 0) ZEND_END_ARG_INFO() #endif @@ -41,44 +41,44 @@ ZEND_END_ARG_INFO() #if defined(PHP_ODBC_HAVE_FETCH_HASH) ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_fetch_object, 0, 1, stdClass, MAY_BE_FALSE) - ZEND_ARG_INFO(0, statement) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, row, IS_LONG, 0, "-1") + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, row, IS_LONG, 1, "null") ZEND_END_ARG_INFO() #endif #if defined(PHP_ODBC_HAVE_FETCH_HASH) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_fetch_array, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) - ZEND_ARG_INFO(0, statement) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, row, IS_LONG, 0, "-1") + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, row, IS_LONG, 1, "null") ZEND_END_ARG_INFO() #endif ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_fetch_into, 0, 2, MAY_BE_LONG|MAY_BE_FALSE) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) ZEND_ARG_INFO(1, array) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, row, IS_LONG, 0, "0") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, row, IS_LONG, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_fetch_row, 0, 1, _IS_BOOL, 0) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, row, IS_LONG, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_result, 0, 2, MAY_BE_STRING|MAY_BE_BOOL|MAY_BE_NULL) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) ZEND_ARG_TYPE_MASK(0, field, MAY_BE_STRING|MAY_BE_LONG, NULL) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_result_all, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, format, IS_STRING, 0, "\"\"") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_free_result, 0, 1, _IS_BOOL, 0) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_connect, 0, 0, 3) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_connect, 0, 3, ODBC\\Connection, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dsn, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, user, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, password, IS_STRING, 0) @@ -88,30 +88,30 @@ ZEND_END_ARG_INFO() #define arginfo_odbc_pconnect arginfo_odbc_connect ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_close, 0, 1, IS_VOID, 0) - ZEND_ARG_INFO(0, odbc) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_num_rows, 0, 1, IS_LONG, 0) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) ZEND_END_ARG_INFO() #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_next_result, 0, 1, _IS_BOOL, 0) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) ZEND_END_ARG_INFO() #endif #define arginfo_odbc_num_fields arginfo_odbc_num_rows ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_field_name, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) ZEND_ARG_TYPE_INFO(0, field, IS_LONG, 0) ZEND_END_ARG_INFO() #define arginfo_odbc_field_type arginfo_odbc_field_name ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_field_len, 0, 2, MAY_BE_LONG|MAY_BE_FALSE) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) ZEND_ARG_TYPE_INFO(0, field, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -120,65 +120,65 @@ ZEND_END_ARG_INFO() #define arginfo_odbc_field_scale arginfo_odbc_field_len ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_field_num, 0, 2, MAY_BE_LONG|MAY_BE_FALSE) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, ODBC\\Result, 0) ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_autocommit, 0, 1, MAY_BE_LONG|MAY_BE_BOOL) - ZEND_ARG_INFO(0, odbc) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, enable, _IS_BOOL, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_commit, 0, 1, _IS_BOOL, 0) - ZEND_ARG_INFO(0, odbc) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_END_ARG_INFO() #define arginfo_odbc_rollback arginfo_odbc_commit ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_error, 0, 0, IS_STRING, 0) - ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, odbc, "null") + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, odbc, ODBC\\Connection, 1, "null") ZEND_END_ARG_INFO() #define arginfo_odbc_errormsg arginfo_odbc_error ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_setoption, 0, 4, _IS_BOOL, 0) - ZEND_ARG_INFO(0, odbc) + ZEND_ARG_OBJ_TYPE_MASK(0, odbc, ODBC\\Connection|ODBC\\Result, 0, NULL) ZEND_ARG_TYPE_INFO(0, which, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_tables, 0, 0, 1) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_tables, 0, 1, ODBC\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, catalog, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, schema, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, table, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, types, IS_STRING, 1, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_columns, 0, 0, 1) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_columns, 0, 1, ODBC\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, catalog, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, schema, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, table, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, column, IS_STRING, 1, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_gettypeinfo, 0, 0, 1) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_gettypeinfo, 0, 1, ODBC\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, data_type, IS_LONG, 0, "0") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_primarykeys, 0, 0, 4) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_primarykeys, 0, 4, ODBC\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_ARG_TYPE_INFO(0, catalog, IS_STRING, 1) ZEND_ARG_TYPE_INFO(0, schema, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, table, IS_STRING, 0) ZEND_END_ARG_INFO() #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35) -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_procedurecolumns, 0, 0, 1) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_procedurecolumns, 0, 1, ODBC\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, catalog, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, schema, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, procedure, IS_STRING, 1, "null") @@ -187,8 +187,8 @@ ZEND_END_ARG_INFO() #endif #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35) -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_procedures, 0, 0, 1) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_procedures, 0, 1, ODBC\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, catalog, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, schema, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, procedure, IS_STRING, 1, "null") @@ -196,8 +196,8 @@ ZEND_END_ARG_INFO() #endif #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35) -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_foreignkeys, 0, 0, 7) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_foreignkeys, 0, 7, ODBC\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_ARG_TYPE_INFO(0, pk_catalog, IS_STRING, 1) ZEND_ARG_TYPE_INFO(0, pk_schema, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, pk_table, IS_STRING, 0) @@ -207,8 +207,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_foreignkeys, 0, 0, 7) ZEND_END_ARG_INFO() #endif -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_specialcolumns, 0, 0, 7) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_specialcolumns, 0, 7, ODBC\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, catalog, IS_STRING, 1) ZEND_ARG_TYPE_INFO(0, schema, IS_STRING, 0) @@ -217,8 +217,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_specialcolumns, 0, 0, 7) ZEND_ARG_TYPE_INFO(0, nullable, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_statistics, 0, 0, 6) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_statistics, 0, 6, ODBC\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_ARG_TYPE_INFO(0, catalog, IS_STRING, 1) ZEND_ARG_TYPE_INFO(0, schema, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, table, IS_STRING, 0) @@ -227,8 +227,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_statistics, 0, 0, 6) ZEND_END_ARG_INFO() #if !defined(HAVE_DBMAKER) && !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) &&!defined(HAVE_SOLID_35) -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_tableprivileges, 0, 0, 4) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_tableprivileges, 0, 4, ODBC\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_ARG_TYPE_INFO(0, catalog, IS_STRING, 1) ZEND_ARG_TYPE_INFO(0, schema, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, table, IS_STRING, 0) @@ -236,8 +236,8 @@ ZEND_END_ARG_INFO() #endif #if !defined(HAVE_DBMAKER) && !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) &&!defined(HAVE_SOLID_35) -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_columnprivileges, 0, 0, 5) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_columnprivileges, 0, 5, ODBC\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, ODBC\\Connection, 0) ZEND_ARG_TYPE_INFO(0, catalog, IS_STRING, 1) ZEND_ARG_TYPE_INFO(0, schema, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, table, IS_STRING, 0) @@ -392,6 +392,16 @@ static const zend_function_entry ext_functions[] = { ZEND_FE_END }; + +static const zend_function_entry class_ODBC_Connection_methods[] = { + ZEND_FE_END +}; + + +static const zend_function_entry class_ODBC_Result_methods[] = { + ZEND_FE_END +}; + static void register_odbc_symbols(int module_number) { REGISTER_STRING_CONSTANT("ODBC_TYPE", PHP_ODBC_TYPE, CONST_PERSISTENT); @@ -491,3 +501,25 @@ static void register_odbc_symbols(int module_number) zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "odbc_pconnect", sizeof("odbc_pconnect") - 1), 2, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); } + +static zend_class_entry *register_class_ODBC_Connection(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ODBC", "Connection", class_ODBC_Connection_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE; + + return class_entry; +} + +static zend_class_entry *register_class_ODBC_Result(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ODBC", "Result", class_ODBC_Result_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE; + + return class_entry; +} diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 804e79b063636..9892b63baf2ca 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -29,6 +29,8 @@ #include "ext/standard/info.h" #include "ext/standard/php_string.h" #include "ext/standard/php_standard.h" +#include "Zend/zend_interfaces.h" +#include "zend_smart_str.h" #include "php_odbc.h" #include "php_odbc_includes.h" @@ -49,18 +51,135 @@ #include "odbc_arginfo.h" -/* - * not defined elsewhere - */ +#define CHECK_ODBC_CONNECTION(conn) \ + if (conn == NULL) { \ + zend_throw_error(NULL, "ODBC connection has already been closed"); \ + RETURN_THROWS(); \ + } -#ifndef TRUE -#define TRUE 1 -#define FALSE 0 -#endif +#define CHECK_ODBC_RESULT(result) \ + if (result->conn_ptr == NULL) { \ + zend_throw_error(NULL, "ODBC result has already been closed"); \ + RETURN_THROWS(); \ + } void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent); +static void safe_odbc_disconnect(void *handle); + +static int le_pconn; + +static zend_class_entry *odbc_connection_ce, *odbc_result_ce; +static zend_object_handlers odbc_connection_object_handlers, odbc_result_object_handlers; + +static inline odbc_link *odbc_link_from_obj(zend_object *obj) { + return (odbc_link *)((char *)(obj) - XtOffsetOf(odbc_link, std)); +} + +#define Z_ODBC_LINK_P(zv) odbc_link_from_obj(Z_OBJ_P(zv)) +#define Z_ODBC_CONNECTION_P(zv) Z_ODBC_LINK_P(zv)->connection + +static zend_object *odbc_connection_create_object(zend_class_entry *class_type) { + odbc_link *intern = zend_object_alloc(sizeof(odbc_link), class_type); + + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + intern->std.handlers = &odbc_connection_object_handlers; + + return &intern->std; +} + +static zend_function *odbc_connection_get_constructor(zend_object *object) { + zend_throw_error(NULL, "Cannot directly construct ODBC\\Connection, use odbc_connect() or odbc_pconnect() instead"); + return NULL; +} + +static void odbc_link_free(odbc_link *link) +{ + /* If aborted via timer expiration, don't try to call any unixODBC function */ + if (!(PG(connection_status) & PHP_CONNECTION_TIMEOUT)) { + safe_odbc_disconnect(link->connection->hdbc); + SQLFreeConnect(link->connection->hdbc); + SQLFreeEnv(link->connection->henv); + } + efree(link->connection); + link->connection = NULL; + ODBCG(num_links)--; + if (!link->persistent) { + zend_hash_del(&ODBCG(connections), link->hash); + } + zend_string_release(link->hash); +} + +static void odbc_connection_free_obj(zend_object *obj) +{ + odbc_link *link = odbc_link_from_obj(obj); + + if (link->connection) { + odbc_link_free(link); + } + + zend_object_std_dtor(&link->std); +} + +static inline odbc_result *odbc_result_from_obj(zend_object *obj) { + return (odbc_result *)((char *)(obj) - XtOffsetOf(odbc_result, std)); +} + +#define Z_ODBC_RESULT_P(zv) odbc_result_from_obj(Z_OBJ_P(zv)) -static int le_result, le_conn, le_pconn; +static zend_object *odbc_result_create_object(zend_class_entry *class_type) { + odbc_result *intern = zend_object_alloc(sizeof(odbc_result), class_type); + + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + intern->std.handlers = &odbc_result_object_handlers; + + return &intern->std; +} + +static zend_function *odbc_result_get_constructor(zend_object *object) { + zend_throw_error(NULL, "Cannot directly construct ODBC\\Result, use an appropriate odbc_* function instead"); + return NULL; +} + +static void odbc_result_free(odbc_result *res) +{ + int i; + + if (res->values) { + for(i = 0; i < res->numcols; i++) { + if (res->values[i].value) + efree(res->values[i].value); + } + efree(res->values); + res->values = NULL; + } + /* If aborted via timer expiration, don't try to call any unixODBC function */ + if (res->stmt && !(PG(connection_status) & PHP_CONNECTION_TIMEOUT)) { +#if defined(HAVE_SOLID) || defined(HAVE_SOLID_30) || defined(HAVE_SOLID_35) + SQLTransact(res->conn_ptr->henv, res->conn_ptr->hdbc, + (SQLUSMALLINT) SQL_COMMIT); +#endif + SQLFreeStmt(res->stmt,SQL_DROP); + /* We don't want the connection to be closed after the last statement has been closed + * Connections will be closed on shutdown + * zend_list_delete(res->conn_ptr->id); + */ + } + if (res->param_info) { + efree(res->param_info); + } + efree(res); +} + +static void odbc_result_free_obj(zend_object *obj) +{ + odbc_result *result = odbc_result_from_obj(obj); + + odbc_result_free(result); + + zend_object_std_dtor(&result->std); +} #define SAFE_SQL_NTS(n) ((SQLSMALLINT) ((n)?(SQL_NTS):0)) @@ -93,44 +212,18 @@ ZEND_TSRMLS_CACHE_DEFINE() ZEND_GET_MODULE(odbc) #endif -/* {{{ _free_odbc_result */ -static void _free_odbc_result(zend_resource *rsrc) -{ - odbc_result *res = (odbc_result *)rsrc->ptr; - int i; +static void close_results_with_connection(const odbc_connection *conn) { + zval *p; - if (res) { - if (res->values) { - for(i = 0; i < res->numcols; i++) { - if (res->values[i].value) - efree(res->values[i].value); - } - efree(res->values); - res->values = NULL; - } - /* If aborted via timer expiration, don't try to call any unixODBC function */ - if (res->stmt && !(PG(connection_status) & PHP_CONNECTION_TIMEOUT)) { -#if defined(HAVE_SOLID) || defined(HAVE_SOLID_30) || defined(HAVE_SOLID_35) - SQLTransact(res->conn_ptr->henv, res->conn_ptr->hdbc, - (SQLUSMALLINT) SQL_COMMIT); -#endif - SQLFreeStmt(res->stmt,SQL_DROP); - /* We don't want the connection to be closed after the last statement has been closed - * Connections will be closed on shutdown - * zend_list_delete(res->conn_ptr->id); - */ - } - if (res->param_info) { - efree(res->param_info); + ZEND_HASH_FOREACH_VAL(&ODBCG(results), p) { + odbc_result *result = Z_ODBC_RESULT_P(p); + if (result->conn_ptr == conn) { + odbc_result_free((odbc_result*) p); } - efree(res); - } + } ZEND_HASH_FOREACH_END(); } -/* }}} */ -/* {{{ safe_odbc_disconnect - * disconnect, and if it fails, then issue a rollback for any pending transaction (lurcher) - */ +/* disconnect, and if it fails, then issue a rollback for any pending transaction (lurcher) */ static void safe_odbc_disconnect( void *handle ) { int ret; @@ -142,51 +235,13 @@ static void safe_odbc_disconnect( void *handle ) SQLDisconnect( handle ); } } -/* }}} */ - -/* {{{ _close_odbc_conn */ -static void _close_odbc_conn(zend_resource *rsrc) -{ - zend_resource *p; - odbc_result *res; - - odbc_connection *conn = (odbc_connection *)rsrc->ptr; - - ZEND_HASH_FOREACH_PTR(&EG(regular_list), p) { - if (p->ptr && (p->type == le_result)) { - res = (odbc_result *)p->ptr; - if (res->conn_ptr == conn) { - zend_list_close(p); - } - } - } ZEND_HASH_FOREACH_END(); - - /* If aborted via timer expiration, don't try to call any unixODBC function */ - if (!(PG(connection_status) & PHP_CONNECTION_TIMEOUT)) { - safe_odbc_disconnect(conn->hdbc); - SQLFreeConnect(conn->hdbc); - SQLFreeEnv(conn->henv); - } - efree(conn); - ODBCG(num_links)--; -} -/* }}} */ /* {{{ void _close_odbc_pconn */ static void _close_odbc_pconn(zend_resource *rsrc) { - zend_resource *p; - odbc_result *res; odbc_connection *conn = (odbc_connection *)rsrc->ptr; - ZEND_HASH_FOREACH_PTR(&EG(regular_list), p) { - if (p->ptr && (p->type == le_result)) { - res = (odbc_result *)p->ptr; - if (res->conn_ptr == conn) { - zend_list_close(p); - } - } - } ZEND_HASH_FOREACH_END(); + close_results_with_connection(conn); /* If aborted via timer expiration, don't try to call any unixODBC function */ if (!(PG(connection_status) & PHP_CONNECTION_TIMEOUT)) { @@ -198,6 +253,7 @@ static void _close_odbc_pconn(zend_resource *rsrc) ODBCG(num_links)--; ODBCG(num_persistent)--; + rsrc->ptr = NULL; } /* }}} */ @@ -378,6 +434,8 @@ static PHP_GINIT_FUNCTION(odbc) ZEND_TSRMLS_CACHE_UPDATE(); #endif odbc_globals->num_persistent = 0; + zend_hash_init(&odbc_globals->results, 0, NULL, NULL, 1); + zend_hash_init(&odbc_globals->connections, 0, NULL, NULL, 1); } /* {{{ PHP_MINIT_FUNCTION */ @@ -389,11 +447,29 @@ PHP_MINIT_FUNCTION(odbc) #endif REGISTER_INI_ENTRIES(); - le_result = zend_register_list_destructors_ex(_free_odbc_result, NULL, "odbc result", module_number); - le_conn = zend_register_list_destructors_ex(_close_odbc_conn, NULL, "odbc link", module_number); le_pconn = zend_register_list_destructors_ex(NULL, _close_odbc_pconn, "odbc link persistent", module_number); odbc_module_entry.type = type; + odbc_connection_ce = register_class_ODBC_Connection(); + odbc_connection_ce->create_object = odbc_connection_create_object; + + memcpy(&odbc_connection_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + odbc_connection_object_handlers.offset = XtOffsetOf(odbc_link, std); + odbc_connection_object_handlers.free_obj = odbc_connection_free_obj; + odbc_connection_object_handlers.get_constructor = odbc_connection_get_constructor; + odbc_connection_object_handlers.clone_obj = NULL; + odbc_connection_object_handlers.compare = zend_objects_not_comparable; + + odbc_result_ce = register_class_ODBC_Result(); + odbc_result_ce->create_object = odbc_result_create_object; + + memcpy(&odbc_result_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + odbc_result_object_handlers.offset = XtOffsetOf(odbc_result, std); + odbc_result_object_handlers.free_obj = odbc_result_free_obj; + odbc_result_object_handlers.get_constructor = odbc_result_get_constructor; + odbc_result_object_handlers.clone_obj = NULL; + odbc_result_object_handlers.compare = zend_objects_not_comparable; + #if defined(HAVE_IBMDB2) && defined(_AIX) /* atexit() handler in the DB2/AIX library segfaults in PHP CLI */ /* DB2NOEXITLIST env variable prevents DB2 from invoking atexit() */ @@ -427,6 +503,9 @@ PHP_RSHUTDOWN_FUNCTION(odbc) /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(odbc) { + zend_hash_destroy(&ODBCG(results)); + zend_hash_destroy(&ODBCG(connections)); + UNREGISTER_INI_ENTRIES(); return SUCCESS; } @@ -510,13 +589,12 @@ void php_odbc_fetch_attribs(INTERNAL_FUNCTION_PARAMETERS, int mode) zval *pv_res; zend_long flag; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &pv_res, &flag) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &flag) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (mode) { result->longreadlen = flag; @@ -656,13 +734,12 @@ void odbc_transact(INTERNAL_FUNCTION_PARAMETERS, int type) RETCODE rc; zval *pv_conn; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_conn) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_conn, odbc_connection_ce) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); rc = SQLTransact(conn->henv, conn->hdbc, (SQLUSMALLINT)((type)?SQL_COMMIT:SQL_ROLLBACK)); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { @@ -674,17 +751,6 @@ void odbc_transact(INTERNAL_FUNCTION_PARAMETERS, int type) } /* }}} */ -/* {{{ _close_pconn_with_res */ -static int _close_pconn_with_res(zend_resource *le, zend_resource *res) -{ - if (le->type == le_pconn && (((odbc_connection *)(le->ptr))->res == res)){ - return 1; - }else{ - return 0; - } -} -/* }}} */ - /* {{{ odbc_column_lengths */ void odbc_column_lengths(INTERNAL_FUNCTION_PARAMETERS, int type) { @@ -703,13 +769,12 @@ void odbc_column_lengths(INTERNAL_FUNCTION_PARAMETERS, int type) zval *pv_res; zend_long pv_num; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &pv_res, &pv_num) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &pv_num) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (pv_num < 1) { zend_argument_value_error(2, "must be greater than 0"); @@ -737,30 +802,26 @@ void odbc_column_lengths(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ Close all ODBC connections */ PHP_FUNCTION(odbc_close_all) { - zend_resource *p; + zval *p; if (zend_parse_parameters_none() == FAILURE) { RETURN_THROWS(); } /* Loop through list and close all statements */ - ZEND_HASH_FOREACH_PTR(&EG(regular_list), p) { - if (p->ptr && (p->type == le_result)) { - zend_list_close(p); - } + ZEND_HASH_FOREACH_VAL(&ODBCG(results), p) { + odbc_result_free((odbc_result*) p); } ZEND_HASH_FOREACH_END(); - /* Second loop through list, now close all connections */ - ZEND_HASH_FOREACH_PTR(&EG(regular_list), p) { - if (p->ptr) { - if (p->type == le_conn){ - zend_list_close(p); - } else if (p->type == le_pconn){ - zend_list_close(p); - /* Delete the persistent connection */ - zend_hash_apply_with_argument(&EG(persistent_list), - (apply_func_arg_t) _close_pconn_with_res, (void *)p); - } + /* Second loop through list, now close all non-persistent connections */ + ZEND_HASH_FOREACH_VAL(&ODBCG(connections), p) { + odbc_link_free((odbc_link*) p); + } ZEND_HASH_FOREACH_END(); + + /* Third loop through persistent list, now close all persistent connections */ + ZEND_HASH_FOREACH_VAL(&EG(persistent_list), p) { + if (Z_RES_P(p)->type == le_pconn) { + zend_list_close(Z_RES_P(p)); } } ZEND_HASH_FOREACH_END(); } @@ -794,29 +855,29 @@ PHP_FUNCTION(odbc_prepare) SQLUINTEGER scrollopts; #endif - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pv_conn, &query, &query_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pv_conn, odbc_connection_ce, &query, &query_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); result->numparams = 0; result->param_info = NULL; rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -853,7 +914,7 @@ PHP_FUNCTION(odbc_prepare) if (result->numcols > 0) { if (!odbc_bindcols(result)) { - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } } else { @@ -871,12 +932,10 @@ PHP_FUNCTION(odbc_prepare) odbc_sql_error(result->conn_ptr, result->stmt, "SQLDescribeParameter"); SQLFreeStmt(result->stmt, SQL_RESET_PARAMS); efree(result->param_info); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } } - - RETURN_RES(zend_register_resource(result, le_result)); } /* }}} */ @@ -915,13 +974,12 @@ PHP_FUNCTION(odbc_execute) int i, ne; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|h", &pv_res, &pv_param_ht) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|h", &pv_res, odbc_result_ce, &pv_param_ht) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (result->numparams > 0) { if ((ne = zend_hash_num_elements(pv_param_ht)) < result->numparams) { @@ -1075,13 +1133,12 @@ PHP_FUNCTION(odbc_cursor) odbc_result *result; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_res) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); rc = SQLGetInfo(result->conn_ptr->hdbc,SQL_MAX_CURSOR_NAME_LEN, (void *)&max_len,sizeof(max_len),&len); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { @@ -1133,7 +1190,7 @@ PHP_FUNCTION(odbc_data_source) UCHAR server_name[100], desc[200]; SQLSMALLINT len1=0, len2=0, fetch_type; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &zv_conn, &zv_fetch_type) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &zv_conn, odbc_connection_ce, &zv_fetch_type) == FAILURE) { RETURN_THROWS(); } @@ -1144,9 +1201,8 @@ PHP_FUNCTION(odbc_data_source) RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(zv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(zv_conn); + CHECK_ODBC_CONNECTION(conn); /* now we have the "connection" lets call the DataSource object */ rc = SQLDataSources(conn->henv, @@ -1196,26 +1252,26 @@ PHP_FUNCTION(odbc_exec) SQLUINTEGER scrollopts; #endif - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pv_conn, &query, &query_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pv_conn, odbc_connection_ce, &query, &query_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -1242,7 +1298,7 @@ PHP_FUNCTION(odbc_exec) */ odbc_sql_error(conn, result->stmt, "SQLExecDirect"); SQLFreeStmt(result->stmt, SQL_DROP); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -1251,7 +1307,7 @@ PHP_FUNCTION(odbc_exec) /* For insert, update etc. cols == 0 */ if (result->numcols > 0) { if (!odbc_bindcols(result)) { - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } } else { @@ -1260,7 +1316,6 @@ PHP_FUNCTION(odbc_exec) Z_ADDREF_P(pv_conn); result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); } /* }}} */ @@ -1276,30 +1331,21 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) RETCODE rc; SQLSMALLINT sql_c_type; char *buf = NULL; + zval *pv_res, tmp; + zend_long pv_row = -1; + bool pv_row_is_null = true; #ifdef HAVE_SQL_EXTENDED_FETCH SQLULEN crow; SQLUSMALLINT RowStatus[1]; - SQLLEN rownum; - zval *pv_res, tmp; - zend_long pv_row = -1; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &pv_res, &pv_row) == FAILURE) { - RETURN_THROWS(); - } - - rownum = pv_row; -#else - zval *pv_res, tmp; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_res) == FAILURE) { - RETURN_THROWS(); - } #endif - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l!", &pv_res, odbc_result_ce, &pv_row, &pv_row_is_null) == FAILURE) { RETURN_THROWS(); } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); + if (result->numcols == 0) { php_error_docref(NULL, E_WARNING, "No tuples available at this result index"); RETURN_FALSE; @@ -1307,8 +1353,8 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) #ifdef HAVE_SQL_EXTENDED_FETCH if (result->fetch_abs) { - if (rownum > 0) { - rc = SQLExtendedFetch(result->stmt,SQL_FETCH_ABSOLUTE,rownum,&crow,RowStatus); + if (!pv_row_is_null) { + rc = SQLExtendedFetch(result->stmt,SQL_FETCH_ABSOLUTE,(SQLLEN) pv_row,&crow,RowStatus); } else { rc = SQLExtendedFetch(result->stmt,SQL_FETCH_NEXT,1,&crow,RowStatus); } @@ -1322,12 +1368,11 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) array_init(return_value); -#ifdef HAVE_SQL_EXTENDED_FETCH - if (rownum > 0 && result->fetch_abs) - result->fetched = rownum; - else -#endif + if (!pv_row_is_null && result->fetch_abs) { + result->fetched = (SQLLEN) pv_row; + } else { result->fetched++; + } for(i = 0; i < result->numcols; i++) { sql_c_type = SQL_C_CHAR; @@ -1437,28 +1482,19 @@ PHP_FUNCTION(odbc_fetch_into) SQLSMALLINT sql_c_type; char *buf = NULL; zval *pv_res, *pv_res_arr, tmp; -#ifdef HAVE_SQL_EXTENDED_FETCH zend_long pv_row = 0; + bool pv_row_is_null = true; +#ifdef HAVE_SQL_EXTENDED_FETCH SQLULEN crow; SQLUSMALLINT RowStatus[1]; - SQLLEN rownum = -1; -#endif /* HAVE_SQL_EXTENDED_FETCH */ - -#ifdef HAVE_SQL_EXTENDED_FETCH - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz|l", &pv_res, &pv_res_arr, &pv_row) == FAILURE) { - RETURN_THROWS(); - } +#endif - rownum = pv_row; -#else - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz", &pv_res, &pv_res_arr) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz|l!", &pv_res, odbc_result_ce, &pv_res_arr, &pv_row, &pv_row_is_null) == FAILURE) { RETURN_THROWS(); } -#endif /* HAVE_SQL_EXTENDED_FETCH */ - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (result->numcols == 0) { php_error_docref(NULL, E_WARNING, "No tuples available at this result index"); @@ -1472,8 +1508,8 @@ PHP_FUNCTION(odbc_fetch_into) #ifdef HAVE_SQL_EXTENDED_FETCH if (result->fetch_abs) { - if (rownum > 0) { - rc = SQLExtendedFetch(result->stmt,SQL_FETCH_ABSOLUTE,rownum,&crow,RowStatus); + if (!pv_row_is_null) { + rc = SQLExtendedFetch(result->stmt,SQL_FETCH_ABSOLUTE,(SQLLEN) pv_row,&crow,RowStatus); } else { rc = SQLExtendedFetch(result->stmt,SQL_FETCH_NEXT,1,&crow,RowStatus); } @@ -1485,12 +1521,11 @@ PHP_FUNCTION(odbc_fetch_into) RETURN_FALSE; } -#ifdef HAVE_SQL_EXTENDED_FETCH - if (rownum > 0 && result->fetch_abs) - result->fetched = rownum; - else -#endif + if (!pv_row_is_null && result->fetch_abs) { + result->fetched = (SQLLEN) pv_row; + } else { result->fetched++; + } for(i = 0; i < result->numcols; i++) { sql_c_type = SQL_C_CHAR; @@ -1569,13 +1604,13 @@ PHP_FUNCTION(solid_fetch_prev) RETCODE rc; zval *pv_res; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_res) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res; + CHECK_ODBC_RESULT(result); + if (result->numcols == 0) { php_error_docref(NULL, E_WARNING, "No tuples available at this result index"); RETURN_FALSE; @@ -1601,20 +1636,19 @@ PHP_FUNCTION(odbc_fetch_row) odbc_result *result; RETCODE rc; zval *pv_res; - zend_long pv_row; - bool pv_row_is_null = 1; + zend_long pv_row = 0; + bool pv_row_is_null = true; #ifdef HAVE_SQL_EXTENDED_FETCH SQLULEN crow; SQLUSMALLINT RowStatus[1]; #endif - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l!", &pv_res, &pv_row, &pv_row_is_null) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l!", &pv_res, odbc_result_ce, &pv_row, &pv_row_is_null) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (result->numcols == 0) { php_error_docref(NULL, E_WARNING, "No tuples available at this result index"); @@ -1665,7 +1699,7 @@ PHP_FUNCTION(odbc_result) #endif ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_RESOURCE(pv_res) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) Z_PARAM_STR_OR_LONG(pv_field_str, pv_field_long) ZEND_PARSE_PARAMETERS_END(); @@ -1677,9 +1711,8 @@ PHP_FUNCTION(odbc_result) field_ind = (int) pv_field_long - 1; } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (result->numcols == 0) { php_error_docref(NULL, E_WARNING, "No tuples available at this result index"); @@ -1873,13 +1906,12 @@ PHP_FUNCTION(odbc_result_all) SQLUSMALLINT RowStatus[1]; #endif - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|s", &pv_res, &pv_format, &pv_format_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s", &pv_res, odbc_result_ce, &pv_format, &pv_format_len) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (result->numcols == 0) { php_error_docref(NULL, E_WARNING, "No tuples available at this result index"); @@ -2007,13 +2039,12 @@ PHP_FUNCTION(odbc_free_result) odbc_result *result; int i; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_res) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (result->values) { for (i = 0; i < result->numcols; i++) { @@ -2046,18 +2077,23 @@ PHP_FUNCTION(odbc_pconnect) /* }}} */ /* {{{ odbc_sqlconnect */ -int odbc_sqlconnect(odbc_connection **conn, char *db, char *uid, char *pwd, int cur_opt, int persistent) +bool odbc_sqlconnect(zval *zv, char *db, char *uid, char *pwd, int cur_opt, int persistent, zend_string *hash) { RETCODE rc; + odbc_link *link; - *conn = (odbc_connection *)pemalloc(sizeof(odbc_connection), persistent); - memset(*conn, 0, sizeof(odbc_connection)); - (*conn)->persistent = persistent; - SQLAllocEnv(&((*conn)->henv)); - SQLAllocConnect((*conn)->henv, &((*conn)->hdbc)); + object_init_ex(zv, odbc_connection_ce); + link = Z_ODBC_LINK_P(zv); + link->connection = (odbc_connection *)pemalloc(sizeof(odbc_connection), persistent); + memset(link->connection, 0, sizeof(odbc_connection)); + + link->persistent = persistent; + link->hash = hash; + SQLAllocEnv(link->connection->henv); + SQLAllocConnect(link->connection->henv, link->connection->hdbc); #if defined(HAVE_SOLID) || defined(HAVE_SOLID_30) - SQLSetConnectOption((*conn)->hdbc, SQL_TRANSLATE_OPTION, + SQLSetConnectOption((link->connection->hdbc, SQL_TRANSLATE_OPTION, SQL_SOLID_XLATOPT_NOCNV); #endif #ifdef HAVE_OPENLINK @@ -2065,16 +2101,15 @@ int odbc_sqlconnect(odbc_connection **conn, char *db, char *uid, char *pwd, int char dsnbuf[1024]; short dsnbuflen; - rc = SQLDriverConnect((*conn)->hdbc, NULL, db, SQL_NTS, dsnbuf, sizeof(dsnbuf) - 1, &dsnbuflen, SQL_DRIVER_NOPROMPT); + rc = SQLDriverConnect(link->connection->hdbc, NULL, db, SQL_NTS, dsnbuf, sizeof(dsnbuf) - 1, &dsnbuflen, SQL_DRIVER_NOPROMPT); } #else if (cur_opt != SQL_CUR_DEFAULT) { - rc = SQLSetConnectOption((*conn)->hdbc, SQL_ODBC_CURSORS, cur_opt); + rc = SQLSetConnectOption(link->connection->hdbc, SQL_ODBC_CURSORS, cur_opt); if (rc != SQL_SUCCESS) { /* && rc != SQL_SUCCESS_WITH_INFO ? */ - odbc_sql_error(*conn, SQL_NULL_HSTMT, "SQLSetConnectOption"); - SQLFreeConnect((*conn)->hdbc); - pefree(*conn, persistent); - return FALSE; + odbc_sql_error(link->connection, SQL_NULL_HSTMT, "SQLSetConnectOption"); + SQLFreeConnect(link->connection->hdbc); + return false; } } /* Possible fix for bug #10250 @@ -2132,9 +2167,9 @@ int odbc_sqlconnect(odbc_connection **conn, char *db, char *uid, char *pwd, int } if (direct) { - rc = SQLDriverConnect((*conn)->hdbc, NULL, (SQLCHAR *) ldb, strlen(ldb), dsnbuf, sizeof(dsnbuf) - 1, &dsnbuflen, SQL_DRIVER_NOPROMPT); + rc = SQLDriverConnect(link->connection->hdbc, NULL, (SQLCHAR *) ldb, strlen(ldb), dsnbuf, sizeof(dsnbuf) - 1, &dsnbuflen, SQL_DRIVER_NOPROMPT); } else { - rc = SQLConnect((*conn)->hdbc, (SQLCHAR *) db, SQL_NTS, (SQLCHAR *) uid, SQL_NTS, (SQLCHAR *) pwd, SQL_NTS); + rc = SQLConnect(link->connection->hdbc, (SQLCHAR *) db, SQL_NTS, (SQLCHAR *) uid, SQL_NTS, (SQLCHAR *) pwd, SQL_NTS); } if (ldb) { @@ -2142,17 +2177,15 @@ int odbc_sqlconnect(odbc_connection **conn, char *db, char *uid, char *pwd, int } } #else - rc = SQLConnect((*conn)->hdbc, (SQLCHAR *) db, SQL_NTS, uid, SQL_NTS, pwd, SQL_NTS); + rc = SQLConnect(link->connection->hdbc, (SQLCHAR *) db, SQL_NTS, uid, SQL_NTS, pwd, SQL_NTS); #endif #endif if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - odbc_sql_error(*conn, SQL_NULL_HSTMT, "SQLConnect"); - SQLFreeConnect((*conn)->hdbc); - pefree((*conn), persistent); - return FALSE; + odbc_sql_error(link->connection, SQL_NULL_HSTMT, "SQLConnect"); + SQLFreeConnect(link->connection->hdbc); + return false; } -/* (*conn)->open = 1;*/ - return TRUE; + return true; } /* }}} */ @@ -2195,42 +2228,43 @@ void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) persistent = 0; } + smart_str hashed_details = {0}; + smart_str_append_printf(&hashed_details, "%s_%s_%s_%s_%d", ODBC_TYPE, db, uid, pwd, cur_opt); + try_and_get_another_connection: if (persistent) { - char *hashed_details; - int hashed_len; zend_resource *le; - hashed_len = spprintf(&hashed_details, 0, "%s_%s_%s_%s_%d", ODBC_TYPE, db, uid, pwd, cur_opt); - /* the link is not in the persistent list */ - if ((le = zend_hash_str_find_ptr(&EG(persistent_list), hashed_details, hashed_len)) == NULL) { + if ((le = zend_hash_find_ptr(&EG(persistent_list), hashed_details.s)) == NULL) { if (ODBCG(max_links) != -1 && ODBCG(num_links) >= ODBCG(max_links)) { - php_error_docref(NULL, E_WARNING, "Too many open links (%ld)", ODBCG(num_links)); - efree(hashed_details); + php_error_docref(NULL, E_WARNING, "Too many open links (" ZEND_LONG_FMT ")", ODBCG(num_links)); + smart_str_free(&hashed_details); RETURN_FALSE; } if (ODBCG(max_persistent) != -1 && ODBCG(num_persistent) >= ODBCG(max_persistent)) { - php_error_docref(NULL, E_WARNING,"Too many open persistent links (%ld)", ODBCG(num_persistent)); - efree(hashed_details); + php_error_docref(NULL, E_WARNING,"Too many open persistent links (" ZEND_LONG_FMT ")", ODBCG(num_persistent)); + smart_str_free(&hashed_details); RETURN_FALSE; } - if (!odbc_sqlconnect(&db_conn, db, uid, pwd, cur_opt, 1)) { - efree(hashed_details); + if (!odbc_sqlconnect(return_value, db, uid, pwd, cur_opt, 1, hashed_details.s)) { + smart_str_free(&hashed_details); + zval_ptr_dtor(return_value); RETURN_FALSE; } - if (zend_register_persistent_resource(hashed_details, hashed_len, db_conn, le_pconn) == NULL) { - free(db_conn); - efree(hashed_details); + db_conn = Z_ODBC_CONNECTION_P(return_value); + + if (zend_register_persistent_resource(ZSTR_VAL(hashed_details.s), ZSTR_LEN(hashed_details.s), db_conn, le_pconn) == NULL) { + smart_str_free(&hashed_details); + zval_ptr_dtor(return_value); RETURN_FALSE; } + ODBCG(num_persistent)++; ODBCG(num_links)++; - db_conn->res = zend_register_resource(db_conn, le_pconn); - RETVAL_RES(db_conn->res); } else { /* found connection */ ZEND_ASSERT(le->type == le_pconn); @@ -2253,7 +2287,7 @@ void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) &dead, 0, NULL); if (ret == SQL_SUCCESS && dead == SQL_CD_TRUE) { /* Bail early here, since we know it's gone */ - zend_hash_str_del(&EG(persistent_list), hashed_details, hashed_len); + zend_hash_del(&EG(persistent_list), hashed_details.s); goto try_and_get_another_connection; } /* If the driver doesn't support it, or returns @@ -2265,7 +2299,7 @@ void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) d_name, sizeof(d_name), &len); if(ret != SQL_SUCCESS || len == 0) { - zend_hash_str_del(&EG(persistent_list), hashed_details, hashed_len); + zend_hash_del(&EG(persistent_list), hashed_details.s); /* Commented out to fix a possible double closure error * when working with persistent connections as submitted by * bug #15758 @@ -2276,23 +2310,29 @@ void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) goto try_and_get_another_connection; } } + + object_init_ex(return_value, odbc_connection_ce); + odbc_link *link = Z_ODBC_LINK_P(return_value); + link->connection = db_conn; + link->hash = zend_string_copy(hashed_details.s); + link->persistent = 1; } - efree(hashed_details); - db_conn->res = zend_register_resource(db_conn, le_pconn); - RETVAL_RES(db_conn->res); } else { /* non persistent */ if (ODBCG(max_links) != -1 && ODBCG(num_links) >= ODBCG(max_links)) { - php_error_docref(NULL, E_WARNING,"Too many open connections (%ld)",ODBCG(num_links)); + php_error_docref(NULL, E_WARNING,"Too many open connections (" ZEND_LONG_FMT ")", ODBCG(num_links)); + smart_str_free(&hashed_details); RETURN_FALSE; } - if (!odbc_sqlconnect(&db_conn, db, uid, pwd, cur_opt, 0)) { + if (!odbc_sqlconnect(return_value, db, uid, pwd, cur_opt, 0, hashed_details.s)) { + smart_str_free(&hashed_details); RETURN_FALSE; } - db_conn->res = zend_register_resource(db_conn, le_conn); - RETVAL_RES(db_conn->res); ODBCG(num_links)++; } + + zend_hash_update(&ODBCG(connections), hashed_details.s, return_value); + smart_str_free(&hashed_details); } /* }}} */ @@ -2300,37 +2340,22 @@ void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) PHP_FUNCTION(odbc_close) { zval *pv_conn; - zend_resource *p; - odbc_connection *conn; - odbc_result *res; - int is_pconn = 0; + odbc_link *link; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_conn) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_conn, odbc_connection_ce) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + link = Z_ODBC_LINK_P(pv_conn); + CHECK_ODBC_CONNECTION(link->connection); - if (Z_RES_P(pv_conn)->type == le_pconn) { - is_pconn = 1; + if (link->persistent) { + zend_hash_del(&EG(persistent_list), link->hash); } - ZEND_HASH_FOREACH_PTR(&EG(regular_list), p) { - if (p->ptr && (p->type == le_result)) { - res = (odbc_result *)p->ptr; - if (res->conn_ptr == conn) { - zend_list_close(p); - } - } - } ZEND_HASH_FOREACH_END(); - - zend_list_close(Z_RES_P(pv_conn)); + close_results_with_connection(link->connection); - if(is_pconn){ - zend_hash_apply_with_argument(&EG(persistent_list), (apply_func_arg_t) _close_pconn_with_res, (void *) Z_RES_P(pv_conn)); - } + odbc_link_free(link); } /* }}} */ @@ -2341,13 +2366,12 @@ PHP_FUNCTION(odbc_num_rows) SQLLEN rows; zval *pv_res; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_res) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); SQLRowCount(result->stmt, &rows); RETURN_LONG(rows); @@ -2362,13 +2386,12 @@ PHP_FUNCTION(odbc_next_result) zval *pv_res; int rc, i; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_res) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (result->values) { for(i = 0; i < result->numcols; i++) { @@ -2413,13 +2436,12 @@ PHP_FUNCTION(odbc_num_fields) odbc_result *result; zval *pv_res; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_res) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); RETURN_LONG(result->numcols); } @@ -2432,13 +2454,12 @@ PHP_FUNCTION(odbc_field_name) zval *pv_res; zend_long pv_num; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &pv_res, &pv_num) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &pv_num) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (pv_num < 1) { zend_argument_value_error(2, "must be greater than 0"); @@ -2468,13 +2489,12 @@ PHP_FUNCTION(odbc_field_type) zval *pv_res; zend_long pv_num; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &pv_res, &pv_num) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &pv_num) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (pv_num < 1) { zend_argument_value_error(2, "must be greater than 0"); @@ -2518,13 +2538,12 @@ PHP_FUNCTION(odbc_field_num) odbc_result *result; zval *pv_res; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pv_res, &fname, &fname_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pv_res, odbc_result_ce, &fname, &fname_len) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (result->numcols == 0) { php_error_docref(NULL, E_WARNING, "No tuples available at this result index"); @@ -2555,13 +2574,12 @@ PHP_FUNCTION(odbc_autocommit) bool pv_onoff = 0; bool pv_onoff_is_null = true; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|b!", &pv_conn, &pv_onoff, &pv_onoff_is_null) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b!", &pv_conn, odbc_connection_ce, &pv_onoff, &pv_onoff_is_null) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); if (!pv_onoff_is_null) { rc = SQLSetConnectOption(conn->hdbc, SQL_AUTOCOMMIT, pv_onoff ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF); @@ -2604,14 +2622,14 @@ static void php_odbc_lasterror(INTERNAL_FUNCTION_PARAMETERS, int mode) zval *pv_handle = NULL; char *ret; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|r!", &pv_handle) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &pv_handle, odbc_connection_ce) == FAILURE) { RETURN_THROWS(); } if (pv_handle) { - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_handle), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_handle); + CHECK_ODBC_CONNECTION(conn); + if (mode == 0) { ret = conn->laststate; } else { @@ -2652,36 +2670,42 @@ PHP_FUNCTION(odbc_errormsg) */ PHP_FUNCTION(odbc_setoption) { - odbc_connection *conn; + odbc_link *link; odbc_result *result; RETCODE rc; zval *pv_handle; zend_long pv_which, pv_opt, pv_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlll", &pv_handle, &pv_which, &pv_opt, &pv_val) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "olll", &pv_handle, &pv_which, &pv_opt, &pv_val) == FAILURE) { RETURN_THROWS(); } switch (pv_which) { case 1: /* SQLSetConnectOption */ - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_handle), "ODBC-Link", le_conn, le_pconn))) { + if (!instanceof_function(Z_OBJCE_P(pv_handle), odbc_connection_ce)) { + zend_argument_type_error(1, "must be of type ODBC\\Connection for SQLSetConnectOption()"); RETURN_THROWS(); } + link = Z_ODBC_LINK_P(pv_handle); + CHECK_ODBC_CONNECTION(link->connection); - if (conn->persistent) { + if (link->persistent) { php_error_docref(NULL, E_WARNING, "Unable to set option for persistent connection"); RETURN_FALSE; } - rc = SQLSetConnectOption(conn->hdbc, (unsigned short) pv_opt, pv_val); + rc = SQLSetConnectOption(link->connection->hdbc, (unsigned short) pv_opt, pv_val); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - odbc_sql_error(conn, SQL_NULL_HSTMT, "SetConnectOption"); + odbc_sql_error(link->connection, SQL_NULL_HSTMT, "SetConnectOption"); RETURN_FALSE; } break; case 2: /* SQLSetStmtOption */ - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_handle), "ODBC result", le_result)) == NULL) { + if (!instanceof_function(Z_OBJCE_P(pv_handle), odbc_result_ce)) { + zend_argument_type_error(1, "must be of type ODBC\\Result for SQLSetStmtOption()"); RETURN_THROWS(); } + result = Z_ODBC_RESULT_P(pv_handle); + CHECK_ODBC_RESULT(result); rc = SQLSetStmtOption(result->stmt, (unsigned short) pv_opt, pv_val); @@ -2713,27 +2737,27 @@ PHP_FUNCTION(odbc_tables) size_t cat_len = 0, schema_len = 0, table_len = 0, type_len = 0; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|s!s!s!s!", &pv_conn, &cat, &cat_len, &schema, &schema_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &table, &table_len, &type, &type_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -2750,7 +2774,7 @@ PHP_FUNCTION(odbc_tables) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLTables"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -2759,7 +2783,7 @@ PHP_FUNCTION(odbc_tables) if (result->numcols > 0) { if (!odbc_bindcols(result)) { - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } } else { @@ -2767,7 +2791,6 @@ PHP_FUNCTION(odbc_tables) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); } /* }}} */ @@ -2781,27 +2804,27 @@ PHP_FUNCTION(odbc_columns) size_t cat_len = 0, schema_len = 0, table_len = 0, column_len = 0; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|s!s!s!s!", &pv_conn, &cat, &cat_len, &schema, &schema_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &table, &table_len, &column, &column_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -2820,7 +2843,7 @@ PHP_FUNCTION(odbc_columns) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLColumns"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -2829,7 +2852,7 @@ PHP_FUNCTION(odbc_columns) if (result->numcols > 0) { if (!odbc_bindcols(result)) { - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } } else { @@ -2837,7 +2860,6 @@ PHP_FUNCTION(odbc_columns) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); } /* }}} */ @@ -2852,27 +2874,27 @@ PHP_FUNCTION(odbc_columnprivileges) size_t cat_len = 0, schema_len, table_len, column_len; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs!sss", &pv_conn, &cat, &cat_len, &schema, &schema_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!sss", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &table, &table_len, &column, &column_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -2884,7 +2906,7 @@ PHP_FUNCTION(odbc_columnprivileges) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLColumnPrivileges"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -2893,7 +2915,7 @@ PHP_FUNCTION(odbc_columnprivileges) if (result->numcols > 0) { if (!odbc_bindcols(result)) { - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } } else { @@ -2901,7 +2923,6 @@ PHP_FUNCTION(odbc_columnprivileges) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); } /* }}} */ #endif /* HAVE_DBMAKER || HAVE_SOLID*/ @@ -2917,7 +2938,7 @@ PHP_FUNCTION(odbc_foreignkeys) size_t pcat_len = 0, pschema_len, ptable_len, fcat_len, fschema_len, ftable_len; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs!sssss", &pv_conn, &pcat, &pcat_len, &pschema, &pschema_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!sssss", &pv_conn, odbc_connection_ce, &pcat, &pcat_len, &pschema, &pschema_len, &ptable, &ptable_len, &fcat, &fcat_len, &fschema, &fschema_len, &ftable, &ftable_len) == FAILURE) { RETURN_THROWS(); } @@ -2934,22 +2955,22 @@ PHP_FUNCTION(odbc_foreignkeys) EMPTY_TO_NULL(ftable); #endif - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -2972,7 +2993,7 @@ PHP_FUNCTION(odbc_foreignkeys) if (result->numcols > 0) { if (!odbc_bindcols(result)) { - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } } else { @@ -2980,7 +3001,6 @@ PHP_FUNCTION(odbc_foreignkeys) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); } /* }}} */ #endif /* HAVE_SOLID */ @@ -2995,28 +3015,28 @@ PHP_FUNCTION(odbc_gettypeinfo) RETCODE rc; SQLSMALLINT data_type; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &pv_conn, &pv_data_type) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &pv_conn, odbc_connection_ce, &pv_data_type) == FAILURE) { RETURN_THROWS(); } data_type = (SQLSMALLINT) pv_data_type; - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3024,7 +3044,7 @@ PHP_FUNCTION(odbc_gettypeinfo) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLGetTypeInfo"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3033,7 +3053,7 @@ PHP_FUNCTION(odbc_gettypeinfo) if (result->numcols > 0) { if (!odbc_bindcols(result)) { - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } } else { @@ -3041,7 +3061,6 @@ PHP_FUNCTION(odbc_gettypeinfo) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); } /* }}} */ @@ -3055,26 +3074,26 @@ PHP_FUNCTION(odbc_primarykeys) size_t cat_len = 0, schema_len, table_len; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs!ss", &pv_conn, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!ss", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3085,7 +3104,7 @@ PHP_FUNCTION(odbc_primarykeys) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLPrimaryKeys"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3094,7 +3113,7 @@ PHP_FUNCTION(odbc_primarykeys) if (result->numcols > 0) { if (!odbc_bindcols(result)) { - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } } else { @@ -3102,7 +3121,6 @@ PHP_FUNCTION(odbc_primarykeys) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); } /* }}} */ @@ -3117,27 +3135,27 @@ PHP_FUNCTION(odbc_procedurecolumns) size_t cat_len = 0, schema_len = 0, proc_len = 0, col_len = 0; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|s!s!s!s!", &pv_conn, &cat, &cat_len, &schema, &schema_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &proc, &proc_len, &col, &col_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3149,7 +3167,7 @@ PHP_FUNCTION(odbc_procedurecolumns) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLProcedureColumns"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3158,7 +3176,7 @@ PHP_FUNCTION(odbc_procedurecolumns) if (result->numcols > 0) { if (!odbc_bindcols(result)) { - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } } else { @@ -3166,7 +3184,6 @@ PHP_FUNCTION(odbc_procedurecolumns) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); } /* }}} */ #endif /* HAVE_SOLID */ @@ -3182,26 +3199,26 @@ PHP_FUNCTION(odbc_procedures) size_t cat_len = 0, schema_len = 0, proc_len = 0; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|s!s!s!", &pv_conn, &cat, &cat_len, &schema, &schema_len, &proc, &proc_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &proc, &proc_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3212,7 +3229,7 @@ PHP_FUNCTION(odbc_procedures) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLProcedures"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3221,7 +3238,7 @@ PHP_FUNCTION(odbc_procedures) if (result->numcols > 0) { if (!odbc_bindcols(result)) { - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } } else { @@ -3229,7 +3246,6 @@ PHP_FUNCTION(odbc_procedures) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); } /* }}} */ #endif /* HAVE_SOLID */ @@ -3246,7 +3262,7 @@ PHP_FUNCTION(odbc_specialcolumns) SQLUSMALLINT type, scope, nullable; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rls!ssll", &pv_conn, &vtype, &cat, &cat_len, &schema, &schema_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ols!ssll", &pv_conn, odbc_connection_ce, &vtype, &cat, &cat_len, &schema, &schema_len, &name, &name_len, &vscope, &vnullable) == FAILURE) { RETURN_THROWS(); } @@ -3255,22 +3271,22 @@ PHP_FUNCTION(odbc_specialcolumns) scope = (SQLUSMALLINT) vscope; nullable = (SQLUSMALLINT) vnullable; - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3283,7 +3299,7 @@ PHP_FUNCTION(odbc_specialcolumns) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLSpecialColumns"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3292,7 +3308,7 @@ PHP_FUNCTION(odbc_specialcolumns) if (result->numcols > 0) { if (!odbc_bindcols(result)) { - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } } else { @@ -3300,7 +3316,6 @@ PHP_FUNCTION(odbc_specialcolumns) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); } /* }}} */ @@ -3316,7 +3331,7 @@ PHP_FUNCTION(odbc_statistics) SQLUSMALLINT unique, reserved; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs!ssll", &pv_conn, &cat, &cat_len, &schema, &schema_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!ssll", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &name, &name_len, &vunique, &vreserved) == FAILURE) { RETURN_THROWS(); } @@ -3324,22 +3339,22 @@ PHP_FUNCTION(odbc_statistics) unique = (SQLUSMALLINT) vunique; reserved = (SQLUSMALLINT) vreserved; - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3352,7 +3367,7 @@ PHP_FUNCTION(odbc_statistics) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLStatistics"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3361,7 +3376,7 @@ PHP_FUNCTION(odbc_statistics) if (result->numcols > 0) { if (!odbc_bindcols(result)) { - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } } else { @@ -3369,7 +3384,6 @@ PHP_FUNCTION(odbc_statistics) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); } /* }}} */ @@ -3384,26 +3398,26 @@ PHP_FUNCTION(odbc_tableprivileges) size_t cat_len = 0, schema_len, table_len; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs!ss", &pv_conn, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!ss", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3414,7 +3428,7 @@ PHP_FUNCTION(odbc_tableprivileges) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLTablePrivileges"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3423,7 +3437,7 @@ PHP_FUNCTION(odbc_tableprivileges) if (result->numcols > 0) { if (!odbc_bindcols(result)) { - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } } else { @@ -3431,7 +3445,6 @@ PHP_FUNCTION(odbc_tableprivileges) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); } /* }}} */ #endif /* HAVE_DBMAKER */ diff --git a/ext/odbc/php_odbc_includes.h b/ext/odbc/php_odbc_includes.h index 21c74d317c68c..383cea5e67557 100644 --- a/ext/odbc/php_odbc_includes.h +++ b/ext/odbc/php_odbc_includes.h @@ -191,10 +191,15 @@ typedef struct odbc_connection { ODBC_SQL_CONN_T hdbc; char laststate[6]; char lasterrormsg[SQL_MAX_MESSAGE_LENGTH]; - zend_resource *res; - int persistent; } odbc_connection; +typedef struct odbc_link { + odbc_connection *connection; + zend_string *hash; + bool persistent; + zend_object std; +} odbc_link; + typedef struct odbc_result_value { char name[256]; char *value; @@ -220,8 +225,9 @@ typedef struct odbc_result { zend_long longreadlen; int binmode; int fetched; - odbc_param_info * param_info; + odbc_param_info *param_info; odbc_connection *conn_ptr; + zend_object std; } odbc_result; ZEND_BEGIN_MODULE_GLOBALS(odbc) @@ -240,8 +246,8 @@ ZEND_BEGIN_MODULE_GLOBALS(odbc) zend_long default_cursortype; char laststate[6]; char lasterrormsg[SQL_MAX_MESSAGE_LENGTH]; - HashTable *resource_list; - HashTable *resource_plist; + HashTable results; + HashTable connections; ZEND_END_MODULE_GLOBALS(odbc) int odbc_add_result(HashTable *list, odbc_result *result); diff --git a/ext/odbc/tests/bug78473.phpt b/ext/odbc/tests/bug78473.phpt index 3903187a36219..bed712e37bd8d 100644 --- a/ext/odbc/tests/bug78473.phpt +++ b/ext/odbc/tests/bug78473.phpt @@ -11,6 +11,6 @@ try { } var_dump(STDIN); ?> ---EXPECTF-- -odbc_close(): supplied resource is not a valid ODBC-Link resource -resource(%d) of type (stream) +--EXPECT-- +odbc_close(): Argument #1 ($odbc) must be of type ODBC\Connection, resource given +resource(1) of type (stream)