I am learning the Zend Framework. Now I need to attach a prepared statement to prevent SQL injection in the Zend Framework.
So I am sharing some functions here which I am using so if you can tell me how I can attach a prepared statement in these Zend Database Query Functions it will be helpful.
public function getRowByID($id) {
$row = $this->fetchRow("id = '$id'");
if (!$row) {
return false;
}
return $row;
}
public function getbyProjectID($projectid) {
$query = "SELECT * FROM auth where projectid = '$projectid'";
$result = $this->getAdapter()->query($query);
return $result->fetchAll();
}
public function updateRowByUserProject($username, $projectid) {
$query = "UPDATE auth SET iscurrent=0 WHERE username = '$username'";
$result = $this->getAdapter()->query($query);
$query1 = "UPDATE auth SET iscurrent=1 WHERE username = '$username' AND projectid = '$projectid'";
$result1 = $this->getAdapter()->query($query1);
$affectedRow = $result1->rowCount();
if($affectedRow == 1){
return true;
}else{
return false;
}
}
For fetching you could use Zend_Db_Select Class methods for preparing a query and executing it, passing variables in questionmark places (placeholders which will be escaped from special characters) after comma (possible multiple questionmarks, passing variables from left to right):
public function getRowByID($id) {
$table = $this->getTable();
$select = $table->select();
$select->where('id = ?', $id);
$row = $table->fetchRow($select);
if (!$row) {
return false;
}
return $row;
}
For your second method getByProjectId() it depends if you are in proper model (like Auth_Model_Auth) or you want to access data from another table
public function getbyProjectID($projectid) {
$table = $this->getTable();
$select = $table->select();
$select->where('projectid = ?', $projectid);
$result = $table->fetchAll($select);
return $result;
}
And for updating you can pass an array to 'update' method in same style like for fetching data. Key of an array must be a column name of your table.
public function updateRowByUserProject($username, $projectid) {
$table = $this->getTable();
// Data you want to insert/update
$data = [
'iscurrent' => 0
];
// Where you want to update it
$where = [
'username = ?' => $username
]
$result = $table->update($data, $where);
$data1 = [
'iscurrent' => 1
]
$where1 = [
'username = ?' => $username,
'projectid = ?' => $projectid
]
$result1 = $table->update($data1, $where1);
}
EDIT:
For both questions from comments you could achieve this by using quoteInto method, which also escapes data from special chars.
In first case you prepare a $where variable, which contains what record you want to delete:
$table = $this->getTable();
$where = $table->getAdapter()->quoteInto('projectid = ?', $projectid);
$isDeleted = $table->delete($where);
In second case you can do exactly the same:
$query = "SELECT COUNT(*) AS total FROM applications WHERE projectid IN (SELECT projectid FROM auth WHERE projectid = ?)";
$query = $this->getAdapter()->quoteInto(?, $projectid):
...
But you should try to avoid writing big queries in one variable and then executing them. I would suggest you to get to know with this:
https://framework.zend.com/manual/1.11/en/zend.db.select.html
Really well explained how to use Zend methods for this purpose.
Related
Trying to get a function working to create simple CRUD "Select" with multiple parameters to any table. I think I got the hardest part, but couldn't fetch the data right now. Maybe I'm doing something wrong I can't figure out.
My prepared statement function:
function prepared_query($mysqli, $sql, $params, $types = ""){
$types = $types ?: str_repeat("s", count($params));
if($stmt = $mysqli->prepare($sql)) {
$stmt->bind_param($types, ...$params);
$stmt->execute();
return $stmt;
} else {
$error = $mysqli->errno . ' ' . $mysqli->error;
error_log($error);
}
}
The query creator:
function create_select_query($table, $condition = "", $sort = "", $order = " ASC ", $clause = ""){
$table = escape_mysql_identifier($table);
$query = "SELECT * FROM ".$table;
if(!empty($condition)){
$query .= create_select_query_where($condition,$clause);
}
if(!empty($sort)){
$query .= " ORDER BY ".$sort." $order";
}
return $query;
}
The helper function to create the WHERE clause:
function create_select_query_where($condition,$clause){
$query = " WHERE ";
if(is_array($condition)){
$pair = array();
$size = count($condition);
$i = 0;
if($size > 1){
foreach($condition as $field => $val){
$i++;
if($size-1 == $i){
$query .= $val." = ? ".$clause. " ";
}else{
$query .= $val." = ? ";
}
}
}else{
foreach($condition as $field => $val){
$query .= $val." = ? ";
}
}
}else if(is_string($condition)){
$query .= $condition;
}else{
$query = "";
}
return $query;
}
The select function itself:
function crud_select($conn, $table, $args, $sort, $order, $clause){
$sql = create_select_query($table, array_keys($args),$sort, $order, $clause);
print_r($sql);
if($stmt = prepared_query($conn, $sql, array_values($args))){
return $stmt;
}else{
$errors [] = "Something weird happened...";
}
}
When I create the query, it seems to be OK but can't fetch the data. If I create an array with only one argument the query translates into:
SELECT * FROM `teste_table` WHERE id = ?
If I create with multiple parameters, it turns like this:
SELECT * FROM `teste_table` WHERE id = ? AND username = ?
So, how can I properly fetch the data from the select. This should be used for multiple purposes, so I could get more than one result, so the best way would be fetch data as array I guess.
I guess I'm close, but can't figure it out. Thanks
I told you to limit your select function to a simple primary key lookup. And now you opened a can of worms. As a result you are getting entangled implementation code and unreadable application code.
$table, $args, $sort, $order, $clause
What all these variables are for? How you're going to call this function - a list of gibberish SQL stubs in a random order instead of plain and simple SQL string? And how to designate a list of columns to select? How to use JOINS? SQL functions? Aliases? Why can't you just write a single SQL statement right away? You already have a function for selects, though without this barbaric error reporting code you added to it:
function prepared_query($mysqli, $sql, $params, $types = ""){
$types = $types ?: str_repeat("s", count($params));
$stmt = $mysqli->prepare($sql)) {
$stmt->bind_param($types, ...$params);
$stmt->execute();
return $stmt;
}
Just stick to it and it will serve you all right.
$sql = "SELECT * FROM `teste_table` WHERE id = ? AND username = ?";
$stmt = prepared_query($mysqli, $sql, [$id, $name]);
$row = $stmt->get_result()->fetch_assoc();
The only specific select function could be, again, a simple primary key lookup:
function crud_find($conn, $table, $id)
{
$table = escape_mysql_identifier($table);
$sql = "SELECT * FROM $table WHERE id=?";
$stmt = prepared_query($conn, $sql, [$id], "i");
return $stmt->get_result()->fetch_assoc();
}
And for the everything else just use a generic function with native SQL.
I'm trying to fix this butchered bit of code - as you might have guessed, I'm cocking up the bind param syntax. In fact, I'm not even sure what I'm trying to do is even possible. Here's the class method...
/***
*
* #select values from table
*
* #access public
*
* #param string $table The name of the table
*
* #param array $fieldlist Fields to return in results, defaults null
*
* #param array $criteria Search criteria by keyed by fieldname
*
* #param int $limit Limit of records to return, defaults 10
*
* #return Array on success or throw PDOException on failure
*
*/
public function dbSearch($table, $fieldList = null, $criteria = null, $limit = 10)
{
// setup $this->db to point to a PDO instance
$this->conn();
// build fieldlist
if( is_null($fieldList) OR !is_array($fieldList) OR count($fieldList) == 0) {
$returnFields = '*';
} else {
$returnFields = "'".implode("', '", $fieldList)."'";
}
// build criteria
if( is_null($criteria) OR !is_array($criteria) OR count($criteria) == 0) {
$whereClause = '';
} else {
$whereClause = array();
foreach ($criteria as $key => $value){
$bind_name = 'bind_'.$key; //generate a name for bind1, bind2, bind3...
$$bind_name = $value; //create a variable with this name with value in it
$bind_names[] = & $$bind_name; //put a link to this variable in array
$whereClause[] = "'$key' = :$bind_name";
}
$whereClause = count($whereClause) > 0 ? ' WHERE '.implode( ' AND ' , $whereClause ) : '';
}
$sql = "SELECT $returnFields FROM '$table' $whereClause LIMIT $limit";
$stmt = $this->db->prepare($sql);
if( $whereClause != '') {
call_user_func_array(array(&$stmt, 'bindParam'), $bind_names);
}
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
... which at some point I want to call using something along these lines...
// look for users in database...
$user_recs = $crud->dbSearch('user', array('user_name'), array('user_name'=> $_POST['username']));
$users = $user_recs->fetchAll(PDO::FETCH_ASSOC);
How bonkers is this? Is it possible? Do I need to pass in the param types as well somehow? Any help gratefully received!
Actually, the problem was using bound parameters as opposed to bound values... doh!
Given an SQL statement and some values in an associative array, e.g.
$sql = "SELECT * FROM event
WHERE eventdate >= :from
AND eventdate <= :until
AND ( user_name LIKE :st OR site_name LIKE :st )
ORDER BY eventdate, start_time LIMIT 100";
$values = array( 'st' => '%'.$searchterm.'%',
'from' => $fromdate,
'until' => $untildate, );
then this class method ( but it could easily by a plain function) did the trick:
public function dbBoundQuery($sql, $values, $types = false) {
$this->conn();
$stmt = $this->db->prepare($sql);
foreach($values as $key => $value) {
if($types) {
$stmt->bindValue(":$key",$value,$types[$key]);
} else {
if(is_int($value)) { $param = PDO::PARAM_INT; }
elseif(is_bool($value)) { $param = PDO::PARAM_BOOL; }
elseif(is_null($value)) { $param = PDO::PARAM_NULL; }
elseif(is_string($value)) { $param = PDO::PARAM_STR; }
else { $param = FALSE;}
if($param) $stmt->bindValue(":$key",$value,$param);
}
}
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
Hope this helps someone else.
I really don't understand how this function is better than conventional
$stmt = $db->prepare("SELECT user_name FROM user WHERE user_name = ?");
$stmt->execute($_POST['username']);
$users = $stmt->fetchAll();
Mind you,
it keeps your query flexible. LIMIT ?,? is possible
it keeps your query readable. Almost natural English of SQL stays in place. You still can tell what does your query do, without the need of learning some brain-damaging language. As a side effect, any other developer can comprehend this code too.
Well, there are quite a few things that can go wrong with the code.
For once, I don't see any AND / OR between WHERE clauses - which is probably why it doesn't work.
Secondly, it doesn't allow you to use SQL functions. Let's say you need to write a query like this:
SELECT * FROM `table` WHERE UNIX_TIMESTAMP(date_added) < ...;
You get the idea.
I would suggest to either use an existing ORM (Doctrine, Propel, etc), or stick to PDO.
Here is an example of how I would use PDO for a User class:
class User
{
protected $data;
public function __get($key) {
return $this->data[$key];
}
public function __set($key, $value) {
$this->data[$key] = $value;
}
/**
* #param $value
* #param $field
* #return $this
*/
public function loadBy($value, $field)
{
$db = DbFactory::getInstance();
$query = "SELECT * FROM users WHERE $field = :$field LIMIT 1";
$stmt = $db->prepare($query);
$stmt->execute(array(":$field" => $value));
$result = $stmt->fetch();
foreach ($result as $key => $value) {
$this->$key = $value;
}
return $this;
}
}
You can create such functions for your entities, this you will have functions that are specialized, and efficient in what they do, and that are easy to test.
PS:
Ignore the issue that appears when you have a field named data :)
I have a selet function where I pass two variables. The table and where.
However, whatever I put for where it always displays id = 1.
Could this be because it is recognising that id has a value and setting it to 1 or am I completely off the mark here?
The function is below:
public function select($table, $where){
$sql = "SELECT * FROM $table WHERE $where";
$result = mysql_query($sql);
if(mysql_num_rows($result) == 1){
return $this->processRowSet($result, true);
}
return $this->processRowSet($result);
}
The call to the function is:
$rowSet = $db->select('users','username = wayne');
Or is it the way I am setting username in the parameter?
username is a String then it is necessary to write
$rowSet = $db->select('users','username = "wayne"');
I'm currently trying to fetch two images location from my database, how do I return both columns and if both empty then echo another image. This is what I've got so far.
How do I return both photo and photo_small so that I may echo them in a php file.
PUBLIC FUNCTION Profile_Pic($uiD) {
$sth = $this->db->prepare("SELECT photo,photo_small FROM users WHERE uiD = :id");
$sth->execute(array(':id' => $uiD));
if ($sth->rowCount() > 0) {
$data = $row['photo'];
return $data;
} else {
$data = './icons/users.png';
return $data;
}
}
PUBLIC FUNCTION Profile_Pic($uiD) {
$sql = "SELECT photo,photo_small FROM users WHERE uiD = ?";
$sth = $this->db->prepare($sql);
$sth->execute(array($uiD));
$data = $sth->fetch();
if (empty($data['photo'])) {
$data['photo'] = './icons/users.png';
}
if (empty($data['photo_small'])) {
$data['photo_small'] = './icons/users.png';
}
return $data;
}
if you want to replace both images if even one is absent
PUBLIC FUNCTION Profile_Pic($uiD) {
$sql = "SELECT photo,photo_small FROM users WHERE uiD = ?";
$sth = $this->db->prepare($sql);
$sth->execute(array($uiD));
$data = $sth->fetch();
if (empty($data['photo']) || empty($data['photo_small'])) {
$data['photo'] = './icons/users.png';
$data['photo_small'] = './icons/users.png';
}
return $data;
}
Just return all of the values you want in an array.
You can ensure that both photo and photo_small are not empty strings or NULL by using empty().
Don't forget to retrieve your row using PDOStatement::fetch().
You should not use rowCount() to determine the number of rows returned in a SELECT statement. According to the documentation for PDOStatement::rowCount():
For most databases, PDOStatement::rowCount() does not return the number of rows affected by a SELECT statement.
Try this:
$row = $sth->fetch(PDO::FETCH_ASSOC);
if ($row && !empty($row['photo']) && !empty($row['photo_small'])) {
$data = array('photo' => $row['photo'], 'photo_small' => $row['photo_small']);
return $data;
} else {
$data = array('photo' => './icons/users.png', 'photo_small' => './icons/users.png');
return $data;
}
Then when you call the function, your returned result can be used like this:
$uiD = 1;
$result = Profile_Pic($uiD);
$photo = $result['photo'];
$photo_small = $result['photo_small'];
I need to run a query in zend frawork. The query is as given below.
select * from users where role_id in (3,5) and emailID not in ('abc#abc.com', 'cba#cba.com');
I am passing the string
$whereString = role_id in (3,5) and emailID not in ('abc#abc.com', 'cba#cba.com');
to
$this->fetchList($whereString) // where $this is object of users
But fetchList() function executes only the first part i.e role_id in (3,5)
but not the second part i.e emailID not in ('abc#abc.com', 'cba#cba.com');
The result from fetchList() contains 'abc#abc.com' and 'cba#cba.com'
fetchList() is:
public function fetchList($where=null, $order=null, $count=null, $offset=null)
{
$resultSet = $this->getDbTable()->fetchAll($where, $order, $count, $offset);
$entries = array();
foreach ($resultSet as $row)
{
$entry = new Application_Model_Users();
$entry->setId($row->id)
->setName($row->name)
->setEmail($row->email)
$entries[] = $entry;
}
return $entries;
}
fetchAll():
/**
* Fetches all rows.
*
* Honors the Zend_Db_Adapter fetch mode.
*
* #param string|array|Zend_Db_Table_Select $where OPTIONAL An SQL WHERE clause or Zend_Db_Table_Select object.
* #param string|array $order OPTIONAL An SQL ORDER clause.
* #param int $count OPTIONAL An SQL LIMIT count.
* #param int $offset OPTIONAL An SQL LIMIT offset.
* #return Zend_Db_Table_Rowset_Abstract The row results per the Zend_Db_Adapter fetch mode.
*/
public function fetchAll($where = null, $order = null, $count = null, $offset = null)
{
if (!($where instanceof Zend_Db_Table_Select)) {
$select = $this->select();
if ($where !== null) {
$this->_where($select, $where);
}
if ($order !== null) {
$this->_order($select, $order);
}
if ($count !== null || $offset !== null) {
$select->limit($count, $offset);
}
} else {
$select = $where;
}
print_r($select);
$rows = $this->_fetch($select);
$data = array(
'table' => $this,
'data' => $rows,
'readOnly' => $select->isReadOnly(),
'rowClass' => $this->getRowClass(),
'stored' => true
);
$rowsetClass = $this->getRowsetClass();
if (!class_exists($rowsetClass)) {
require_once 'Zend/Loader.php';
Zend_Loader::loadClass($rowsetClass);
}
return new $rowsetClass($data);
}
Any body knows what wrong did I do.
I'm going out on a bit of limb, because I'm not sure. This feels like it might be a quote issue. Maybe try passing your query string like:
//use Zend_Db_Statement to process raw SQL.
//get default adapter
$db = Zend_Db_Table::getDefaultAdapter();
//write SQL statement
$sql = "select * from users where role_id in (3,5) and emailID not in ('abc#abc.com', 'cba#cba.com')";
//assuming MySql as DB instantiate object
$stmt = new Zend_Db_Statement_Mysqli($db, $sql);
//Execute SQL Statement accepts optional parameter for array() of values to be bound
$stmt->execute();
or you might like to build the query using the select() object:
//get default adapter
$db = Zend_Db_Table::getDefaultAdapter();
//instantiate the select() object
$select = new Zend_Db_Select($db);
$select->from('users');
//two where will build an AND query use $select->orWhere() to build an OR query
$select->where('role_id in (3,5)');
$select->where("emailID not in ('abc#abc.com', 'cba#cba.com')");
$result = $model->fetchList($select);
and it might work if $whereString were a valid PHP string, beacuse fetchAll() accepts a string or an instance of Zend_Db_Select:
$whereString = "role_id in (3,5) and emailID not in ('abc#abc.com', 'cba#cba.com')";
Hope this helps some... I took a shot.