I have a simple MySQL stored procedure that takes two parameters and inserts a row into a table. I can execute it just fine from Zend Framework 2 like this:
$result = $this->dbAdapter->query('CALL sp_register_user(?, ?)', array('username', 'password'));
I can also access any result sets returned from my stored procedure.
What I want now is to have an output value from my stored procedure as a third parameter, so something like this:
DELIMITER //
CREATE PROCEDURE sp_register_user(IN username VARCHAR(50), IN password VARCHAR(128), OUT code INTEGER)
NOT DETERMINISTIC
COMMENT 'Registers a user'
BEGIN
INSERT INTO user VALUES (username, password);
SET code = 123;
END //
The question is how I can access this output variable from PHP (ZF2). I have only been able to find examples of how to do it directly through PDO, which I am using. Example 4 on this page shows how to do it through PDO directly. My concern is that if I use the PDO object directly, I am losing some abstractions and I am thereby assuming that I will always be using PDO.
Still, I tried to make it work with PDO directly, like this:
$username = 'my_username';
$password = 'my_password';
$code = 0;
$stmt = $this->dbAdapter->createStatement();
$stmt->prepare('CALL sp_register_user(?, ?, ?)');
$stmt->getResource()->bindParam(1, $username);
$stmt->getResource()->bindParam(2, $password);
$stmt->getResource()->bindParam(3, $code, \PDO::PARAM_INT, 3);
$stmt->execute();
However, I get an error saying that the statement could not be executed.
The ideal solution would be one where I could make use of ZF2's abstraction layer, but any ideas on how to access the output parameter are welcome and appreciated.
this must work, because i m using it :
$str = "DECLARE #Msgvar varchar(100);DECLARE #last_id int;
exec CallEntry_Ins $CallLoginId,".$this->usrId .",#Msg = #Msgvar OUTPUT,#LAST_ID = #last_id OUTPUT;
SELECT #Msgvar AS N'#Msg',#last_id AS '#LAST_ID'; ";
$stmt = $db->prepare($str);
$stmt->execute();
$rtStatus = $stmt->fetchAll();
$rtStatus[0]["#LAST_ID"] //accessing Op para
Related
I am trying to make simple form where you put user_id etc. etc. and it will change in mysql, which works pretty fine but the question is, is there any way I could instead of users_ids use usernames ? thanks
if(isset($_POST['btn-change'])) {
$account = strip_tags($_POST['account']);
$value = strip_tags($_POST['value']);
$string = strip_tags($_POST['string']);
$account = $DBcon->real_escape_string($account);
$value = $DBcon->real_escape_string($value);
$string = $DBcon->real_escape_string($string);
$sql = "UPDATE tbl_users SET $value='$string' WHERE user_id=$account";
if ($DBcon->query($sql) === TRUE) {
$msg1 = '<div class="alert alert-success">
<i class="fa fa-info" aria-hidden="true"></i> Successfully changed !
</div>';
} else {
$msg1 = '<div class="alert alert-danger">
<i class="fa fa-info" aria-hidden="true"></i></span> Something went wrong !
</div>';
}
$DBcon->close();
}
The original MySQL extension has been removed in PHP7 and was deprecated in PHP 5.5.
Don't be intimidated by PDO though, it's actually super simple and much better.
Require this code at the start of any PHP files that need to make database queries.
$host = "127.0.0.1";
$port = "3306";
$dbname = "NameOfTheDatabase";
$dsn = "mysql:host=$host;dbname=$dbname;port=$port;";
$dbuser = "UsernameForDatabase";
$dbpass = "PasswordForDatabase";
$options = array(PDO::MYSQL_ATTR_INIT_COMMAND => "set names utf8");
$database = new PDO($dsn, $dbuser, $dbpass, $options);
Note that it can be improved upon drastically but for the sake of example, we'll keep things simple. Usually you would want to load this information from a protected config file.
Now let's say we want to select the user by their username.
$statement = $database->prepare("
SELECT user_id
FROM tbl_users
WHERE username LIKE :username
");
$statement->bindParam("username", $_POST['username'], PDO::PARAM_STR);
$statement->execute();
$result = $statement->fetch(PDO::FETCH_OBJ);
echo $result->user_id;
Basically what we're doing here is telling PDO to store a query that we've prepared in the $statement variable. We then tell it to use bindParam() to replace or bind :username with the POST variable (this will take care of escaping).
After the statement is executed using execute(), we have two options. The fetch() function essentially takes the next (or in this case, first) returned row and stores it in the $result variable. After that, you can access the returned columns using their names, in this case, $result->user_id. However if you SELECTed the rank, for example, you would be able to access it with $result->rank.
The second option is the fetchAll() function which works in nearly the same way, except that it takes every row returned and stores it in an array. Even if only one row is returned. That means that you'd need to access the data using $result[0]->user_id where 0 is the returned row.
foreach($result as $row){
echo $row->user_id;
}
Or you can loop over the result array using foreach as above. One of the greatest features of PHP in my opinion.
Updating and inserting works in nearly the same way except that no rows are returned by the execute function. Instead, the amount of rows that were changed is returned.
$statement = $database->prepare("
UPDATE tbl_users
SET email = :email
WHERE user_id = :user_id
");
$statement->bindParam("email", $_POST['email'], PDO::PARAM_STR);
$statement->bindParam("user_id", $_POST['account'], PDO::PARAM_INT);
$statement->execute();
echo "Rows changed: ".$statement->rowCount();
In that example, we're just updating the user's email address using a POST variable for the email and the account variable for the user_id. Notice that I used PDO::PARAM_INT for the third parameter of bindParam, that's just because the user_id is an integer. Usually you can get away with PDO::PARAM_STR but it's supposed to be for strings.
If you want a bit more detail by somebody far better at explaining things than me, check this introduction to PDO out. Hope this gets you on the right track!
This has been driving me crazy, the issue is I cannot work out How i can get and set the cached data to be displayed within my view.
public function get_something($id, $account_name)
{
$sql = "SELECT one,two,three FROM table WHERE id = ? and account_name = ? ";
$key = md5("SELECT one,two,three FROM table WHERE id = $id and account_name = $account_name ");
$get_result = $this->Core->Core->Memcache->get($key);
if($get_result)
{
// How would I set the Data
}
else
{
$stmt = $this->Core->Database->prepare($sql);
$stmt->bind_param("is", $id, $account_name);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($one, $two, $three);
$stmt->fetch();
//Below is how i set the data
$this->Core->Template->set_data('one', $one);
//Set the Memcache
$this->Core->Memcache->set($key, $stmt, TRUE, 20);
}
So my question is how can I get and set the data from a prepared statement fetch within memcache?
Memcache is a key/value storage system with both the key and the value needing to be serialized. From the php.net page:
Remember that resource variables (i.e. file and connection descriptors) cannot be stored in the cache, because they cannot be adequately represented in serialized state.
It appears your sql statement is looking for three values in a single row. I'm no expert on mysqli, but this is kind of what you want to do:
public function get_something($id, $account_name){
$sql = "SELECT one,two,three FROM table WHERE id = ? and account_name = ? ";
$key = md5("SELECT one,two,three FROM table WHERE id = $id and account_name = $account_name ");
$get_result = $this->Core->Core->Memcache->get($key);
if($get_result){
return $get_result;//#1 just return it, the format is an array like what is being built below
}
else{
$stmt = $this->Core->Database->prepare($sql);
$stmt->bind_param("is", $id, $account_name);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($one, $two, $three);
$stmt->fetch();
//Below is how i set the data
$this->Core->Template->set_data('one', $one);//#2 I don't know what this line does or is for, presumably for something else besides memcache stuff, maybe it acts like return
//Set the Memcache
$array=array();//#3
$array[]=$one;
$array[]=$two;
$array[]=$three;
$this->Core->Memcache->set($key, $array, TRUE, 20);
//this is a function, do you want to return your values somewhere?
}
A few notes, #1 the answer to your question is simple, just return $get_result. It should be an array with three values. #2 I'm not familiar with this line, nor what it does. Is this how your "return" the values to your controller? If so, you'll want to mimick that line where I put the return inside the if #3 This is your problem. You can't save the $stmt variable in memcache, it's a mysqli object, not the data you want. You need to build an array and then save that array. And that should do it for you.
There are other nuances to do, you can loop on the returned values. You should check for mysql not returning anything. But this is the basic starting point to get this going.
Let me know if this works for you.
I'm attempting to convert my script that I use for registering a user on my website from SQL to SQLi. I have some code and wondered if it was correct. Thanks.
$members = new mysqli("localhost", "root", "pass", "members");
$check = $members->prepare("select email from users where email = ?");
$check->bind_param('s', $_POST['r_email']);
$check->execute();
$check->store_result();
if ($check->num_rows > 0) {
echo "user already registered";
} else {
$user_id = mt_rand(100000000, 999999999);
$add_user = $members->prepare("insert into users(email, password, user_id) values(?, ?, ?)");
$add_user->bind_param('ssi', $r_email, $r_password, $user_id);
$r_email = $_POST['r_email'];
$r_password = md5($_POST['r_password']);
$add_user->execute();
$add_user->close();
}
$check->close();
$members->close();
Dealing with the error message you noted in your comment, 'All data must be fetched before a new statement prepare takes place'' ...
The error means exactly what it says: You're trying to prepare a new statement before you've fetched all the data from the previous statement. From the manual entry on mysqli::use_resultdocs ...
Used to initiate the retrieval of a result set from the last query
executed using the mysqli_real_query() function on the database
connection.
Either this or the mysqli_store_result() function must be called
before the results of a query can be retrieved, and one or the other
must be called to prevent the next query on that database connection
from failing.
Further, from the manual entry on mysqli_stmt::num_rowsdocs ...
Returns the number of rows in the result set. The use of
mysqli_stmt_num_rows() depends on whether or not you used
mysqli_stmt_store_result() to buffer the entire result set in the
statement handle.
You need to call mysqli_stmt::store_result before you check mysqli_stmt::num_rows (as described at mysqli_stmt::num-rows). After that, you need to close the statement using mysqli_stmt::close (mysqli_stmt::close).
Edit: Also, using md5 for password hashing (especially without a salt) is very insecure. Take a look at https://stackoverflow.com/a/1581919/140827 for suggestions on more secure solutions (bcrypt, salt, etc.)
I'm trying to learn more about MySQL and how to protect against SQL injections so my research has brought me to Prepared Statements which seems to be the way to go.
I'm also working on learning how to write Stored Procedures and am now trying to combine the two. There isn't much info on this though.
At the moment in my PHP test app I have a function that calls an SP with a normal MySQL command like this:
mysql_query("CALL usp_inserturl('$longurl', '$short_url', '$source')");
How can I do the same with MySQLi and a Prepared Statement to make it as safe as possible to injections?
Thanks!
Try the following:
$mysqli= new mysqli(... info ...);
$query= "call YourSPWithParams(?,?,?)";
$stmt = $mysqli->prepare($query);
$x = 1; $y = 10; $z = 14;
$stmt->bind_param("iii", $x, $y, $z);
$stmt->execute();
You can use both of them in the same time: just make preparation with stored procedure:
//prepare and bind SP's parameters with your variables only once
$stmt=$db->prepare("CALL MyStoredProc(?,?)");
$stmt->bind_param('is',$i,$name);
//then change binded variables and execute statement
for($i=1;$i<9;$i++)
{
$name="Name".$i;
$stmt->execute();
}
Bear in mind that you should do the preparation only once (not again for each execution), then execute it more times (just change parameter value before).
This one was a bit tricky but I eventually figured out how to both use a stored procedure (using IN parameters) that uses a prepared statement and retrieve the data through PHP. This example uses PHP 7.4.6 and MySQL 8.0.21 Community edition.
Here is the Stored Procedure:
CREATE DEFINER=`root`#`loalhost` PROCEDURE `SP_ADMIN_SEARCH_PLEDGORS`(
IN P_email VARCHAR(60),
IN P_password_hash VARCHAR(255),
IN P_filter_field VARCHAR(80),
IN P_filter_value VARCHAR(255)
)
BEGIN
#Takes admin credentials (first tow paramaters and searches the pledgors_table where field name (P_filter_field) is LIKE value (%P_filter_value%))
DECLARE V_admin_id INT(11);
BEGIN
GET DIAGNOSTICS CONDITION 1 #ERRNO = MYSQL_ERRNO, #MESSAGE_TEXT = MESSAGE_TEXT;
SELECT 'ERROR' AS STATUS, CONCAT('MySQL ERROR: ', #ERRNO, ': ', #MESSAGE_TEXT) AS MESSAGE;
END;
SELECT admin_id INTO V_admin_id FROM admin_table WHERE password_hash = P_password_hash AND email = P_email;
IF ISNULL(V_admin_id) = 0 THEN
SET #statement = CONCAT('SELECT pledgor_id, email, address, post_code, phone, alt_phone, contact_name
FROM pledgors_table
WHERE ',P_filter_field, ' LIKE \'%', P_filter_value, '%\';');
PREPARE stmnt FROM #statement;
EXECUTE stmnt;
ELSE
SELECT 'ERROR' AS STATUS, 'Bad admin credentials' AS MESSAGE;
END IF;
END
And here is the PHP script
query = 'CALL SP_ADMIN_SEARCH_PLEDGORS(\''.
strtolower($email).'\', \''.
$password_hash.'\', \''.
$filter_field.'\', \''.
$filter_value.'\');';
$errNo = 0;
//$myLink is a mysqli connection
if(mysqli_query($myLink, $query)) {
do {
if($result = mysqli_store_result($myLink)) {
while($row = mysqli_fetch_assoc($result)) {
$data[] = $row;
}
mysqli_free_result($result);
}
} while(mysqli_next_result($myLink));
}
else {
$errNo = mysqli_errno($myLink);
}
mysqli_close($myLink);
You might find the following answer of use:
MySql: Will using Prepared statements to call a stored procedure be any faster with .NET/Connector?
In addition:
GRANT execute permissions only so your application level user(s) can only CALL stored procedures. This way, your application user(s) can only interact with the database through your stored procedure API, they can not directly:
select, insert, delete, update, truncate, drop, describe, show etc.
Doesn't get much safer than that. The only exception to this is if you've used dynamic sql in your stored procedures which I would avoid at all costs - or at least be aware of the dangers if you do so.
When building a database e.g. foo_db, I usually create two users. The first foo_dbo (database owner) is the user that owns the database and is granted full permissions (ALL) so they can create schema objects and manipulate data as they want. The second user foo_usr (application user) is only granted execute permisisons and is used from within my application code to access the database through the stored procedure API I have created.
grant all on foo_db.* to foo_dbo#localhost identified by 'pass';
grant execute on foo_db.* to foo_usr#localhost identified by 'pass';
Lastly you can improve your code example above by using mysql_real_escape_string:
http://php.net/manual/en/function.mysql-real-escape-string.php
$sqlCmd = sprintf("call usp_inserturl('%s','%s','%s')",
mysql_real_escape_string($longurl),
mysql_real_escape_string($shorturl),
mysql_real_escape_string($source));
$result = mysql_query($sqlCmd);
I'm playing around with MySQLi at the moment, trying to figure out how it all works. In my current projects I always like to echo out a query string while coding, just to make sure that everything is correct, and to quickly debug my code. But... how can I do this with a prepared MySQLi statement?
Example:
$id = 1;
$baz = 'something';
if ($stmt = $mysqli->prepare("SELECT foo FROM bar WHERE id=? AND baz=?")) {
$stmt->bind_param('is',$id,$baz);
// how to preview this prepared query before acutally executing it?
// $stmt->execute();
}
I've been going through this list (http://www.php.net/mysqli) but without any luck.
EDIT
Well, if it's not possible from within MySQLi, maybe I'll stick with something like this:
function preparedQuery($sql,$params) {
for ($i=0; $i<count($params); $i++) {
$sql = preg_replace('/\?/',$params[$i],$sql,1);
}
return $sql;
}
$id = 1;
$baz = 'something';
$sql = "SELECT foo FROM bar WHERE id=? AND baz=?";
echo preparedQuery($sql,array($id,$baz));
// outputs: SELECT foo FROM bar WHERE id=1 AND baz=something
Far from perfect obviously, since it's still pretty redundant — something I wanted to prevent — and it also doesn't give me an idea as to what's being done with the data by MySQLi. But I guess this way I can quickly see if all the data is present and in the right place, and it'll save me some time compared to fitting in the variables manually into the query — that can be a pain with many vars.
I don't think you can - at least not in the way that you were hoping for. You would either have to build the query string yourself and execute it (ie without using a statement), or seek out or create a wrapper that supports that functionality. The one I use is Zend_Db, and this is how I would do it:
$id = 5;
$baz = 'shazam';
$select = $db->select()->from('bar','foo')
->where('id = ?', $id)
->where('baz = ?', $baz); // Zend_Db_Select will properly quote stuff for you
print_r($select->__toString()); // prints SELECT `bar`.`foo` FROM `bar` WHERE (id = 5) AND (baz = 'shazam')
I have struggled with this one in the past. So to get round it I wrote a little function to build the SQL for me based on the SQL, flags and variables.
//////////// Test Data //////////////
$_GET['filmID'] = 232;
$_GET['filmName'] = "Titanic";
$_GET['filmPrice'] = 10.99;
//////////// Helper Function //////////////
function debug_bind_param(){
$numargs = func_num_args();
$numVars = $numargs - 2;
$arg2 = func_get_arg(1);
$flagsAr = str_split($arg2);
$showAr = array();
for($i=0;$i<$numargs;$i++){
switch($flagsAr[$i]){
case 's' : $showAr[] = "'".func_get_arg($i+2)."'";
break;
case 'i' : $showAr[] = func_get_arg($i+2);
break;
case 'd' : $showAr[] = func_get_arg($i+2);
break;
case 'b' : $showAr[] = "'".func_get_arg($i+2)."'";
break;
}
}
$query = func_get_arg(0);
$querysAr = str_split($query);
$lengthQuery = count($querysAr);
$j = 0;
$display = "";
for($i=0;$i<$lengthQuery;$i++){
if($querysAr[$i] === '?'){
$display .= $showAr[$j];
$j++;
}else{
$display .= $querysAr[$i];
}
}
if($j != $numVars){
$display = "Mismatch on Variables to Placeholders (?)";
}
return $display;
}
//////////// Test and echo return //////////////
echo debug_bind_param("SELECT filmName FROM movies WHERE filmID = ? AND filmName = ? AND price = ?", "isd", $_GET['filmID'], $_GET['filmName'], $_GET['filmPrice']);
I have also build a little online tool to help.
Mysqli Prepare Statement Checker
I recently updated this project to include composer integration, unit testing and to better handle accepting arguments by reference (this requires updating to php 5.6).
In response to a request I received on a project I created to address this same issue using PDO, I created an extension to mysqli on github that seems like it addresses your issue:
https://github.com/noahheck/E_mysqli
This is a set of classes that extend the native mysqli and mysqli_stmt classes to allow you to view an example of the query to be executed on the db server by interpolating the bound parameters into the prepared query then giving you access to resultant query string as a new property on the stmt object:
$mysqli = new E_mysqli($dbHost, $dbUser, $dbPass, $dbName);
$query = "UPDATE registration SET name = ?, email = ? WHERE entryId = ?";
$stmt = $mysqli->prepare($query);
$stmt->bindParam("ssi", $_POST['name'], $_POST['email'], $_POST['entryId']);
$stmt->execute();
echo $stmt->fullQuery;
Will result in:
UPDATE registration SET name = 'Sue O\'reilly', email = 'sue.o#example.com' WHERE entryId = 5569
Note that the values in the fullQuery are escaped appropriately taking into account the character set on the db server, which should make this functionality suitable for e.g. log files, backups, etc.
There are a few caveats to using this, outlined in the ReadMe on the github project, but, especially for development, learning and testing, this should provide some helpful functionality.
As I've outlined in the github project, I don't have any practical experience using the mysqli extension, and this project was created at the request of users of it's sister project, so any feedback that can be provided from devs using this in production would be greatly appreciated.
Disclaimer - As I said, I made this extension.
Just set it to die and output the last executed query. The Error handling should give you meaningful information which you can use to fix up your query.
You can turn on log queries on mysql server.
Just execute command:
sql> SHOW VARIABLES LIKE "general_log%";
sql> SET GLOBAL general_log = 'ON';
And watch queries in the log file.
After testing turn log off:
sql> SET GLOBAL general_log = 'OFF';
I was able to use var_dump() to at least get a little more info on the mysqli_stmt:
$postmeta_sql = "INSERT INTO $db_new.wp_postmeta (post_id, meta_key, meta_value) VALUES (?, ?, ?)";
$stmt = $new_conn->prepare($postmeta_sql);
$stmt->bind_param("sss", $post_id, $meta_key, $meta_value);
echo var_dump($stmt);
$stmt->execute();
$stmt->close();