Using PHP and SQLite - php

I am going to use a small SQLite database to store some data that my application will use.
I cant however get the syntax for inserting data into the DB using PHP to correctly work, below is the code that i am trying to run:
<?php
$day = $_POST["form_Day"];
$hour = $_POST["form_Hour"];
$minute = $_POST["form_Minute"];
$type = $_POST["form_Type"];
$lane = $_POST["form_Lane"];
try
{
$db = new PDO('sqlite:EVENTS.sqlite');
$db->exec("INSERT INTO events (Day, Hour, Minute, Type, Lane) VALUES ($day, $hour, $minute, $type, $lane);");
$db = NULL;
}
catch(PDOException $e)
{
print 'Exception : '.$e->getMessage();
}
?>
I have successfully created a SQLite database file using some code that i wrote but i just cant seem to insert data into the database.

You can't simply insert strings inside your query like that. Take a look at PDO::quote() and prepared statements.

there's nothing syntactically wrong with this, unless one of the vars ($day, $hour, etc) returns an empty string.
$db->exec("INSERT INTO events (Day, Hour, Minute, Type, Lane) VALUES ($day, $hour, $minute, $type, $lane);");
having said that, i'd be more worried about sql injection because you're applying $_POST variables directly into an sql statement without validation.

You should rather use parametrized queries. Try this:
$db = new PDO('sqlite:EVENTS.sqlite');
$stmnt = $db->prepare("INSERT INTO events (Day, Hour, Minute, Type, Lane) VALUES (:day, :hour, :minute, :type, :lane);");
$stmnt->execute( array('day'=>$day,'hour'=>$hour, 'minute'=>$minute, 'type'=>$type, 'lane'=>$lane) );
$db = NULL;

You should explicitly commit transactions after the modifying DML statements (INSERT, DELETE, UPDATE) with COMMIT;.

Related

How can I avoid inserting empty rows into MySQL when working with PHP variables that have no value?

I have searched for the last few hours on this and have come up empty.
I am using a sample piece of code that I have edited slightly to work as needed. It posts values to a MySQL table, each set to a respective row. What I would like to be able to do is have the code not create a row if the PHP variable does not have a value. I am still relatively new with MySQL but have been really digging in so please if you have an answer, help me understand some of the meaning behind it. Thank you for your help!
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "database";
$col1Val1 = $_POST['col1Val1'];
$col1Val2 = $_POST['col1Val2'];
$col1Val3 = $_POST['col1Val3'];
$col2Val1 = $_POST['col2Val1'];
$col2Val2 = $_POST['col2Val2'];
$col2Val3 = $_POST['col2Val3'];
$col3Val1 = $_POST['col3Val1'];
$col3Val2 = $_POST['col3Val2'];
$col3Val3 = $_POST['col3Val3'];
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
// set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// begin the transaction
$conn->beginTransaction();
// our SQL statements
$conn->exec("INSERT INTO tableName(column1, column2, column3)
VALUES ('$col1Val1', '$col2Val1', '$col3Val1'),
('$col1Val2', '$col2Val2', '$col3Val2'),
('$col1Val3', '$col2Val3', '$col3Val3')");
// commit the transaction
$conn->commit();
echo "New records created successfully";
}
catch(PDOException $e)
{
// roll back the transaction if something failed
$conn->rollback();
echo "Error: " . $e->getMessage();
}
$conn = null;
?>
The way that query is currently written with multiple value sets, it's going to insert three rows whether they're empty or not. You need to evaluate each row separately in order to avoid inserting empty ones.
You have two main options.
Prepare an INSERT statement with placeholders for one row of values, iterate your $_POST and only execute the statement with rows that aren't empty. This is what I would suggest. With only a maximum of three rows to insert, the performance hit of executing multiple queries should be minimal.
Build your INSERT statement dynamically, and append a set of values for each of the rows that aren't empty. This is fine too, and it is still possible to construct a prepared statement that way, but for something like this it seems more complicated than necessary.
My suggestion for option 1:
$sql = "INSERT INTO tableName(column1, column2, column3) VALUES (?, ?, ?)";
$statement = $conn->prepare($sql);
for ($i=1; $i <= 3; $i++) {
$row = [];
for ($j=0; $j <= 3; $j++) {
$row[] = $_POST["col{$i}Val{$j}"];
}
if (array_filter($row)) { // check if the row has any non-empty values
$statement->execute($row);
}
}
This could be simplified a bit if you changed the names of your form fields up a bit so that you could get the values from sub-arrays in $_POST.
So thank you to all who gave me some tips. I believe I came up with a solution that will work decently for anyone who comes across the same issue.
What I did was for each set of data that goes to its own row, I created an ID field "row#Data" in the HTML that is defaulted to 0 but changes to 1 if each value is filled out. Then I used the if statement for each row instead of checking each variable.
There may be other ways to do this dynamically but to ensure functionality, this is what I came up with.
if ($row1Data == 1) $conn->exec(INSERT INTO...
if ($row2Data == 1) $conn->exec(INSERT INTO...
if ($row3Data == 1) $conn->exec(INSERT INTO...
...

In Cakephp, how to prevent sql injection if I use direct mysql queires rather than using models?

I have to deal with large mysql DB. Sql queries with lot of calculations (in select clause) and several kind of conditions in where clauses. So, I decided to use row/direct sql queries to deal with DB by using $db = ConnectionManager::getDataSource('default');
If I use this, how I prevent sql injection in mysql query? "mysql_real_escape_string" no longer exists. Is there any way to use PDO within CakePHP?
You can use this in your controller (or component)
// Initiate PDO connection
$this->_pdocon = $this->WhateverYourModel->getDataSource()->getConnection();
try {
// Select Query
$company = "What";
$stmt = $this->_pdocon->prepare('SELECT * FROM `agents` WHERE `company` LIKE :company LIMIT 2');
$stmt->bindValue(':company', $company, PDO::PARAM_STR);
// Start transaction
$this->_pdocon->begin();
// Loop through the events
if( $stm->execute() ) {
while ($row = $stmt->fetchAll(PDO::FETCH_ASSOC)) {
$stmt2 = $this->_pdocon->prepare("INSERT INTO `company`
(`id`, `name`, `identityno`, `modified`, `created`)
VALUES
(NULL, :name, :identityno, NOW(), NOW())");
$stmt2->bindValue(':name', $row['name'], PDO::PARAM_STR);
$stmt2->bindValue(':identityno', $row['id'], PDO::PARAM_INT);
$stmt2->execute();
}
}
// Commit transaction
$this->_pdocon->commit();
// Get last insert Id
$row_id = $this->_pdocon->lastInsertId();
var_dump($row_id);
} catch (PDOException $e) {
// Rollback transaction
$this->_pdocon->rollback();
echo "! PDO Error : " . $e->getMessage() . "<br/>";
}
This is what I ended-up. Using PDO has been solved thousands of issues. Now the system is fast and no memory exhaust error. And I can not putting all issues, errors what I got, in my question. It's good to giving direct answer rather trying to changing questions in here!
A large part of the point of cakePhp is not to do this. Therefore I would recommend not doing this.
Cakephp has a its own implementation for accessing a DB and you should use it if at all possible. Is there a particular reason you want to go around it?
if you realy want to, you can still use mysqli but I cant recommend it.

Bulk insert using PDO and PHP variable containing all database values

I am new to PHP and am trying to update a deprecated code from mysql to PDO.
Considering that the variable $insert contains all values to bulk insert such as:
('82817cf5-52be-4ee4-953c-d3f4ed1459b0','1','EM3X001P.1a','04.03.10.42.00.02'),
('82817cf5-52be-4ee4-953c-d3f4ed1459b0','2','EM3X001P.2a','04.03.10.33.00.02'),
...etc 13k lines to insert
here is the deprecated code:
mysql_connect('localhost', 'root', '') or die(mysql_error());
mysql_select_db("IPXTools") or die(mysql_error());
if ($insert != '')
{
$insert = "INSERT INTO IPXTools.MSSWireList (ID,Record,VlookupNode,HostWireLocation) VALUES ".$insert;
$insert .= "ON DUPLICATE KEY UPDATE Record=VALUES(Record),VlookupNode=VALUES(VlookupNode),HostWireLocation=VALUES(HostWireLocation)";
mysql_query($insert) or die(mysql_error());
$insert = '';
}
here is the new code:
try
{
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //set the PDO error mode to exception
// prepare sql and bind parameters
$stmt = $conn->prepare("INSERT INTO IPXTools.MSSWireList (ID, Record, VlookupNode, HostWireLocation)
VALUES (:ID, :Record, :VlookupNode, :HostWireLocation)");
$stmt->bindParam(':ID', $ID);
$stmt->bindParam(':Record', $Record);
$stmt->bindParam(':VlookupNode', $VlookupNode);
$stmt->bindParam(':HostWireLocation', $HostWireLocation);
// insert a row
// loop through all values inside the $insert variable??????? how?
$stmt->execute();
}
catch(PDOException $e)
{
echo "Error: " . $e->getMessage();
}
$conn = null;
During my research I found an excellent post:
PDO Prepared Inserts multiple rows in single query
One method says I would have to change my $insert variable to include all the field names.
And other method says I dont have to do that. I am looking at Chris M. suggestion:
The Accepted Answer by Herbert Balagtas works well when the $data array is small. With larger $data arrays the array_merge function becomes prohibitively slow. My test file to create the $data array has 28 cols and is about 80,000 lines. The final script took 41s to complete
but I didnt understand what he is doing and I am trying to adapt my code to his. The PHP sintax is new to me so I am strugling with handling the arrays, etc...
I guess the starting point would be the variable $insert which contains all the database values I need.
Do I need to modify my $insert variable to include the field names?
Or I could just use its content and extract the values (how?) and include the values in a loop statement? (that would probably execute my 13k rows one at at time)
Thank you
If you have 13k records to insert, it is good for performance to do not use prepared SQL statement. Just generate SQL query in format like this:
INSERT INTO IPXTools.MSSWireList
(ID, Record, VlookupNode, HostWireLocation)
VALUES
('id1', 'r1', 'node1', 'location1'),
('id2', 'r2', 'node2', 'location2'),
...
('id13000', 'r13000', 'node13000', 'location13000');
What you may do for it - use maner of your legacy code. Your try block will looks loke this:
try
{
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->exec($insert);
}

Real_escape_string prevents INSERT statement from working MYSQL PHP

So I'm making my own blog scripts using MYSQL and PHP.
I had the whole 'writing the blog to a database' thing working perfectly, until I realised that if you tried to write a blog with speech marks, this would prevent the INSERT statement from working (obviously - the speechmarks were ending the SQL statement).
So I tried to use real_escape_string, and now the INSERT doesn't work even if you exclude quotes.
I tried using:
sqlstate
in order to find out the issue, and it returned "42000" - which, after googling for a little bit, refers to a syntax error, which doesn't make much sense as there is no syntax error before the use of real_escape_string.
Also, I'm now getting this error:
Call to a member function close() on a non-object in /postarticle.php on line 37
Which refers to the close() call in the ELSE statement.
Please may you help? Been going round in circles for a while. Here is my code:
<?php
$host = 'CENSORED';
$user = 'CENSORED';
$pass = 'CENSORED';
$db = 'CENSORED';
$connection = new mysqli($host,$user,$pass,$db);
$_SESSION["article"] = $_POST["article"];
$date_of_blog = getdate();
$article = ($_SESSION["article"]);
$sql1 = "SELECT * FROM `Blogs`";
$res1 = $connection->query($sql1);
$newrows = $res1->num_rows + 1;
$sql2 = "INSERT INTO Blogs(BlogID, Blog_Contents, D_O_B) VALUES ('$newrows','$article','$date_of_blog')";
$sql2 = $connection->real_escape_string($sql2);
$res2 = $connection->query($sql2);
if ($res2->num_rows == $newrows)
{
$res->close();
$connection->close();
header( 'Location: adminpanel.php' );
}
else
{
echo ($connection->sqlstate);
$connection->close();
$res->close();
}
exit();
?>
Also, on a side note, the getdate() call that I've got has never worked. In the database every blog post comes up as:
0000:00:00 00:00:00
EDIT:
Issue is now solved. Find the functional code below:
<?php
$host = 'CENSORED';
$user = 'CENSORED';
$pass = 'CENSORED';
$db = 'CENSORED';
$connection = new mysqli($host,$user,$pass,$db);
$_SESSION["article"] = $_POST["article"];
$article = ($_SESSION["article"]);
$article = $connection->real_escape_string($article);
$sql1 = "SELECT * FROM `Blogs`";
$res1 = $connection->query($sql1);
$newrows = $res1->num_rows + 1;
$sql2 = "INSERT INTO Blogs(BlogID, Blog_Contents, D_O_B) VALUES (\"$newrows\",\"$article\",CURDATE())";
$res2 = $connection->query($sql2);
if ($res2 != false)
{
header( 'Location: adminpanel.php' );
}
else
{
echo ($connection->sqlstate);
}
$connection->close();
$res->close();
exit();
?>
I'm very sorry if these questions are basic and annoy the professionals around here; I've tried to follow the guidelines and I've googled for a while etc. I just haven't found any solutions that match my issue(s).
Thankyou for your time.
There are a number issues with the code as originally posted. Chiefly, the cause of the two issues you initially identified is a misuse of mysqli::real_escape_string(). It needs to be called on each variable individually which appears in the code. So instead of calling it on the whole statement, it must be called multiple times for multiple variables like:
$article = $connection->real_escape_string($connection);
The failure of the query due to incorrect quoting (due to real_escape_string()) is the reason for the error message calling close().
As ascertained in the comments, you are using num_rows + 1 to validate that one new row has been inserted based on the previous number of rows returned. This is problematic for a few reasons. Mainly, it exposes a race condition wherein a row may be inserted from two sessions at once and one or both will fail because the expected value for $newrows doesn't match. Really BlogID should be an auto_increment column in your database. That eliminates the need for any logic around it whatsoever. You don't even need to include it in the INSERT because it will be automatically incremented.
That also completely eliminates the need for the first SELECT statement.
Substituting MySQL's native NOW() function for the date value, you can simplify the statement to:
INSERT INTO Blogs (Blog_Contents, D_O_B) VALUES ('$article', NOW())
To test success or failure of the insert, you just need to verify that its variable is not false.
Putting this together, your code can be reduced as:
if (!isset($_POST['article'])) {
// exit or handle an empty post somehow...
}
$connection = new mysqli($host,$user,$pass,$db);
$_SESSION["article"] = $_POST["article"];
// Escape $article for later use
$article = $connection->real_escape_string($_SESSION["article"]);
// Only an INSERT is needed. $article is already escaped
$sql = "INSERT INTO Blogs (Blog_Contents, D_O_B) VALUES ('$article', NOW())";
// Run the query
$res = $connection->query($sql);
// Test for failure by checking for a false value
if ($res) {
// The connection & resource closure can be omitted
// PHP will handle that automatically and implicitly.
header( 'Location: adminpanel.php' );
// Explictly exit as good practice after redirection
exit();
}
else {
// The INSERT failed. Check the error message
echo $connection->error;
}
This should bring your current code into working order. However, since you're learning this it is an excellent time to begin learning to use prepared statements via prepare()/bind_param()/execute() in MySQLi. This is a recommended best practice to prevent SQL injection, although using real_escape_string() works as long as you use it correctly and never forget.
See How can I prevent SQL injection in PHP for examples.
But it would look like:
// connection already established, etc...
// Prepare the statement using a ? placeholder for article
$stmt = $connection->prepare("INSERT INTO Blogs (Blog_Contents, D_O_B) VALUES (?, NOW())");
if ($stmt) {
// bind in the variable and execute
// Note that real_escape_string() is not needed when using
// the ? placeholder for article
$stmt->bind_param('s', $_SESSION['article']);
$stmt->execute();
// Redirect
header( 'Location: adminpanel.php' );
exit();
}
else {
echo $connection->error;
}
You need to apply the real_escape_string function to the variables not the entire SQL string.
$sql2 = "INSERT INTO Blogs(BlogID, Blog_Contents, D_O_B) VALUES ('".$connection->real_escape_string($newrows)."','".$connection->real_escape_string($article)."','".$connection->real_escape_string($date_of_blog)."')";
The purpose is to remove anything that might be misinterpreted as query functions by MySQL, but there are parts of the query that you obviously want to be interpreted as such.

Query executing in a loop causes script to exit after first iteration

Perplexed here.... This code:
$qry = sprintf("INSERT INTO news_sites_homepage_grab
VALUES ('', %d, '%s', NOW(), NOW())",
$site_id, mysql_real_escape_string($html));
...is executed in a loop where $html changes every time. This code executes once but the next time, the script simply dies. No warnings/errors, nothing. $html is a string representing a webpage so it could be very long. I have upped the memory limit in PHP to 32M and set max_allowed_packet in MySQL to 16M but nothing.
Anyone have any ideas? Thanks!
UPDATE: Here is the function that is called within a loop.
function save_html($site_id, $html) {
global $db;
try {
$qry = sprintf("INSERT INTO site_grab VALUES ('', %d, '%s', NOW(), NOW())",
$site_id,
mysql_real_escape_string($html));
$db->insert($qry);
}
catch(Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
return;
}
My recommendation would be to forgo troubleshooting this issue and switch to PDO. It supports prepared statements, which aren't vulnerable to SQL injection when you use parameters and are much more performant for repeated queries.
Here's a simple refactoring of your function, which assumes $db holds a PDO instance:
function save_html($site_id, $html) {
global $db;
static $insert = Null;
if (isnull($insert)) {
/* explicitly name the columns, in case you later add more to the table
or change their order. I'm also guessing `id` is auto-incrementing,
so I'm leaving it out.
*/
$insert = $db->prepare("INSERT INTO site_grab (sid, site_text, date_added, date_modified) VALUES (?, ?, NOW(), NOW())");
}
try {
$insert->execute(array($site_id, $html));
} catch(Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
return;
}
If date_added has type TIMESTAMP, you can set its default value to CURRENT_TIMESTAMP and leave it out of the insert. Alternatively, add an ON UPDATE CURRENT_TIMESTAMP property to the date_modified column, which means you don't need to explicitly set the field when you update a row. If inserting is going to be more common than updating, do the former; otherwise, do the latter.
You could also store prepared statements in an object so they're accessible from multiple functions and replace the global $db with some sort of service locator (the simplest way is to use a static function or property of some class) or use dependency injection (read "Inversion of Control Containers and the Dependency Injection pattern").
I preffer using PDO and prepared statements but to (try to) answer your question try adding $db as the link identifier to mysql_real_escape_string() function.
Does that help? If not, try echoing the output of the mysql_error() function.
Sorry for the lack of formating, I'm mobile ATM.

Categories