{"id":2379,"date":"2016-04-03T12:33:09","date_gmt":"2016-04-03T19:33:09","guid":{"rendered":"http:\/\/www.wellgolly.com\/?p=2379"},"modified":"2018-05-03T06:59:03","modified_gmt":"2018-05-03T13:59:03","slug":"using-the-pdo-mysql-extension","status":"publish","type":"post","link":"https:\/\/www.wellgolly.com\/?p=2379","title":{"rendered":"Using the PDO MySQL extension"},"content":{"rendered":"<p>As I mentioned in previous posts, my server is continuously being attacked by hackers attempting to exploit MySQL injection vulnerabilities. I use input validation and mysql_real_escape_string to keep them from getting into the databases. Another way to prevent injection is to use stored procedures. Both PHP and perl allow stored procedures. To use them with PHP you use the PHP Data Objects extension. It should be automatically installed with your PHP and MySQL installation.<\/p>\n<p>To test if PDO is enabled, at the command line type:<br \/>\n<pre><code class=\"preserve-code-formatting\">\nphp -i|grep PDO<\/code><\/pre><br \/>\nYou should get something like this.<br \/>\n<pre><code class=\"preserve-code-formatting\">\nPDO\nPDO support =&gt; enabled\nPDO drivers =&gt; mysql, sqlite\nPDO Driver for MySQL =&gt; enabled\nPDO Driver for SQLite 3.x =&gt; enabled<\/code><\/pre><\/p>\n<p>As I mentioned in a previous post, I like to put the database setup info in a separate directory outside of the document root and then include it in the php file where it is used. The file would look like this:<br \/>\n<pre><code class=\"preserve-code-formatting\">\n&lt;?php\n$user = &#039;username&#039;;\n$pass = &#039;password&#039;;\n$host = &#039;localhost&#039;;\n$db_name = &#039;website&#039;;\n\ntry {\n&nbsp;&nbsp;&nbsp;&nbsp;$dbWG = new PDO(&quot;mysql:host=$host;db_name=$dbname&quot;, $user, $pass, array(PDO::MYSQL_ATTR_INIT_COMMAND =&gt; &quot;SET NAMES utf8&quot;) );\n&nbsp;&nbsp;&nbsp;&nbsp;$dbWG-&gt;setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );\n&nbsp;&nbsp;&nbsp;&nbsp;$dbWG-&gt;setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);\n&nbsp;&nbsp;&nbsp;&nbsp;$dbWG-&gt;setAttribute(PDO::ATTR_EMULATE_PREPARES, false);\n}\ncatch(PDOException $e) {\n&nbsp;&nbsp;&nbsp;&nbsp;$e-&gt;getMessage();\n&nbsp;&nbsp;&nbsp;&nbsp;file_put_contents(&#039;\/var\/log\/PDO_WG_errors.log&#039;, $e-&gt;getMessage(), FILE_APPEND);\n}\n?&gt;<\/code><\/pre><br \/>\nDon\u2019t forget to use start and end with the php markers or the code will show up on your page. To keep the naming convention consistent with what I have been using, this file would be called wg_db_pdo.inc, since the original PEAR version of the files was called wg.db.inc, and the MySQL versions have been called wg.db.my.inc. <\/p>\n<p>I used try-catch pattern so that I could find out if errors were happening while debugging, but it also is useful in production. Writing the errors to a file also prevents hackers from seeing errors on your site and giving them ideas about how to break in. To see if it was working, I changed the password to an invalid password and got this in the error log, so I know it works.<br \/>\n<pre><code class=\"preserve-code-formatting\">\nSQLSTATE[28000] [1045] Access denied for user &#039;username&#039;@&#039;localhost&#039; (using password: YES)<\/code><\/pre> <\/p>\n<p>I was getting this error in my normal php error logs.<br \/>\n<code class=\"preserve-code-formatting\">Invalid catalog name: 1046 No database selected<\/code><br \/>\nuntil I realized that the full database name is required when accessing a table.<br \/>\n<pre><code class=\"preserve-code-formatting\">\nSELECT * FROM `database`.`table`<\/code><\/pre><br \/>\nnot<br \/>\n<pre><code class=\"preserve-code-formatting\">\nSELECT * FROM `table`<\/code><\/pre><\/p>\n<p>When setting up the database access there are three attributes that I generally want on the database. There are several ways that the data can be returned. I usually want it to be in an associative array so I set the ATTR_DEFAULT_FETCH_MODE to FETCH_ASSOC. The data is in an associative array indexed by column name. As shown in the sample code below, it is straightforward to access the data with this method. Sometimes it is useful to have the data indexed by column number and you can override the default when you make your query. All of the options are listed on <a href=\"http:\/\/php.net\/manual\/en\/pdostatement.fetch.php\">php.net<\/a>.<\/p>\n<p>I\u2019ve been exploring various options and the code might be useful to someone, so I\u2019ve included it below.<br \/>\n<pre><code class=\"preserve-code-formatting\">\n&lt;!DOCTYPE html&gt;\n&lt;html lang=&quot;en&quot;&gt;\n\n&lt;head&gt;\n&nbsp;&nbsp;&lt;title&gt;PDO Test&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n&lt;?php\necho &quot;Available drivers: &quot;;\nprint_r(PDO::getAvailableDrivers());\n\nrequire_once(&#039;wg_db_pdo.inc&#039;);\n\n&nbsp;&nbsp;&nbsp;&nbsp;\/\/ Don&#039;t use this construct. It works but gives an error on the next query\n&nbsp;&nbsp;&nbsp;&nbsp;\/\/ 2014 Cannot execute queries while other unbuffered queries are active.\n&nbsp;&nbsp;&nbsp;&nbsp;\/\/$status = $dbWG-&gt;exec(&#039;SELECT * FROM `database`.`product`&#039;);\n&nbsp;&nbsp;&nbsp;&nbsp;\/\/echo &quot;The result of the select statement was $status&quot;;\n\n\/\/ Check to see if the MySQL driver is available\n\/\/ http:\/\/php.net\/manual\/en\/ref.pdo-mysql.php\nif ($dbWG-&gt;getAttribute(PDO::ATTR_DRIVER_NAME) == &#039;mysql&#039;) {\n&nbsp;&nbsp;&nbsp;&nbsp;$stmt = $dbWG-&gt;prepare(&#039;SELECT * FROM `database`.`product` LIMIT 10&#039;,\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;array(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY =&gt; true));\n&nbsp;&nbsp;&nbsp;&nbsp;$stmt-&gt;execute();\n&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;&lt;p&gt;Connected to database&lt;\/p&gt;\\n&quot;;\n\n} else {\n&nbsp;&nbsp;&nbsp;&nbsp;die(&quot;Failed to open database&quot;);\n}\n\n# using the shortcut -&gt;query() method here since there are no variable\n# values in the select statement.\n$result = $dbWG-&gt;query(&#039;SELECT name, id, tagline FROM `database`.`product` LIMIT 10&#039;);\n\necho &quot;&lt;h3&gt;Read from the&nbsp;&nbsp;result&lt;\/h3&gt;&quot;;\nwhile($row = $result-&gt;fetch()) {\n&nbsp;&nbsp;&nbsp;&nbsp;echo $row[&#039;id&#039;] . &quot;&nbsp;&nbsp; &quot;;\n&nbsp;&nbsp;&nbsp;&nbsp;echo $row[&#039;name&#039;] . &quot;&amp;mdash;&nbsp;&nbsp; &quot;;\n&nbsp;&nbsp;&nbsp;&nbsp;echo $row[&#039;tagline&#039;] . &quot;&lt;br \/&gt;&quot;;\n}\n\n\/\/ Resetting the cursor position is not supported.\necho &quot;&lt;h3&gt;Reading from the same result yields nothing&lt;\/h3&gt;&quot;;\necho &quot;&lt;p&gt;&quot;;\nwhile($row = $result-&gt;fetch()) {\n&nbsp;&nbsp;&nbsp;&nbsp;echo $row[&#039;id&#039;] . &quot;&nbsp;&nbsp; &quot;;\n&nbsp;&nbsp;&nbsp;&nbsp;echo $row[&#039;name&#039;];\n&nbsp;&nbsp;&nbsp;&nbsp;echo $row[&#039;tagline&#039;] . &quot;&lt;br \/&gt;&quot;;\n}\necho &quot;&lt;\/p&gt;&quot;;\n\n\/\/If you want to iterate twice over the results, fetch to an array and iterate over this array:\n$stmt = $dbWG-&gt;prepare(&#039;SELECT * FROM `database`.`product` LIMIT 10&#039;);\n$stmt-&gt;execute();\n\necho &quot;&lt;h3&gt;You can get the row count in MySQL after the statement is executed&lt;\/h3&gt;&quot;;\n$row_count = $stmt-&gt;rowCount();\necho $row_count.&#039; rows selected&#039;;\n\n$results = $stmt-&gt;fetchAll();\n\necho &quot;&lt;h3&gt;First iteration&lt;\/h3&gt;&quot;;\nforeach($results as $row) {\n&nbsp;&nbsp;&nbsp;&nbsp;echo $row[&#039;name&#039;] . &quot;&lt;br \/&gt;&quot;;\n}\n\necho &quot;&lt;h3&gt;Second iteration&lt;\/h3&gt;&quot;;\nforeach($results as $row) {\n&nbsp;&nbsp;&nbsp;&nbsp;echo $row[&#039;name&#039;] . &quot;&lt;br \/&gt;&quot;;\n}\n\n\/\/ If you load the results into an array, you can get the row count by counting on the array.\necho &quot;&lt;h3&gt;Get the number of rows from array&lt;\/h3&gt;&quot;;\n$numberOfRows = count($results);\necho &quot;The number of rows is $numberOfRows&quot;;\n\n\/\/ Alternatively, you need to do two queries. One to count and one for the data\necho &quot;&lt;h3&gt;Get number of rows using count(*)&lt;\/h3&gt;&quot;;\n$numRows = $dbWG-&gt;query(&#039;SELECT count(*) FROM `database`.`product` LIMIT 10&#039;)-&gt;fetchColumn();\necho $numRows;\n\necho &quot;&lt;h3&gt;Fetch as number: FETCH_NUM&lt;\/h3&gt;&quot;;\n$stmt-&gt;execute();\nwhile ($row = $stmt-&gt;fetch(PDO::FETCH_NUM)) {\n&nbsp;&nbsp;echo &quot;$row[0] $row[1] &lt;br \/&gt;&quot;;\n}\n\necho &quot;&lt;h3&gt;Alternative Fetch as number: FETCH_NUM&lt;\/h3&gt;&quot;;\n$stmt-&gt;execute();\n$result = $stmt-&gt;fetchAll(PDO::FETCH_NUM);\n foreach($result as $key=&gt;$val) {\n&nbsp;&nbsp;&nbsp;&nbsp;echo $key.&#039; - &#039;.$val[3].&#039;&lt;br \/&gt;&#039;;\n}\n\necho &quot;&lt;h3&gt;Prepared Statement with Parameters&lt;\/h3&gt;&quot;;\n$stmt = $dbWG-&gt;prepare(&quot;SELECT * FROM `database`.`product` WHERE id &gt; :id AND name LIKE :name&quot;);\n\n$product_id = 1;\n$input_from_user = &quot;Artic&quot;;\n$name = &quot;%&quot; . $input_from_user . &quot;%&quot;;\n$stmt-&gt;bindParam(&#039;:id&#039;, $product_id, PDO::PARAM_INT);\n$stmt-&gt;bindParam(&#039;:name&#039;, $name, PDO::PARAM_STR);\n\/\/ Alternatively\n$stmt-&gt;bindValue(&#039;:name&#039;, &quot;%{$input_from_user}%&quot;, PDO::PARAM_STR);\n\n$stmt-&gt;execute();\n$results = $stmt-&gt;fetchAll();\n\nforeach($results as $row) {\n&nbsp;&nbsp;&nbsp;&nbsp;echo $row[&#039;name&#039;] . &quot;&lt;br \/&gt;&quot;;\n}\n\n\/\/ These examples use question mark placeholders\necho &quot;&lt;h3&gt;Insert&lt;\/h3&gt;&quot;;\n$stmt = $dbWG-&gt;prepare(&#039;INSERT INTO `database`.`product_log` (`product_id`) VALUES (?)&#039;);\nfor ($i = 0; $i &lt; 10; $i++) {\n&nbsp;&nbsp;&nbsp;&nbsp;$product_id = $i + 10;\n&nbsp;&nbsp;&nbsp;&nbsp;$stmt-&gt;bindParam(1, $product_id);\n&nbsp;&nbsp;&nbsp;&nbsp;$stmt-&gt;execute();\n}\n\n\/\/ Insert with Array and placeholders\n\/\/ The product_log has a source field called src\necho &quot;&lt;h3&gt;Insert with Array and Placeholders&lt;\/h3&gt;&quot;;\n$values = array(&#039;page1.php&#039;, &#039;page2.php&#039;, &#039;page3.php&#039;, &#039;page4.php&#039;);\n$source = &#039;&#039;;\n$stmt = $dbWG-&gt;prepare(&#039;INSERT INTO `database`.`product_log` (`src`) VALUES (:source)&#039;);\n$stmt-&gt;bindParam(&#039;:source&#039;, $source, PDO::PARAM_STR);\nforeach($values as $source) {\n&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;The value to be inserted is $source&lt;br \/&gt;&quot;;\n&nbsp;&nbsp;&nbsp;&nbsp;$stmt-&gt;execute();\n}\n\n$insertId = $dbWG-&gt;lastInsertId();\necho &quot;The ID of the last row is $insertId&quot;;\n\necho &quot;&lt;h3&gt;Delete the last two rows&lt;\/h3&gt;&quot;;\n$stmt = $dbWG-&gt;prepare(&#039;DELETE FROM `database`.`product_log` WHERE id &gt;= ? AND id &lt;= ?&#039;);\n$min = $insertId - 1;\n$max = $insertId;\n$stmt-&gt;bindParam(1, $min, PDO::PARAM_INT);\n$stmt-&gt;bindParam(2, $max, PDO::PARAM_INT);\n\n$stmt-&gt;execute();\n$count = $stmt-&gt;rowCount();\necho &quot;The number of rows deleted is $count&quot;;\n\n# close the connection\n$dbWG = null;\n?&gt;\n&lt;\/body&gt;\n<\/code><\/pre><\/p>\n<p>For further reading I suggest <a href= \"http:\/\/wiki.hashphp.org\/PDO_Tutorial_for_MySQL_Developers\">hashPHP<\/a> to start and the <a href=\"http:\/\/php.net\/manual\/en\/book.pdo.php\">PDO Book<\/a> at php.net. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>As I mentioned in previous posts, my server is continuously being attacked by hackers attempting to exploit MySQL injection vulnerabilities. I use input validation and mysql_real_escape_string to keep them from getting into the databases. Another way to prevent injection is to use stored procedures. Both PHP and perl allow stored procedures. To use them with &hellip; <a href=\"https:\/\/www.wellgolly.com\/?p=2379\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Using the PDO MySQL extension<\/span><\/a><\/p>\n","protected":false},"author":6,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[27,34],"tags":[],"class_list":["post-2379","post","type-post","status-publish","format-standard","hentry","category-mysql","category-php"],"_links":{"self":[{"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=\/wp\/v2\/posts\/2379","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2379"}],"version-history":[{"count":0,"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=\/wp\/v2\/posts\/2379\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2379"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2379"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2379"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}