Happy New Year to all. I need to point out I am trying to use PDO exclusively and I'm a relative noob to using PDO, so please excuse the question if it appears plainly obvious.
I'm having a bit of a stupid moment because I cannot seem to understand a few things as to why a relatively simple email validation system I have (tried) to write is not quite working correctly. Everything is ok until the php at the end of the validation link is setting the email address as being validated. Here is my code, followed by questions:
Firstly I have an include file that holds the DB login. It looks like this:
<?php
// DATABASE SETTINGS
$hostname = "127.0.0.1";
$username = "devProduction";
$password = "ienx3rybcisuc";
$database = "devProduction";
try {
$conn = new PDO("mysql:host=$hostname; dbname=$database", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$conn->exec("SET CHARACTER SET utf8"); // Sets encoding UTF-8
// close the database connection (removed as I do this at the end of each call)
//$conn = null;
} catch(PDOException $e) {
echo $e->getMessage();
}
?>
And then in the page that actually received the user after they click on the link sent out to their email:
<?php
// Grab our includes
include '../conf/Funcs.php';
include '../conf/DBconfig.php'; // (This is the file displayed above)
require_once '../conf/Mobile_Detect.php';
// Check out what device is looking at us
$detect = new Mobile_Detect;
$deviceType = ($detect->isMobile() ? ($detect->isTablet() ? 'tablet' : 'phone') : 'computer');
$scriptVersion = $detect->getScriptVersion();
// Check to see if we are already logged in under an already validated account
if(isset($_COOKIE['AGMARDTuid']) || isset($_COOKIE['AGMARDTtoken'])) {
logout();
header("Location: ../");
exit;
} else {
$val = base64url_decode($_GET['val']);
$val = explode(":-:", $val);
$uid = $val[0];
$add = $val[1];
$key = $val[2];
// These are the three items that are pulled out of the URL $val value. This works fine
// It's only here to check it's working ok for the moment
echo "uid: ".$uid."<br>add: ".$add."<br>key: ".$key."<br><br>";
// Kill the process if either of the three values - $uid, $add, $key - are empty
if(($uid == "") || ($uid == NULL) || ($add == "") || ($add == NULL) || ($key == "") || ($key == NULL)) {
logout();
header("Location: ../");
exit;
} else {
// Seems everything is in order for email validation, so lets validate
$yes = "yes";
$NULL = NULL;
try {
$stmt = $conn->prepare("UPDATE $database.users SET `emailValidated` = :validate, `emailValidationKey` = :newkey WHERE `uid` = :uid AND `email` = :add AND `emailValidationKey` = :key");
$stmt->bindParam(':uid', $uid);
$stmt->bindparam(':add', $add);
$stmt->bindParam(':key', $key);
$stmt->bindParam(':validate', $yes);
$stmt->bindParam(':newkey', $NULL);
$stmt->execute();
$result = "success";
} catch(PDOException $e) { catchMySQLerror($e->getMessage()); $result = "fail"; }
$conn = null;
echo "result: ".$result." (post sql)<br><br>";
if($result == "fail") {
echo "Email did not successfully validate, there was a problem<br><br>";
echo $conn . "<br>" . $e->getMessage();
} else if($result == "success"){
echo "Email successfully validated<br><br>";
echo $conn . "<br>" . $e->getMessage();
}
echo "<br><br>We got to the end!";
}
}
?>
The code works, kinda. The problem is, if there is NOT an account within the database that matches all three values passed to the script from the URL, it still displays as having updated (validated) an account, even though it has not. Why is this?
Also, for the section that I am binding some parameters, specifically these two:
$stmt->bindParam(':validate', $yes);
$stmt->bindParam(':newkey', $NULL);
Why do I seem to have to assign $yes = "yes"; and "$NULL = NULL; as variables beforehand? I did try:
$stmt->bindParam(':validate', 'yes');
$stmt->bindParam(':newkey', NULL);
and
$stmt->bindParam(':validate', yes);
$stmt->bindParam(':newkey', NULL);
and
$stmt->bindParam(':validate', 'yes');
$stmt->bindParam(':newkey', 'NULL');
all without success.
Answers and info and suggestions always welcome and appreciated. Thank you!
C
You should use bindValue instead bindParam when you want to pass a value (or the result of a function) in the prepared statement.
$id = 100;
$datas = array('a', 'b', 'c');
$stmt = $db->prepare("SELECT * FROM user WHERE id = :id AND status > :status AND justForExample = :other");
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->bindValue(':status', 1, PDO::PARAM_INT);
$stmt->bindValue(':other', implode("", $datas), PDO::PARAM_STR);
$stmt->execute();
The documentation to BindValue
The documentation to BindParam
More informations about the difference
Related
i have a php script with a sql query that is inside a foreach loop and if the query succeed to run it should echo "Success" but since its inside a loop it echo "success" multiple times but i wish to only echo "success" once. I have seen other similar threads here and i have looked at those and tried solve my problem from those threads but i have not been able to solve this problem from any other threads so please dont mark this as a duplicate.
Anyway, Here is my code
<?php
session_start();
include('../../config/dbconf.php');
foreach($_POST['entry'] as $entryid) {
$stmt = $authconn->prepare("UPDATE account SET dp = dp - ? WHERE username = ?");
$stmt->bind_param("is", $_POST['price'], $_SESSION['classicusr']);
if($stmt->execute()) {
echo "Success";
}else{
echo "Failed";
}
}
?>
You could try something like this?
Also, there is no need to prepare for each iteration of the loop.
<?php
session_start();
include('../../config/dbconf.php');
$bError = false;
$authconn->prepare("UPDATE account SET dp = dp - ? WHERE username = ?");
foreach($_POST['entry'] as $entryid) {
$stmt = $stmt->bind_param("is", $_POST['price'], $_SESSION['classicusr']);
try{
if(!$stmt->execute()) {
$bError = true;
// optional break and do not process further
// break;
$stmt = $stmt->errorInfo()
}
}catch (PDOException Exception){
$bError = true;
// additional erorr logging here. Could add to a delimetered string to report on later } }
echo ($bError ? 'Failure' : 'Success' );
?>
Seeing as you're not saying anything about the Failed statement, something like this might do the trick. All it requires is setting a simple flag.
<?php
session_start();
include('../../config/dbconf.php');
$feedbackGiven = FALSE;
foreach($_POST['entry'] as $entryid) {
$stmt = $authconn->prepare("UPDATE account SET dp = dp - ? WHERE username = ?");
$stmt->bind_param("is", $_POST['price'], $_SESSION['classicusr']);
if($stmt->execute()) {
if (!$feedbackGiven)
{
echo "Success";
$feedbackGiven = TRUE;
}
}else{
echo "Failed";
}
}
?>
I have this code on "insert.php":
if ($stmt = $GLOBALS['mysqli']->prepare("INSERT INTO table1(iduser, title, msg, anonim, ip, iduniversity) VALUES (?,?,?,?,?,?)")) {
$stmt->bind_param("issisi", $_SESSION['iduser'], $_POST['title'], $_POST['msg'], $anonim, getIP(), $_SESSION['iduniversity']);
if ($stmt->execute()) {
$idmsg = $GLOBALS['mysqli']->insert_id;
$i = 0;
$stmt2 = $GLOBALS['mysqli']->prepare("INSERT INTO tag(idmsg, tag) VALUES(?,?)");
foreach ($tags as $tag) {
if ($tag != '') {
$stmt2->bind_param("is", $idmsg, $tag);
if ($stmt2->execute()) {
$i++;
}
}
}
$stmt2->close();
$stmt->close();
mysqli_close($GLOBALS['mysqli']);
sendFile($idmsg);
header("Location: /public/watch_msg.php?idmsg=" . $idmsg);
exit();
} else {
exit("Ops! Ocorreu algum erro. COD1370");
}
} else {
exit("Ops! Ocorreu algum erro. COD1371");
}
So, everything is working fine, except that sometimes when it redirects to "watch_msg.php" the message seems not to be on the database yet. When this happens, as soon as I refresh the page, everything is there!
First thing I thought is that there could be a race-condition somewhere, but I read in another question that PHP is sequential, and as I close both statements and connection before the redirect (so that used tables should be unlocked), why i'm getting this result somethimes? What i'm doing wrong?
Also, no functions outputs anything, but "sendFile" saves an image if the user sends one, so headers should be fine (it also gives me the error when I comment the function).
Code on watch_msg:
$msg = NULL;
$tags = NULL;
$coments = NULL;
$data_high = date("Y-m-d H:i:s");
$iduser;
if ($loggedin) { //If logged in
$idmsg = filter_input(INPUT_GET, 'idmsg', FILTER_SANITIZE_STRING);
$iduser = $_SESSION['iduser'];
$query = "SELECT * FROM table1 WHERE iduser = ? AND idmsg = ? AND datemsg < ?";
$stmt = $GLOBALS['mysqli']->prepare($query);
$stmt->bind_param("iis", $iduser, $idmsg, $data_high);
if ($stmt->execute()) {
$msg = mysqli_fetch_assoc($stmt->get_result());
if ($msg === NULL) {
exit('This message doesn\'t exists');
}
...
} else {
echo "Error.";
}
}
I want to get and echo a users permission level.
I have a function where the users email is passed, the function then needs to get the users permission level and return it, so it can be echoed on another page.
I imagine the function will look though the database for the passed email, it then finds the users permission and returns with that.
In the 'User.class.php'
public static function permGetter($email)
{
try
{
$db = Database::getInstance();
$stmt = $db->prepare('SELECT permission FROM users WHERE email = :email LIMIT 1');
$stmt->execute([':permission'=>$permission]);
$user = $stmt->fetchObject('User');
if($user !== false)
{
return $permission;
}
}
catch (PDOException $exception)
{
error_log($exception->getMessage());
return false;
}
}
In the 'permRequest.php'
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once("../includes/init.php");
//Get passed from an external program
$email = $_GET['email'];
$pass = $_GET['pass'];
if($email && $pass !== null)
{
// Checks if the user's entered credential matches with those in database
if(Auth::getInstance()->login($email, $pass))
{
//Uses the passed email to get permission level in 'User.class.php'
if(User::permGetter($email))
{
echo 'Permission ' + (int) $permission;
}
}
else
{
//I use level 5 as a debug so i can see when it fails
echo 'Permission 5';
}
}
?>
Database
Here's an example on what my database looks like.
Edit 1
Okay messing about, I think i got closer to the solution.
First, #Lawrence Cherone, thanks for pointing out my mistake in my execute.
Okay I have changed my code in
User.class.php
public static function permGetter($email, $permission)
{
try
{
$db = Database::getInstance();
$stmt = $db->prepare('SELECT permission FROM users WHERE email = :email');
$stmt->execute([':email'=>$email]);
$row = $stmt->fetch(PDO::FETCH_NUM);
$permission = $row['permission'];
}
catch (PDOException $exception)
{
error_log($exception->getMessage());
return false;
}
}
I have made small changes to
permRequest.php
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once("../includes/init.php");
//Get passed from an external program
$email = $_GET['email'];
$pass = $_GET['pass'];
$permission = '';
if($email && $pass !== null)
{
// Checks if the user's entered credential matches with those in database
if(Auth::getInstance()->login($email, $pass))
{
//Uses the passed email to get permission level in 'User.class.php'
if(User::permGetter($email, $permission))
{
echo 'Permission ', $permission;
}
}
}
?>
But now i get an error. The error is this Notice: Undefined index: permission in /classes/User.class.php on line 56
So, I read up on it and it seemed like it should be emptied first, so I empty it in permRequest.php that's why I'm passing it too, but I still get this error after i emptied it?
However if i change
$row = $stmt->fetch(PDO::FETCH_NUM);
to
$row = $stmt->fetch(PDO::FETCH_ASSOC);
/* OR */
$row = $stmt->fetch(PDO::FETCH_BOTH);
I get no error but it simply says my email or password is incorrect, which it isn't I have double and triple checked it.
So I'm confused to which PDO::FETCH_ I should use. I have read this (Click here) and I would say that both ASSOC, BOTH and NUM would fit the purpose.
So why is one giving an error while the two other's simply fails the login?
Edit 2
Found the solution and i have written it as a Answer. Can't accept it for the next two days however.
I moved everything out of the User.class.php and moved it into permRequest.php. This solved my problem for some reason. So my code looks like this now
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL ^ E_NOTICE);
require_once("../includes/init.php");
$email = $_GET['email'];
$pass = $_GET['pass'];
if($email && $pass !== null)
{
if(Auth::getInstance()->login($email, $pass))
{
try
{
$db = Database::getInstance();
$stmt = $db->prepare("SELECT * FROM users WHERE email = :email");
$stmt->execute([':email' => $email]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$sub = $row['permission'];
echo 'Permission ', $sub;
}
catch (PDOException $exception)
{
error_log($exception->getMessage());
return false;
}
}
}
And I don't use the User.class.php for this function.
So my guess is something went wrong when returning $sub when it was in User.class.php
I keep getting an error, even though I am 100% sure I followed the example that is found in the PHP manual.
The simplified version of the code can be found below.
note: connection to the database is ok.
EDIT: I keep getting an "Catchable fatal error: Object of class mysqli_stmt could not be converted to string" error.
EDIT: Now I keep getting "Mission Failed" even though I am sure that the row count should be 1.
Here's the coode used:
# $db = new mysqli('localhost', 'USER', 'PASSWORD', 'DATABSE');
$email = $db->prepare("select * from members where email = ?");
$email->bind_param('s', $email);
$email->execute;
$email->store_result;
$email->num_rows;
if ($email > 0) {
echo "<p>This e-mail is already in use, please try again with another e-mail.</p>";
exit;
} else {
echo "mission failed";
}
exit;
EDIT:
# $db = new mysqli('localhost', 'USER', 'PASSWORD', 'DB');
if ($db->connect_errno) {
echo "<p id=\"signup_confirmed\">Error: could not connect to database. Please try again later.</p>";
exit;
}
$checkRow = $db->prepare("select * from members where email = ?");
$checkRow->bind_param('s', $email);
$checkRow->execute;
$checkRow->store_result;
if ($checkRow->num_rows > 0) {
echo "<p id=\"signup_confirmed\">This e-mail is already in use, please try again with another e-mail.</p>";
exit;
} else {
echo "<p id=\"signup_confirmed\">Row checking has failed</p>";
}
change
$email->num_rows();
to
$email->num_rows;
in your code
New edit
$count = $email->num_rows;
if ($count > 0) {
echo "<p>This e-mail is already in use, please try again with another e-mail.</p>";
exit;
} else {
echo "mission failed";
}
More edit
change this to
$email = $db->prepare("select * from members where email = ?");
$email->bind_param('s', $email);
this
// you are over riding your $email value with the query thats the reason its not working
$query= $db->prepare("select * from members where email = ?");
$query->bind_param('s', $email);
# $db = new mysqli('localhost', 'USER', 'PASSWORD?', 'DATABSE');
$query_email = "select * from members where email = ?";
$email = $db->prepare($query_email);
$email->bind_param('s', $email);
$email->execute();
$email->store_result();
if ( $email->num_rows > 0) {
echo "<p id=\"signup_confirmed\">This e-mail is already in use, please try again with another e-mail.</p>";
exit;
}
$email is a result set, an object.
It will hold data returned from SQL operation.
You need to get the num_rows() in a variable.
Corrected code:
$cnt = $email->num_rows;
if ($cnt > 0) {
OR
if ($email->num_rows > 0) {
instead of
if ($email > 0) use if ($email->num_rows() > 0)
Okay I have a problem here. Whether users column "checked" is 1 or 0 it ignores the IF statement and still adding 10000 points. Can somebody explain me why ?
foreach ($users as $u) {
if (isset($username) . $u['checked'] == 0) {
foreach ($points as $p) {
$username = $_SESSION['username'];
$p = $p['points'] + 10000;
try {
$q = $pdo->prepare("UPDATE users SET points = ?, checked = ? WHERE username = ?");
$q->execute(array($p, $ten, $username));
} catch (Exception $e) {
echo $e->getMessage();
die();
}
header("Location: index.php");
}
} else {
echo "Nothing";
}
}
Try using a more explicit if statement like
if(isset($username) && $u['checked'] == 0)
Also as Bery said in the comments, isn't $username supposed to be $u?