I've been playing around with switching over to the PDO way of doing database work in PHP. In my java life, I am able to place named queries into an associative array and call the prepared statement using the index. It's a bit more complex than that, but...
Anyways I thought it would be cool to do the same type of thing in PHP.
$NamedQueries['SelectBlackBoxById'] = "select name, category, rating from blackbox where id = :blackbox_id";
So I can prepare my statements this way:
$sth = $dbh->prepare($NamedQueries['SelectBlackBoxById']);
$sth->execute(array('blackbox_id' => '1'));
$sth->setFetchMode(PDO::FETCH_OBJ);
return $sth->fetch();
Instead of this way:
$sth = $dbh->prepare("select name, category, rating from blackbox where id = :blackbox_id");
$sth->execute(array('blackbox_id' => '1'));
$sth->setFetchMode(PDO::FETCH_OBJ);
return $sth->fetch();
I'm sure I am overlooking something, because my preferred way returns false. Any ideas would be greatly appreciated.
$sth->execute(array('blackbox_id' => '1'));
Should be
$sth->execute(array(':blackbox_id' => '1'));
You have to include the :
Did you try to dump out your Array value before you use it to prepare the query?
Overall, I don't see anything wrong with how the PDO would use it, you could also var_dump() your $sth variable after you prepare it to see what might be in there.
In general, as others pointed out the ':' is something you should include when you bind variables, although in this case I don't necessarily think it is the root problem as you said your second example worked, which uses the same syntax, the only difference is using the query from the array instead of a raw string. Thinking that since it is only 1 variable to bind, not having the ':' is not causing problems (though you should add it)
So, your real problem is lack of error handling. And it persists.
error_reporting(E_ALL);
to be notified of variables that out of scope.
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
after connect to be notified of empty queries and whatever else PDO errors
Related
I am trying to get a database query page to work but cant seem to do so.
my Code so far (here I tried bindValue, but previously tried bindParam and got the same result):
var_dump($_POST);
$dbh = new PDO ("mysql:host=$myServer;dbname=$myDB", $myUser, $myPw);
$columName = $_POST["columName"];
$tblName = $_POST["tblName"];
$valueName = $_POST["valueName"];
$specificValue = $_POST["specificValue"];
$stmt = $dbh->prepare("SELECT :columName FROM :tblName Where :valueName = :specificValue");
$stmt->bindValue(":columName", $columName);
$stmt->bindValue(":valueName", $valueName);
$stmt->bindValue(":tblName", $tblName);
$stmt->bindValue(":specificValue", $specificValue);
$stmt->execute();
$result = $stmt->fetch();
if(empty($result)){echo "empty";}
print_r ($stmt);
print_r($result);
Printing result and $stmt brings following results:
empty
PDOStatement Object ( [queryString] => SELECT :columName FROM :tblName Where :valueName = :specificValue )
What did I do wrong? What could I try to get it to work?
I am new to the whole coding thing, so please ask if I forgot any code or other important information!
Thanks!
Placeholder parameters can only represent VALUES in the query. Tables, field names, sql key words, etc.. are all impossible to use placeholders on.
If you need to build a dynamic query and replace field/table names, then you'll have to use good old string construction methods, and be aware that you'll be opening yourself to SQL injection attacks again:
$sql = "SELECT * FROM $foo WHERE $bar = :baz";
$stmt = $dbh->prepare($sql);
$stmt->bindValue(':baz', $baz);
I'm afraid you need to rethink how parameterised queries work. It's not just a case of magically inserting data in a safe way. It's about distingushing between the structure of a query and the data.
So the database name, the column names, the table names and any SQL keywords are part of the structure of the query. Every time you run the query, they will be the same.
The data, however, can change between running the query.
So the structure needs to be in place when the query is prepared. However, you obviously can't just plonk the $columName variable etc into the query for SQL injection reasons. If you really need to have flexible queries like this (nb that you probably don't) you need to create a whitelist of allowed values, either in your code or retrieved from the database.
Your query is invalid (you're using parameters for object identifiers) but you are not getting any notification because you have neither configured PDO to throw exceptions nor are calling the error check functions manually.
Add the PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION option to PDO's constructor:
$dbh = new PDO ("mysql:host=$myServer;dbname=$myDB", $myUser, $myPw, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
Once you do so, you'll get a prompt exception on the exact issue, e.g.:
PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near ''user' Where 'login' = 'john'' at line 1 in [...]
As you can see, this is trying to run a query like (e.g.):
SELECT 'user_id'
FROM 'user'
Where 'login' = 'john'
Additionally, beware of SQL injection. It's terribly unsafe to compose SQL queries using data from $_POST.
This problem is proceeding to drive me crazy...
First a disclaimer, I have very little formal programming training. I am stumbling thru this.
I am receiving the General error: 2014 error; at first it was one particular query (which was established in a class) and the warning was dependent upon where in the code I was instantiating the object. I proceeded to change every query to a fetchAll and then closed the cursor for every query as well after the fetchAll didn't work. Now there are two offending queries. Here is a copy and paste of one:
(UPDATED CODE):
$sql = "select initial_state from source_nodes where id = :id";
$core = Core::getInstance();
$stmt = $core->dbh->prepare($sql);
$stmt->bindParam(':id', $allSources->id[$id], PDO::PARAM_INT);
if ($stmt->execute()) {
$row = $stmt->fetchAll();
$stmt->closeCursor();
foreach($row as $i=>$value){
$allSources->state[$id] = $row[$i]['initial_state'];
}
}
Not certain if it matters, but the warning is thrown on the 'if'. As far as I am aware, every 'fetch' is now a 'fetchAll' and includes a 'closeCursor'.
The connection is being set like this:
$this->dbh = new PDO($dsn, $user, $password, array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY =>true
));
Suggestions?
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, TRUE);
is the silver bullet to fix the error, which text you didn't even bothered to provide, supposing readers to know every error by numbers by heart.
While just mechanically replacing all the fetch() calls to fetchAll() is not.
Two things on your edited code.
You have quite useless loop. As I said above, doing something mechanically, mindlessly, can bring no good. One have to have at least minimal understanding. The loop you have is useless twice: because $row you have from fetchAll() don't need any looping over (it already contain all the data) and because you are getting only one row anyway and no loop ever needed. And thus fetch() should be used not fetchAll.
Despite of your feeling, not "every 'fetch' is now a 'fetchAll'" as the error message clearly tells us.
It seems you are selecting rows one by one inside of a larger loop. And it apparently smells of some wrong query. You have to make this (invisible to us) outer query to get all the data at once, using fetchAll() and without all these inner queries.
I think that it could be a PHP bug.
https://bugs.php.net/bug.php?id=57540
What is your PHP version?
I've always done the simple connection of mysql_connect, mysql_pconnect:
$db = mysql_pconnect('*host*', '*user*', '*pass*');
if (!$db) {
echo("<strong>Error:</strong> Could not connect to the database!");
exit;
}
mysql_select_db('*database*');
While using this I've always used the simple method to escape any data before making a query, whether that be INSERT, SELECT, UPDATE or DELETE by using mysql_real_escape_string
$name = $_POST['name'];
$name = mysql_real_escape_string($name);
$sql = mysql_query("SELECT * FROM `users` WHERE (`name` = '$name')") or die(mysql_error());
Now I understand this is safe, to an extent!
It escapes dangerous characters; however, it is still vulnerable to other attacks which can contain safe characters but may be harmful to either displaying data or in some cases, modifying or deleting data maliciously.
So, I searched a little bit and found out about PDO, MySQLi and prepared statements. Yes, I may be late to the game but I've read many, many tutorials (tizag, W3C, blogs, Google searches) out there and not a single one has mentioned these. It seems very strange as to why, as just escaping user input really isn't secure and not good practice to say the least. Yes, I'm aware you could use Regex to tackle it, but still, I'm pretty sure that's not enough?
It is to my understanding that using PDO/prepared statements is a much safer way to store and retrieve data from a database when the variables are given by user input. The only trouble is, the switch over (especially after being very stuck in my ways/habits of previous coding) is a little difficult.
Right now I understand that to connect to my database using PDO I would use
$hostname = '*host*';
$username = '*user*';
$password = '*pass*';
$database = '*database*'
$dbh = new PDO("mysql:host=$hostname;dbname=$database", $username, $password);
if ($dbh) {
echo 'Connected to database';
} else {
echo 'Could not connect to database';
}
Now, function names are different so no longer will my mysql_query, mysql_fetch_array, mysql_num_rows etc work. So I'm having to read/remember a load of new ones, but this is where I'm getting confused.
If I wanted to insert data from say a sign up/registration form, how would I go about doing this, but mainly how would I go about it securely? I assume this is where prepared statements come in, but by using them does this eliminate the need to use something like mysql_real_escape_string? I know that mysql_real_escape_string requires you to be connected to a database via mysql_connect/mysql_pconnect so now we aren't using either won't this function just produce an error?
I've seen different ways to approach the PDO method too, for example, I've seen :variable and ? as what I think are known as place holders (sorry if that is wrong).
But I think this is roughly the idea of what should be done to fetch a user from a database
$user_id = $_GET['id']; // For example from a URL query string
$stmt = $dbh->prepare("SELECT * FROM `users` WHERE `id` = :user_id");
$stmt->bindParam(':user_id', $user_id, PDO::PARAM_INT);
But then I'm stuck on a couple things, if the variable wasn't a number and was a string of text, you have to given a length after PDO:PARAM_STR if I'm not mistaken. But how can you give a set length if you're not sure on the value given from user in-putted data, it can vary each time? Either way, as far as I know to display the data you then do
$stmt->execute();
$result = $stmt->fetchAll();
// Either
foreach($result as $row) {
echo $row['user_id'].'<br />';
echo $row['user_name'].'<br />';
echo $row['user_email'];
}
// Or
foreach($result as $row) {
$user_id = $row['user_id'];
$user_name = $row['user_name'];
$user_email = $row['user_email'];
}
echo("".$user_id."<br />".$user_name."<br />".$user_email."");
Now, is this all safe?
If I am right, would inserting data be the same for example:
$username = $_POST['username'];
$email = $_POST['email'];
$stmt = $dbh->prepare("INSERT INTO `users` (username, email)
VALUES (:username, :email)");
$stmt->bindParam(':username, $username, PDO::PARAM_STR, ?_LENGTH_?);
$stmt->bindParam(':email, $email, PDO::PARAM_STR, ?_LENGTH_?);
$stmt->execute();
Would that work, and is that safe too? If it is right what value would I put in for the ?_LENGTH_?? Have I got this all completely wrong?
UPDATE
The replies I've had so far have been extremely helpful, can't thank you guys enough! Everyone has got a +1 for opening my eyes up to something a little different. It's difficult to choose the top answer, but I think Col. Shrapnel deserves it as everything is pretty much covered, even going into other arrays with custom libraries which I wasn't aware of!
But thanks to all of you:)
Thanks for the interesting question. Here you go:
It escapes dangerous characters,
Your concept is utterly wrong.
In fact "dangerous characters" is a myth, there are none.
And mysql_real_escape_string escaping but merely a string delimiters. From this definition you can conclude it's limitations - it works only for strings.
however, it is still vulnerable to other attacks which can contain safe characters but may be harmful to either displaying data or in some cases, modifying or deleting data maliciously.
You're mixing here everything.
Speaking of database,
for the strings it is NOT vulnerable. As long as your strings being quoted and escaped, they cannot "modify or delete data maliciously".*
for the other data typedata - yes, it's useless. But not because it is somewhat "unsafe" but just because of improper use.
As for the displaying data, I suppose it is offtopic in the PDO related question, as PDO has nothing to do with displaying data either.
escaping user input
^^^ Another delusion to be noted!
a user input has absolutely nothing to do with escaping. As you can learn from the former definition, you have to escape strings, not whatever "user input". So, again:
you have escape strings, no matter of their source
it is useless to escape other types of data, no matter of the source.
Got the point?
Now, I hope you understand the limitations of escaping as well as the "dangerous characters" misconception.
It is to my understanding that using PDO/prepared statements is a much safer
Not really.
In fact, there are four different query parts which we can add to it dynamically:
a string
a number
an identifier
a syntax keyword.
so, you can see that escaping covers only one issue. (but of course, if you treat numbers as strings (putting them in quotes), when applicable, you can make them safe as well)
while prepared statements cover - ugh - whole 2 isues! A big deal ;-)
For the other 2 issues see my earlier answer, In PHP when submitting strings to the database should I take care of illegal characters using htmlspecialchars() or use a regular expression?
Now, function names are different so no longer will my mysql_query, mysql_fetch_array, mysql_num_rows etc work.
That is another, grave delusion of PHP users, a natural disaster, a catastrophe:
Even when utilizing old mysql driver, one should never use bare API functions in their code! One have to put them in some library function for the everyday usage! (Not as a some magic rite but just to make the code shorter, less repetitive, error-proof, more consistent and readable).
The same goes for the PDO as well!
Now on with your question again.
but by using them does this eliminate the need to use something like mysql_real_escape_string?
YES.
But I think this is roughly the idea of what should be done to fetch a user from a database
Not to fetch, but to add a whatever data to the query!
you have to given a length after PDO:PARAM_STR if I'm not mistaken
You can, but you don't have to.
Now, is this all safe?
In terms of database safety there are just no weak spots in this code. Nothing to secure here.
for the displaying security - just search this site for the XSS keyword.
Hope I shed some light on the matter.
BTW, for the long inserts you can make some use of the function I wrote someday, Insert/update helper function using PDO
However, I am not using prepared statements at the moment, as I prefer my home-brewed placeholders over them, utilizing a library I mentioned above. So, to counter the code posted by the riha below, it would be as short as these 2 lines:
$sql = 'SELECT * FROM `users` WHERE `name`=?s AND `type`=?s AND `active`=?i';
$data = $db->getRow($sql,$_GET['name'],'admin',1);
But of course you can have the same code using prepared statements as well.
* (yes I am aware of the Schiflett's scaring tales)
I never bother with bindParam() or param types or lengths.
I just pass an array of parameter values to execute(), like this:
$stmt = $dbh->prepare("SELECT * FROM `users` WHERE `id` = :user_id");
$stmt->execute( array(':user_id' => $user_id) );
$stmt = $dbh->prepare("INSERT INTO `users` (username, email)
VALUES (:username, :email)");
$stmt->execute( array(':username'=>$username, ':email'=>$email) );
This is just as effective, and easier to code.
You may also be interested in my presentation SQL Injection Myths and Fallacies, or my book SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
Yes, :something is a named placeholder in PDO, ? is an anonymous placeholder. They allow you to either bind values one by one or all at once.
So, basically that makes four options to provide your query with values.
One by one with bindValue()
This binds a concrete value to your placeholder as soon as you call it. You may even bind hard coded strings like bindValue(':something', 'foo') if desired.
Providing a parameter type is optional (but suggested). However, since the default is PDO::PARAM_STR, you only need to specify it when it is not a string. Also, PDO will take care of the length here - there is no length parameter.
$sql = '
SELECT *
FROM `users`
WHERE
`name` LIKE :name
AND `type` = :type
AND `active` = :active
';
$stm = $db->prepare($sql);
$stm->bindValue(':name', $_GET['name']); // PDO::PARAM_STR is the default and can be omitted.
$stm->bindValue(':type', 'admin'); // This is not possible with bindParam().
$stm->bindValue(':active', 1, PDO::PARAM_INT);
$stm->execute();
...
I usually prefer this approach. I find it the cleanest and most flexible.
One by one with bindParam()
A variable is bound to your placeholder that will be read when the query is executed, NOT when bindParam() is called. That may or may not be what you want. It comes in handy when you want to repeatedly execute your query with different values.
$sql = 'SELECT * FROM `users` WHERE `id` = :id';
$stm = $db->prepare($sql);
$id = 0;
$stm->bindParam(':id', $id, PDO::PARAM_INT);
$userids = array(2, 7, 8, 9, 10);
foreach ($userids as $userid) {
$id = $userid;
$stm->execute();
...
}
You only prepare and bind once which safes CPU cycles. :)
All at once with named placeholders
You just drop in an array to execute(). Each key is a named placeholder in your query (see Bill Karwins answer). The order of the array is not important.
On a side note: With this approach you cannot provide PDO with data type hints (PDO::PARAM_INT etc.). AFAIK, PDO tries to guess.
All at once with anonymous placeholders
You also drop in an array to execute(), but it is numerically indexed (has no string keys). The values will replace your anonymous placeholders one by one in the order they appear in your query/array - first array value replaces first placeholder and so forth. See erm410's answer.
As with the array and named placeholders, you cannot provide data type hints.
What they have in common
All of those require you to bind/provide as much values as you have
placeholders. If you bind too many/few, PDO will eat your children.
You don't have to take care about escaping, PDO handles that. Prepared PDO statements are SQL injection safe by design. However, that's not true for exec() and query() - you should generally only use those two for hardcoded queries.
Also be aware that PDO throws exceptions. Those could reveal potentially sensitive information to the user. You should at least put your initial PDO setup in a try/catch block!
If you don't want it to throw Exceptions later on, you can set the error mode to warning.
try {
$db = new PDO(...);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING)
} catch (PDOException $e) {
echo 'Oops, something went wrong with the database connection.';
}
To answer the length question, specifying it is optional unless the param you are binding is an OUT parameter from a stored procedure, so in most cases you can safely omit it.
As far as safety goes, escaping is done behind the scenes when you bind the parameters. This is possible because you had to create a database connection when you created the object. You are also protected from SQL injection attacks since by preparing the statement, you are telling your database the format of the statement before user input can get anywhere near to it. An example:
$id = '1; MALICIOUS second STATEMENT';
mysql_query("SELECT * FROM `users` WHERE `id` = $id"); /* selects user with id 1
and the executes the
malicious second statement */
$stmt = $pdo->prepare("SELECT * FROM `users` WHERE `id` = ?") /* Tells DB to expect a
single statement with
a single parameter */
$stmt->execute(array($id)); /* selects user with id '1; MALICIOUS second
STATEMENT' i.e. returns empty set. */
Thus, in terms of safety, your examples above seem fine.
Finally, I agree that binding parameters individually is tedious and is just as effectively done with an array passed to PDOStatement->execute() (see http://www.php.net/manual/en/pdostatement.execute.php).
I've seen this code that's been floating around, and also the fixed? version. Basically I've gotten this to work:
mysql_connect("host","client_name","client_pw");
mysql_select_db("database");
$q=mysql_query("SELECT * FROM table");
while($e=mysql_fetch_assoc($q))
$output[]=$e;
print(json_encode($output));
mysql_close();
but for some reason I feel it should be in mysqli. I'm new, and tried to write an equivalent mysqli OO code:
$mysqli = new mysqli("host", "client_name", "client_pw");
$mysqli->select_db("database");
$q = "SELECT * FROM table";
while($e=$mysqli->fetch_assoc($q))
$output[]=$e;
print(json_encode($output));
mysql_close();
It fails. I've tried other combinations, such as preparing a query and executing it, and setting that as $e, but all fail.
Do I have to manually build the array for the json_encode or something?
Maybe a better question is why I want to reinvent the wheel, but this has been bothering me.
Ah, I see you are not one with the database. Let us perform an exercise.
Close your eyes, breathe in, breathe out.
Relax.
You are one with the database.
You are one with the code.
Repeat after me.
Prepare.
Bind.
Execute.
Repeat it.
Again.
This is your new mantra, my friend.
You've accidentally skipped a step in your existing code. Let's throw it out and start over.
I am going to show you how to use PDO, one of the better ways PHP has to communicate with a database. It's less convoluted than the mysqli extension.
// Make sure these variables contain the correct data.
$pdo = new PDO("mysql:host=$hostname;dbname=$database", $username, $password);
// Ask PDO to throw exceptions instead of warnings.
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Here's our SQL. We're getting back a PDOStatement object here.
$sh = $pdo->prepare('SELECT * FROM Foo WHERE bar = ?');
// That question mark is a placeholder. bindValue lets us replace the question mark
// with the specified data. This is called a prepared statement. The end result is
// *complete and total immunity* from SQL Injection, if performed correctly.
$sh->bindValue(1, "I'm looking for a bar that is equal to this.");
// Okay, we've bound everything, let's run the query.
$sh->execute();
// And assuming there are no errors (note my severe lack of error handling),
// we should now have our complete list of data from the database.
print_r($sh->fetchAll(PDO::FETCH_ASSOC));
// Alternatively, we could pass bound variables as an array to execute:
$sh = $pdo->prepare('SELECT * FROM Foo WHERE bar = ?');
$sh->execute(array( "I'm a bar!" ));
// And of course, we can use variables in the binding...
$bar = 746;
$sh = $pdo->prepare('SELECT * FROM Foo WHERE bar = ?');
$sh->bindValue(1, $bar);
$sh->execute();
PDO's support for prepared statements and placeholders makes it one of the best choices for database access in modern PHP.
(mysqli also has access to prepared statements, but it forces you to also bind result variables, and that can be damned awkward under a lot of circumstances.)
fetchAll(PDO::FETCH_ASSOC) returns a multidimensional array. The outer array is numerically indexed, each value being a row. Each row is a string-keyed array, where the keys are column names and the values are the data from the database. There are a few other things that fetchAll can do, though I haven't found many of them to be useful. You can also fetch one row at a time
You can probably pass the results directly to json_encode, if you'd like, and not suffer too many problems.
Understand that you will want to add appropriate error detection to this code. I have omitted it here for brevity.
try
{
$db = new mysqli("your_host_ip", "your_username", "your_pass", "your_db", 3306);
if ($db->connect_errno) throw new exception(sprintf("Could not connect: %s", $db->connect_error));
$sqlCmd = "select * from users order by username";
$result = $db->query($sqlCmd);
if(!$result) throw new exception(sprintf("Invalid query : %s", $sqlCmd));
...
$q=mysql_query("SELECT * FROM table");
Here is how to do it with mysqli OOP
After the line $q= etc. -add the following code..
<?php
$result=$mysqli->query($q);
while($e=$result->fetch_assoc()){
$output[]=$e;
}
print(json_encode($output));
?>
This is a terrible question because I don't have a simple way to reproduce it. However, I'm using the Zend Framework to connect to my MySQL database on OS X. Sometimes a call to the prepare function on a mysqli object returns null. The stated return values for the prepare function are false or a statement object.
I can't figure out where else to look for info on why the prepare statement is failing. Is there any way to get visibility into the prepare process to see why it is failing? All of my problems are coming up while a transaction is open.
Sorry for the lack of specifics, but I really can't nail down why this is happening.
Just to correct ToughPal, you should be using:
mysqli_query($db, "INSERT INTO table (variable1, variable2) VALUES (hello, mynameis);
Remember that you need to have the db connection defined and stated in the query first, before your actual SQL.
Remember to enclose the table name, column names and value data in backtick escapes.
Example prepared statement
$result = $db->query( 'INSERT INTO server (key, value) VALUES (:key, :value)',
array('key' => $foo, 'value' => $bar)
Can you let us know your DB query?
Try and execute your DB query with test data and see if the query works fine to start with. If the query is ok then we can look why the code fails.
Well I managed to find the issue over the weekend but was really only able to fix the symptoms and not the cause.
I didn't include any SQL in the original issue because the problem was happening randomly, the same code would sometimes work and sometimes not. The issue looks like it was a memory pointer problem. Whenever I had a problem Zend Debugger told me that I had a mysqli object. I believe this because otherwise I would've gotten an error when trying to run the prepare function on it. I have a singleton object that acts as a container for my mysqli connection but whenever the prepare function failed, === showed that the mysqli being used was not the same as the mysqli connection in my singleton object.
In the end, Zend Framework's only issue is that it doesn't fail if the the prepare function returns null. If you are seeing this problem use === to verify that the connection is actually the same as the one that you've previously initiated.
if you're doing something like this
$mysqli = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
$query = "...";
$mysqli->prepare($query);
then you can inspect mysqli::$error next to see useful errors about why prepare() failed
print_r($mysqli->error);