From 9c9b8f3e66ec3cb9a2e638bec8d2be2b8cfc0ff2 Mon Sep 17 00:00:00 2001
From: Kamil Tekiela <tekiela246@gmail.com>
Date: Mon, 22 Mar 2021 17:10:20 +0100
Subject: [PATCH] Improve mysqli_query docs

* Add a nice big SQL injection warning

* Break overlong breaks

* Rewrite result_mode explanation

* Simplify examples

* Add mysqli_prepare to see also

Closes GH-497.
---
 reference/mysqli/mysqli/query.xml      | 129 +++++++++++--------------
 reference/mysqli/mysqli/real-query.xml |  16 ++-
 2 files changed, 71 insertions(+), 74 deletions(-)

diff --git a/reference/mysqli/mysqli/query.xml b/reference/mysqli/mysqli/query.xml
index 4805db6150..3df7427eed 100644
--- a/reference/mysqli/mysqli/query.xml
+++ b/reference/mysqli/mysqli/query.xml
@@ -77,29 +77,46 @@
       <para>
        The query string.
       </para>
-      <para>
-       Data inside the query should be <link linkend="mysqli.real-escape-string">properly escaped</link>.
-      </para>
+      <warning>
+       <title>Security warning: SQL injection</title>
+       <para>
+        If the query contains any variable input then 
+        <link linkend="mysqli.quickstart.prepared-statements">parameterized
+        prepared statements</link> should be used instead. Alternatively, the
+        data must be properly formatted and all strings must be escaped using 
+        the <function>mysqli_real_escape_string</function>
+        function.
+       </para>
+      </warning>
      </listitem>
     </varlistentry>
     <varlistentry>
      <term><parameter>result_mode</parameter></term>
      <listitem>
       <para>
-       Either the constant <constant>MYSQLI_USE_RESULT</constant> or
-       <constant>MYSQLI_STORE_RESULT</constant> depending on the desired
-       behavior. By default, <constant>MYSQLI_STORE_RESULT</constant> is used.
+       The result mode can be one of 3 constants indicating how the result will
+       be returned from the MySQL server.
       </para>
       <para>
-       If you use <constant>MYSQLI_USE_RESULT</constant> all subsequent calls
-       will return error <literal>Commands out of sync</literal> unless you
-       call <function>mysqli_free_result</function>
+       <constant>MYSQLI_STORE_RESULT</constant> (default) - returns a
+       <classname>mysqli_result</classname> object with buffered result set.
       </para>
       <para>
-       With <constant>MYSQLI_ASYNC</constant> (available with mysqlnd), it is
-       possible to perform query asynchronously.
+       <constant>MYSQLI_USE_RESULT</constant> - returns a
+       <classname>mysqli_result</classname> object with unbuffered result set. 
+       As long as there are pending records waiting to be fetched, the
+       connection line will be busy and all subsequent calls will return error 
+       <literal>Commands out of sync</literal>. To avoid the error all records 
+       must be fetched from the server or the result set must be discarded by
+       calling <function>mysqli_free_result</function>.
+      </para>
+      <para>
+       <constant>MYSQLI_ASYNC</constant> (available with mysqlnd) - the query is
+       performed asynchronously and no result set is immediately returned.
        <function>mysqli_poll</function> is then used to get results from such
-       queries.
+       queries. Used in combination with either
+       <constant>MYSQLI_STORE_RESULT</constant> or
+       <constant>MYSQLI_USE_RESULT</constant> constant.
       </para>
      </listitem>
     </varlistentry>
@@ -110,9 +127,11 @@
  <refsect1 role="returnvalues">
   &reftitle.returnvalues;
   <para>
-   Returns &false; on failure. For successful queries which produce a result set, such as <literal>SELECT, SHOW, DESCRIBE</literal> or
+   Returns &false; on failure. For successful queries which produce a result
+   set, such as <literal>SELECT, SHOW, DESCRIBE</literal> or
    <literal>EXPLAIN</literal>, <function>mysqli_query</function> will return
-   a <classname>mysqli_result</classname> object. For other successful queries, <function>mysqli_query</function> will
+   a <classname>mysqli_result</classname> object. For other successful queries,
+   <function>mysqli_query</function> will
    return &true;.
   </para>
  </refsect1>
@@ -125,90 +144,59 @@
    <programlisting role="php">
 <![CDATA[
 <?php
+
+mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
 $mysqli = new mysqli("localhost", "my_user", "my_password", "world");
 
-/* check connection */
-if ($mysqli->connect_errno) {
-    printf("Connect failed: %s\n", $mysqli->connect_error);
-    exit();
-}
-
 /* Create table doesn't return a resultset */
-if ($mysqli->query("CREATE TEMPORARY TABLE myCity LIKE City") === TRUE) {
-    printf("Table myCity successfully created.\n");
-}
+$mysqli->query("CREATE TEMPORARY TABLE myCity LIKE City");
+printf("Table myCity successfully created.\n");
 
 /* Select queries return a resultset */
-if ($result = $mysqli->query("SELECT Name FROM City LIMIT 10")) {
-    printf("Select returned %d rows.\n", $result->num_rows);
-
-    /* free result set */
-    $result->close();
-}
+$result = $mysqli->query("SELECT Name FROM City LIMIT 10");
+printf("Select returned %d rows.\n", $result->num_rows);
 
 /* If we have to retrieve large amount of data we use MYSQLI_USE_RESULT */
-if ($result = $mysqli->query("SELECT * FROM City", MYSQLI_USE_RESULT)) {
+$result = $mysqli->query("SELECT * FROM City", MYSQLI_USE_RESULT);
 
-    /* Note, that we can't execute any functions which interact with the
-       server until result set was closed. All calls will return an
-       'out of sync' error */
-    if (!$mysqli->query("SET @a:='this will not work'")) {
-        printf("Error: %s\n", $mysqli->error);
-    }
-    $result->close();
-}
-
-$mysqli->close();
-?>
+/* Note, that we can't execute any functions which interact with the
+	server until all records have been fully retrieved or the result
+	set was closed. All calls will return an 'out of sync' error */
+$mysqli->query("SET @a:='this will not work'");
 ]]>
    </programlisting>
    <para>&style.procedural;</para>
    <programlisting role="php">
 <![CDATA[
 <?php
+
+mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
 $link = mysqli_connect("localhost", "my_user", "my_password", "world");
 
-/* check connection */
-if (mysqli_connect_errno()) {
-    printf("Connect failed: %s\n", mysqli_connect_error());
-    exit();
-}
-
 /* Create table doesn't return a resultset */
-if (mysqli_query($link, "CREATE TEMPORARY TABLE myCity LIKE City") === TRUE) {
-    printf("Table myCity successfully created.\n");
-}
+mysqli_query($link, "CREATE TEMPORARY TABLE myCity LIKE City");
+printf("Table myCity successfully created.\n");
 
 /* Select queries return a resultset */
-if ($result = mysqli_query($link, "SELECT Name FROM City LIMIT 10")) {
-    printf("Select returned %d rows.\n", mysqli_num_rows($result));
-
-    /* free result set */
-    mysqli_free_result($result);
-}
+$result = mysqli_query($link, "SELECT Name FROM City LIMIT 10");
+printf("Select returned %d rows.\n", mysqli_num_rows($result));
 
 /* If we have to retrieve large amount of data we use MYSQLI_USE_RESULT */
-if ($result = mysqli_query($link, "SELECT * FROM City", MYSQLI_USE_RESULT)) {
+$result = mysqli_query($link, "SELECT * FROM City", MYSQLI_USE_RESULT);
 
-    /* Note, that we can't execute any functions which interact with the
-       server until result set was closed. All calls will return an
-       'out of sync' error */
-    if (!mysqli_query($link, "SET @a:='this will not work'")) {
-        printf("Error: %s\n", mysqli_error($link));
-    }
-    mysqli_free_result($result);
-}
-
-mysqli_close($link);
-?>
+/* Note, that we can't execute any functions which interact with the
+	server until all records have been fully retrieved or the result
+	set was closed. All calls will return an 'out of sync' error */
+mysqli_query($link, "SET @a:='this will not work'");
 ]]>
    </programlisting>
-   &examples.outputs;
+   &examples.outputs.similar;
    <screen>
 <![CDATA[
 Table myCity successfully created.
 Select returned 10 rows.
-Error: Commands out of sync;  You can't run this command now
+
+Fatal error: Uncaught mysqli_sql_exception: Commands out of sync; you can't run this command now in...
 ]]>
    </screen>
   </example>
@@ -220,6 +208,7 @@ Error: Commands out of sync;  You can't run this command now
    <simplelist>
     <member><function>mysqli_real_query</function></member>
     <member><function>mysqli_multi_query</function></member>
+    <member><function>mysqli_prepare</function></member>
     <member><function>mysqli_free_result</function></member>
    </simplelist>
   </para>
diff --git a/reference/mysqli/mysqli/real-query.xml b/reference/mysqli/mysqli/real-query.xml
index 59d170385d..e4e5f08fc5 100644
--- a/reference/mysqli/mysqli/real-query.xml
+++ b/reference/mysqli/mysqli/real-query.xml
@@ -40,11 +40,19 @@
      <term><parameter>query</parameter></term>
      <listitem>
       <para>
-       The query, as a string.
-      </para>
-      <para>
-       Data inside the query should be <link linkend="mysqli.real-escape-string">properly escaped</link>.
+       The query string.
       </para>
+      <warning>
+       <title>Security warning: SQL injection</title>
+       <para>
+        If the query contains any variable input then 
+        <link linkend="mysqli.quickstart.prepared-statements">parameterized
+        prepared statements</link> should be used instead. Alternatively, the
+        data must be properly formatted and all strings must be escaped using 
+        the <function>mysqli_real_escape_string</function>
+        function.
+       </para>
+      </warning>
      </listitem>
     </varlistentry>
    </variablelist>