The following code returns me people with similar telephone numbers. It works perfectly but when there are no numbers the function still returns information meaning that I cannot check hide a certain box if there are no other people with similar numbers.
THE FUNCTION
function getothers($tid,$criteria,$telephone,$telephone2,$elector){
global $dbh;
$tid = '-TID'.$tid;
$sql = "SELECT * FROM electors WHERE ((telephone > 0 AND telephone IN ('$telephone','$telephone2')) OR (telephone2 > 0 AND telephone2 IN ('$telephone','$telephone2'))) $criteria AND records NOT RLIKE '$tid' AND ID != '$elector' LIMIT 10";
$result = $dbh->query($sql);
return $result;
}
THE CALL
<?php $others = getothers($post['TID'],$post['criteria'],$elector['telephone'],$elector['telephone2'],$elector['ID']); ?>
THE LINE THAT DOES NOT WORK
<?php if(!$others){?>
$others still has something in it despite no results. I think I might be missing a line in y PDO. Any ideas?
The print_r
PDOStatement Object ( [queryString] => SELECT * FROM electors WHERE ((telephone > 0 AND telephone IN ('02085414023 ','')) OR (telephone2 > 0 AND telephone2 IN ('02085414023 ',''))) AND (this_vi_street = '' AND this_vi_telephone = '') AND (mosaic IN ('A01','A02','A03','A04','A05','A07','B11','C15','C16','C17','C18','H46','J52','K57','K58','K60') OR last_vi IN ('C','P')) AND postal_vote != 1 AND records NOT RLIKE '-TID1' AND ID != '13' LIMIT 10 )
As per the comments, a version using prepared statements:
function getothers($tid, $criteria, $telephone, $telephone2, $elector) {
global $dbh;
$stmt = $dbh->prepare("SELECT *
FROM electors
WHERE ((telephone > 0 AND telephone IN (:telephone, :telephone2))
OR (telephone2 > 0 AND telephone2 IN (:telephone, :telephone2)))
$criteria
AND records NOT RLIKE :tid
AND ID != :elector
LIMIT 10";
$stmt->execute(array(
':telephone' => $telephone,
':telephone2' => $telephone2,
':tid' => '-TID' . $tid,
':elector' => $elector
));
return $stmt->fetchAll();
}
There are still some bad points in this code:
Uses global to get the DB connection, this is overall bad application structure. You should probably use a class or pass $dbh as a regular argument into the function.
Concatenates $criteria into the prepared statement. Do you really need such dynamic conditions that you can't prepare a query for it without concatenating whole SQL blocks into it?
Doesn't necessarily address your actual problem of function returns.
Maybe do something like
$result = $dbh->query($sql);
if($result->rowCount()>0)
{
return $result;
}
return false;
Related
Hi i am trying to check if the IP is in the blocked list or not.
I am using SQLite3 in PHP. my problem is when trying to check with the function bellow it returns always true.
function isBlocked($ip){
global $pdo;
$check = $pdo->prepare("SELECT * FROM blockedUsers WHERE ip='".$ip."'");
$check->execute();
if($check->rowCount() >= 1){
return true;
}else{
return false;
}
}
Use SELECT COUNT(*) rather than SELECT *, since you don't care about the row data, just the count.
Also, use a parameter rather than substituting the variable into the SQL.
function isBlocked($ip){
global $pdo;
$check = $pdo->prepare("SELECT COUNT(*) AS count FROM blockedUsers WHERE ip=:ip");
$check->execute([':ip' => $ip]);
$row = $check->fetch(PDO::FETCH_ASSOC);
return $row['count'] > 0;
}
I have to process a few queries using a loop and all queries are the same, except for one which doesn't use the parameter the others do:
$queries = array(
'query1' = "SELECT * FROM table_1 WHERE id=:id",
'query2' = "SELECT * FROM table_2 WHERE id=:id",
'query3' = "SELECT * FROM table_3"
);
$params = array(':id',1);
foreach($queries as $q) {
$st = $pdo->prepare($q);
if($st->execute($params)) {
// do stuff with results
} else {
echo json_encode($st->errorInfo());
}
}
The problem here is that $st->execute($params) will not work on the query with no parameters defined, which is why I would like to know if it is possible to analyze the query before sending it.
This is fake code, and it should work regardless of the query structure as long as there is one parameter (:id) or none.
UPDATE, SOLVED:
How I applied the solution given by #Jonast92:
foreach($queries as $q) {
$st = $pdo->prepare($q);
if($st->execute(substr_count($q,":") > 0 ? $params : null)) {
// do stuff with results
} else {
echo json_encode($st->errorInfo());
}
}
You can use substr_count to count the number of : occurring, indicating the number of arguments to be executed onto the prepared statement.
$itemInArray = "SELECT * FROM table_1 WHERE id=:id";
$count = substr_count($itemInArray, ':'); // 1
so this has been driving me nuts for the last two hours...
$sql = 'SELECT * FROM master_rets_table
WHERE listing_entry_timestamp > "2014-04-01 00:00:00"
ORDER BY listing_price DESC';
}
$this->stm = $this->prepare($sql);
$this->stm->execute();
Running this query outside PDO in Navicat works fine ( i get 17 records), but no matter how I try to change this up, it is simply not working in PDO. I'm using this same code for lots of PDO queries that do not use a date constraint, and they all work wonderfully.
fyi, listing_entry_timestamp is a DateTime field
EDIT - here is the whole code block
if ($this->city != "" ) {
$sql = 'SELECT * FROM master_rets_table
WHERE city = '.$this->city.'
ORDER BY listing_price DESC';
}
else if ($this->subdiv != ""){
$sql = 'SELECT * FROM master_rets_table
WHERE subdivision REGEXP '.$this->subdiv.'
ORDER BY listing_price DESC';
}
else if ($this->date_from != "") {
$sql = "SELECT * FROM master_rets_table WHERE (listing_entry_timestamp > '2014-04-01') ORDER BY listing_price DESC";
}
$this->stm = $this->prepare($sql);
$this->stm->execute();
$this->rows= $this->stm->fetchAll(PDO::FETCH_ASSOC);
$this->count = $this->stm->rowCount();
$this->rowIdx = 0;
it is working fine for the other two cases...it seems to have something to do with the datetime field or something.
EDIT [SOLUTION] - Well, it turns out that the live data wasn't being pushed to the development server and i was testing it in Navicat against the live data, so the query looking for yesterday's data couldn't find anything.
PDO has nothing to do with date fields.
It works with dates all right.
So, look for bugs in your code/data base/environment/whatever.
My bet for the different databases. One you connect with navicat contains 17 records to match and one you connect with PDO contains nones.
Second guess is some wrong code in your db class.
What is driving me nuts is pointless suggestions with wild guesses.
O.K.
mysql
mysql> create table so_again(d datetime);
Query OK, 0 rows affected (0.23 sec)
mysql> insert into so_again values (now());
Query OK, 1 row affected (0.08 sec)
mysql> select * from so_again where d > '2014-04-01 00:00:00';
+---------------------+
| d |
+---------------------+
| 2014-04-02 00:23:16 |
+---------------------+
1 row in set (0.02 sec)
PHP
$sql = 'select * from so_again where d > "2014-04-01 00:00:00"';
$stm = $pdo->prepare($sql);
$stm->execute();
$stm = $stm->fetchAll();
var_dump($stm);
ouptut
array(1) {
[0]=>
array(1) {
["d"]=>
string(19) "2014-04-02 00:23:16"
}
}
It works.
at least with raw PDO.
there is no problem with SQL
there is no problem with PDO
there is no problem with date
there is no problem with quotes (as long as we take 17 returned results for granted. however, in ANSI mode they will cause an error)
There can be only problem with your own code/data/input
Try this:
$sql = "SELECT * FROM master_rets_table WHERE listing_entry_timestamp > :date ORDER BY listing_price DESC";
$statement = $this->prepare($sql);
$statement->bindParam (":date", "2014-04-01 00:00:00");
$statement->execute();
The only two things that come to my mind are that you might have some kind of casting error in the date parameter, or a logic path error. I also noticed that you treat $city as if it was a string, yet you do not quote it.
Try with:
if ($this->city != "" ) {
$where = 'city = ?';
$match = $this->city;
} else if ($this->subdiv != "") {
$where = 'subdivision REGEXP ?';
$match = $this->subdiv;
} else if ($this->date_from != "") {
$where = 'listing_entry_timestamp > ?';
$match = 20140401;
} else {
trigger_error("No search", E_USER_ERROR);
}
$sql = "SELECT * FROM master_rets_table WHERE {$where} ORDER BY listing_price DESC";
$this->stm = $this->prepare($sql);
$this->stm->execute(array($match));
$this->rows = $this->stm->fetchAll(PDO::FETCH_ASSOC);
$this->count = $this->stm->rowCount();
$this->rowIdx = 0;
This should both supply a 'recognizable' parameter to PDO and ensure that $sql is indeed populated.
Another possibility
You say,
$city is preformatted with PDO::quote()...thats an old habit of mine
If it were so, let's suppose that $this->city is initially empty.
Then PDO::quote would make it into an empty quoted string: i.e. a pair of quote marks with nothing inside.
Then, the comparison $this->city != "" would always return true, because city is not "", but "''".
And so the SQL would run a different query from what you'd expect.
can you tell me the output when you execute this modified query:
$sql = 'SELECT * FROM master_rets_table
WHERE (listing_entry_timestamp > :entry_date)
ORDER BY listing_price DESC';
}
$this->stm = $this->prepare($sql);
$this->stm->execute(array("entry_date"=>"2014-04-01 00:00:00"));
This query returns 5 results in phpMyAdmin:
SELECT * FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT 0,5
And this returns count=12 in phpMyAdmin (which is fine, because there are 12 records):
SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT 0,5
This function was working fine before I added the two variables (offset, display), however now it doesn't work, and printing the variables out gives me offset=0, display=5 (so still LIMIT 0,5).
function getProducts($offset, $display) {
$sql = "SELECT * FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT ?,?;";
$data = array((int)$offset, (int)$display);
$rows = dbRowsCount($sql, $data);
logErrors("getProducts(".$offset.",".$display.") returned ".$rows." rows.");
if ($rows > 0) {
dbQuery($sql, $data);
return dbFetchAll();
} else {
return null;
}
}
It's not working because my dbRowsCount(...) method was returning empty string (stupid PDOStatement::fetchColumn), so I changed it to return the count with PDO::FETCH_ASSOC and it returns count=0.
Here is the function which does the row-count:
function dbRowsCount($sql, $data) {
global $db, $query;
$regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/is';
if (preg_match($regex, $sql, $output) > 0) {
$query = $db->prepare("SELECT COUNT(*) AS count FROM {$output[1]}");
logErrors("Regex output: "."SELECT COUNT(*) AS count FROM {$output[1]}");
$query->setFetchMode(PDO::FETCH_ASSOC);
if ($data != null) $query->execute($data); else $query->execute();
if (!$query) {
echo "Oops! There was an error: PDOStatement returned false.";
exit;
}
$result = $query->fetch();
return (int)$result["count"];
} else {
logErrors("Regex did not match: ".$sql);
}
return -1;
}
My error log gives me this output from the program:
Regex output: SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT ?,?;
getProducts(0,5) returned 0 rows.
As you can see, the SQL has not been malformed, and the method input variables were 0 and 5 as expected.
Does anyone know what has gone wrong?
Update
Following a suggestion, I did try to execute the query directly, and it returned the correct result:
function dbDebugTest() {
global $db;
$stmt = $db->query("SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update LIMIT 0,5;");
$result = $stmt->fetch();
$rows = (int)$result["count"];
logErrors("dbDebugTest() returned rows=".$rows);
}
Output:
> dbDebugTest() returned rows=12
Following another suggestion, I changed !=null to !==null, and I also printed out the $data array:
logErrors("Data: ".implode(",",$data));
if ($data !== null) $query->execute($data); else $query->execute();
Output:
> Data: 0,5
However, the dbRowsCount($sql, $data) still returns 0 rows for this query!
Update 2
Following advice to implement a custom PDOStatement class which would allow me to output the query after the values have been binded, I found that the function would stop after $query->execute($data) and so the output would not be printed, although the custom class works for every other query in my program.
Updated code:
function dbRowsCount($sql, $data) {
global $db, $query;
$regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/is';
if (preg_match($regex, $sql, $output) > 0) {
$query = $db->prepare("SELECT COUNT(*) AS count FROM {$output[1]}");
logErrors("Regex output: "."SELECT COUNT(*) AS count FROM {$output[1]}");
$query->setFetchMode(PDO::FETCH_ASSOC);
logErrors("Data: ".implode(",",$data));
$query->execute($data);
logErrors("queryString:".$query->queryString);
logErrors("_debugQuery():".$query->_debugQuery());
if (!$query) {
echo "Oops! There was an error: PDOStatement returned false.";
exit;
}
$result = $query->fetch();
return (int)$result["count"];
} else {
logErrors("Regex did not match: ".$sql);
}
return -1;
}
Output:
Regex output: SELECT COUNT() AS count FROM tbl_product_category WHERE id=?;
Data: 5
queryString:SELECT COUNT() AS count FROM tbl_product_category WHERE id=?;
_debugQuery():SELECT COUNT(*) AS count FROM tbl_product_category WHERE id=?;
Regex output: SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT ?,?;
Data: 0,5
// function stopped and _debugQuery couldn't be output
Update 3
Since I couldn't get the custom PDOStatement class to give me some output, I thought I'd rewrite the getProducts(...) class to bind the params with named placeholders instead.
function getProducts($offset, $display) {
$sql = "SELECT * FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT :offset, :display;";
$data = array(':offset'=>$offset, ':display'=>$display);
$rows = dbRowsCount($sql, $data);
logErrors("getProducts(".$offset.",".$display.") returned ".$rows." rows.");
if ($rows > 0) {
dbQuery($sql, $data);
return dbFetchAll();
} else {
return null;
}
}
Output:
Regex output: SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update DESC LIMIT :offset, :display;
Data: 0,5
// Still crashes after $query->execute($data) and so logErrors("getProducts(".$offset."...)) wasn't printed out
Update 4
This dbDebugTest previously worked with declaring the limit values 0,5 directly in the SQL string. Now I've updated it to bind the parameters properly:
function dbDebugTest($offset, $display) {
logErrors("Beginning dbDebugTest()");
global $db;
$stmt = $db->prepare("SELECT COUNT(*) AS count FROM tbl_product WHERE 1 ORDER BY last_update LIMIT :offset,:display;");
$stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
$stmt->bindParam(':display', $display, PDO::PARAM_INT);
if ($stmt->execute()) {
$result = $stmt->fetch();
$rows = (int)$result["count"];
logErrors("dbDebugTest() returned rows=".$rows);
} else {
logErrors("dbDebugTest() failed!");
}
}
The function crashes, and only this is output:
Beginning dbDebugTest()
Update 5
Following a suggestion to turn errors on (they are off by default), I did this:
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
That itself, made the dbDebugTest() from Update 4 work!
Beginning dbDebugTest()
dbDebugTest() returned rows=12
And now an error is generated in my webserver logs:
[warn] mod_fcgid: stderr: PHP Fatal error:
Uncaught exception 'PDOException' with message '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 ''0', '5'' at line 1'
in /home/linweb09/b/example.com-1050548206/user/my_program/database/dal.php:36
Line 36 refers to dbRowsCount(...) method and the line is $query->execute($data).
So the other method getProducts(...) still doesn't work because it uses this method of binding the data and the params turn into ''0' and '5'' (is this a bug?). A bit annoying but I'll have to create a new method in my dal.php to allow myself to bind the params in the stricter way - with bindParam.
Thanks especially to #Travesty3 and #eggyal for their help!! Much, much appreciated.
Based on Update 2 in the question, where execution stops after the execute statement, it looks like the query is failing. After looking at some PDO documentation, it looks like the default error handling setting is PDO::ERRMODE_SILENT, which would result in the behavior you are seeing.
This is most likely due to the numbers in your LIMIT clause being put into single-quotes when passed in as parameters, as was happening in this post.
The solution to that post was to specify the parameters as integers, using the bindValue method. So you will probably have to do something similar.
And it looks like you should also be executing your queries with try-catch blocks in order to catch the MySQL error.
bindValue method:
if ($data !== null)
{
for ($i=0; $i<count($data); $i++)
$query->bindValue($i+1, $data[$i], PDO::PARAM_INT);
$query->execute($data);
}
else
$query->execute();
You're testing to see if $data is NULL with the equality, rather than the identity, operator (see the PHP manual for more information on how NULL values are handled by the various comparison operators). You need to either use the identity test === / !==, or else call is_null().
As #Travesty3 mentioned above, to test whether an array is empty, use empty().
ihave created a simple project to help me get to grips with php and mysql, but have run into a minor issue, i have a working solution but would like to understand why i cannot run this code successfully this way, ill explain:
i have a function,
function fetch_all_movies(){
global $connection;
$query = 'select distinct * FROM `'.TABLE_MOVIE.'` ORDER BY movieName ASC';
$stmt = mysqli_prepare($connection,$query);
mysqli_execute($stmt);
mysqli_stmt_bind_result($stmt,$id,$name,$genre,$date,$year);
while(mysqli_stmt_fetch($stmt)){
$editUrl = "index.php?a=editMovie&movieId=".$id."";
$delUrl = "index.php?a=delMovie&movieId=".$id."";
echo "<tr><td>".$id."</td><td>".$name."</td><td>".$date."</td><td>".get_actors($id)."</td><td>Edit | Delete</td></tr>";
}
}
this fetches all the movies in my db, then i wish to get the count of actors for each film, so i pass in the get_actors($id) function which gets the movie id and then gives me the count of how many actors are realted to a film.
here is the function for that:
function get_actors($movieId){
global $connection;
$query = 'SELECT DISTINCT COUNT(*) FROM `'.TABLE_ACTORS.'` WHERE movieId = "'.$movieId.'"';
$result = mysqli_query($connection,$query);
$row = mysqli_fetch_array($result);
return $row[0];
}
the functions both work perfect when called separately, i just would like to understand when i pass the function inside a function i get this warning:
Warning: mysqli_fetch_array() expects
parameter 1 to be mysqli_result,
boolean given in
/Applications/MAMP/htdocs/movie_db/includes/functions.inc.php
on line 287
could anyone help me understand why?
many thanks.
mysqli_query failed to run your query:
Returns FALSE on failure. For
successful SELECT, SHOW, DESCRIBE or
EXPLAIN queries mysqli_query() will
return a result object. For other
successful queries mysqli_query() will
return TRUE.
Before running mysqli_fetch_array test $result... Something like:
if ($result !== false)
$row = mysqli_fetch_array($result);
else
return false;
Seems like a variable scope issue within your SQL statement. Outputting the SQL should show you the "true" error.
You may want to try using classes with your functions, for example:
class getInfo {
function fetch_all_movies(){
global $connection;
$query = 'select distinct * FROM `'.TABLE_MOVIE.'` ORDER BY movieName ASC';
$stmt = mysqli_prepare($connection,$query);
mysqli_execute($stmt);
mysqli_stmt_bind_result($stmt,$id,$name,$genre,$date,$year);
while(mysqli_stmt_fetch($stmt)){
$editUrl = "index.php?a=editMovie&movieId=".$id."";
$delUrl = "index.php?a=delMovie&movieId=".$id."";
echo "<tr><td>".$id."</td><td>".$name."</td><td>".$date."</td><td>".get_actors($id)."</td><td>Edit | Delete</td></tr>";
}
}
function get_actors($movieId){
global $connection;
$query = 'SELECT DISTINCT COUNT(*) FROM `'.TABLE_ACTORS.'` WHERE movieId = "'.$movieId.'"';
$result = mysqli_query($connection,$query);
$row = mysqli_fetch_array($result);
return $row[0];
}
}
$showInfo = new getInfo;
//fetch all movies
$showInfo->fetch_all_movies();
//List actors from movie 1
$showInfo->get_actors("1");
In case of an error mysqli_query will return false. You have to handle the error a simple way to do this might be:
$result = mysqli_query($connection,$query);
if (!$result) {
die(mysqli_error($connection));
}
$row = mysqli_fetch_array($result);
Please note that terminating (by doing die() ) usually is no good way to react on an error, log it, give the user anice error page etc. It's alsonogood practice to give the low level error message toauser, this might motivate users to try to exploit a possile security issue.
Last remark: you were writing
$query = 'SELECT DISTINCT COUNT(*) FROM `'.TABLE_ACTORS.'` WHERE movieId = "'.$movieId.'"';
you should properly escape the movieId there.