Switching from mysql_ to PDO - pdo

After a lot of recommendation from others I have decided to make the switch from mysql_ to PDO. I started looking at PDO literally around 15 minutes ago and I'm stuck trying to convert this line of code into PDO format.
function verify_user($username, $recover_password) {
return (mysql_result(mysql_query("SELECT COUNT(`user_id`) FROM `users` WHERE `username` = '$username' AND `password_recovery` = '$recover_password'"), 0) == 1) ? true : false;
}
I have looked at a couple of tutorials and as far as I can work out I can do the actual query with this code:
$verify_user = "SELECT COUNT(`user_id`) FROM `users` WHERE `username` = '$username' AND `password_recovery` = '$recover_password'";
$result = $con->prepare($verify_user);
$result->execute();
The problem I am having is the second part of the line of code - the mysql_result. Now that the query has run I have no idea how to return true or false using PDO. I'd appreciate any help. Thanks!
Updated:
$result = $con->prepare("SELECT COUNT(`user_id`) FROM `users` WHERE `username` = :username AND `password_recovery` = :recover_password");
$result->bindParam(':username', $username, PDO::PARAM_STR);
$result->bindParam(':password_recovery', $recover_password, PDO::PARAM_STR);
$result->execute();
From reading that page you provided it would be:
$result = $con->prepare("SELECT COUNT(`user_id`) FROM `users` WHERE `username` = :username AND `password_recovery` = :recover_password");
$result->bindParam(':username', $username, PDO::PARAM_STR);
$result->bindParam(':password_recovery', $recover_password, PDO::PARAM_STR);
$result->execute();
return ($con->fetch($result) == 1) ? true : false;
I'm probably miles out but I appreciate the help you've given me :) I'll do a couple more searches.

I would write the function this way:
function verify_user($username, $recover_password) {
$sql = "SELECT COUNT(`user_id`) AS count FROM `users`
WHERE `username` = ? AND `password_recovery` = ?";
$stmt = $con->prepare($sql);
$stmt->execute(array($username, $recover_password));
while ($row = $stmt->fetch()) { } /* should be exactly one row anyway */
return $row["count"] == 1;
}
There's no need to use bind_param(), since you can just pass values in an array argument to execute(). And there's no need to specify the parameter type (that's actually ignored, at least in the MySQL PDO driver).
Also be careful to do error-checking. The prepare() and execute() functions return false on error. Many things can cause an error. You could misspell a column name. Your database connection may lack the right database privileges. Someone could drop the table.
FWIW, proper error-checking is important when using the mysql_* and mysqli_* API's too, but it seems that few people do it right.
In the above code, I don't show checking the return values because I've made an assumption that we've enabled exceptions when we created the PDO connection.
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
That relieves us of having to write code to check the return values every time, but it means that an error will cause our application to go "white-screen". It's best practice to handle the exceptions in the caller function, and display some friendly error screen.

Related

Object of class PDOStatement could not convert to string

I changed my mysqli connection to PDO statment so i have to much error on my page this is the my code pls help us
.
.
.
if ($fn && $ln && $e && $p) { // If everything's OK...
// Make sure the email address is available:
//$q = "SELECT user_id FROM users WHERE email='$e'";
$q = $dbc->query("SELECT user_id FROM users WHERE email='$e'");
$q->execute(array($e));
$r = $q->fetchAll(PDO::FETCH_ASSOC);
//$r = mysqli_query ($dbc, $q) or trigger_error("Query: $q\n<br />MySQL Error: " . mysqli_error($dbc));
if (mysqli_num_rows($r) == 0) { // Available.
// Create the activation code:
$a = md5(uniqid(rand(), true));
Here is your code converted to PDO.
// Make sure the email address is available:
$q = $dbc->query("SELECT user_id FROM users WHERE email=?");
$q->execute(array($e));
$r = $q->fetchColumn();
if (!$r) { // Available.
// Create the activation code:
$a = md5(uniqid(rand(), true));
Three things has been corrected
You have to always use a placeholder tp represent a variable in the query.
To get a single value from the result, fetchColumn have to be used instead of fetchAll
No need for the manual reporting, as PDO can report its errors automatically, if confugired properly, as described in this tutorial I wrote

Converting SQL to Joomla Syntax (set and update)

Ok so basically I need to convert this regular sql statement to the syntax joomla uses via
https://api.joomla.org/11.4/Joomla-Platform/Database/JDatabaseQuery.html
here is my statement
SET #myunsubid = (
SELECT subid
FROM aqbi8_acymailing_subscriber s
WHERE s.email = 'email#email.co.nz'
);
SELECT #myunsubid;
UPDATE aqbi8_acymailing_listsub a
SET a.`status` = 1
WHERE a.subid = #myunsubid AND a.listid = 232
So id like it to be like
$db->set(#myunsubid = ( $db->select($db->quoteName('subid') )
$db->from($db->quoteName('aqbi8_acymailing_subscriber s') )
$db->where($db->quoteName('s.email') = 'email#email.co.nz')
)
$db->update($db->quoteName('aqbi8_acymailing_listsub a'))
$db->set($db->quoteName('a.status') = 1)
$db->where ($db->quoteName('a.subid') = #myunsubid AND $db->quoteName('a.listid') = 232 )
But this isnt quite right. please help!
I actually figured it out got it to work like this.
$db = &JDatabase::getInstance($option);
$query = $db->getQuery(true);
// make a variable for subID
$query->select($db->quoteName(array('subid')));
$query->from($db->quoteName('aqbi8_acymailing_subscriber'));
$query->where($db->quoteName('email') . " = '" . $email ."'");
$db->setQuery($query);
$db->execute();
$test = $db->loadObjectList();
print_r( $test );
$myid = $test[0]->subid;
$query->clear();
// // Create Database query
$fields = $db->quoteName('status') . ' = 1';
$conditions = array(
$db->quoteName('subid') . ' = ' . $myid,
$db->quoteName('listid') . ' = ' . $listid
);
// // update query
$query->update($db->quoteName('aqbi8_acymailing_listsub'))->set($fields)->where($conditions);
$db->setQuery($query);
$db->execute();
You don't need to make two trips to the database, you can write a subquery into your UPDATE's WHERE condition (no mysql variables or table aliases are necessary).
Raw Query:
UPDATE aqbi8_acymailing_listsub
SET status = 1
WHERE listid = 232
AND subid = (
SELECT subid
FROM aqbi8_acymailing_subscriber
WHERE `email` = 'email#email.co.nz'
)
Tested Code:
$db = JFactory::getDBO();
try {
$subquery = $db->getQuery(true)
->select('subid')
->from('#__acymailing_subscriber')
->where("email = 'email#email.co.nz'");
$query = $db->getQuery(true)
->update("#__acymailing_listsub")
->set("status = 1")
->where(["listid = 232", "personid = ($subquery)"]); // or make 2 where() calls
echo $query->dump(); // if you want to see; *during development ONLY
$db->setQuery($query);
$db->execute();
if ($affrows = $db->getAffectedRows()) {
JFactory::getApplication()->enqueueMessage("Updated. Rows affected: $affrows", 'success');
} else {
JFactory::getApplication()->enqueueMessage("Logic Error", 'error');
}
} catch (Exception $e) {
JFactory::getApplication()->enqueueMessage("Query Syntax Error: " . $e->getMessage(), 'error'); // never show getMessage() to public
}
It is not clear if any of your values are coming from users/untrusted sources, so be sure to follow good practices when writing variables into your queries -- like casting integers with (int) and calling $db->quote() on string values.
If you want to see a complex/convoluted UPDATE query with several other tables and techniques blended in, here is a comprehensive post: https://joomla.stackexchange.com/a/22916/12352
Please DON'T USE JDatabase Object to update Joomla tables, when there's an API available for the extension.
Whilst I appreciate the OP's question is pertaining to how to update the joomla database using the joomla database object (JDatabase), I propose a safer and more robust method, the "ACYMailing API".
"BUT WHY?", I hear you ask...
Good question!!!
There are 2 pitfalls in updating the joomla database directly - be it on the command-line, in a GUI such as MySQL Workbench or PHPMyAdmin, or even with the Joomla Database Object. Simply put, they both concern compatibility - 1. regarding third party integrations, and 2. concerning the future compatibility of your code. In a nutshell, whenever there's a an API for interacting with a component, I'd use it, over JDatabase every time to future proof your code, and ensure that all pre and post save, update, delete... ...move, and publish plugin events take care of your integrations, just as if you'd performed the action authentically.
To elaborate on these points a bit...
Most Joomla extensions (core and 3rd-party) make use of Joomla's powerful plugin architecture. By doing so, extensions can perform actions at key points in the application's life cycle. For example, after deleting a record from a table belonging to component1, delete related records from a table relating to compnent2. Therefore, one run's the risk of breaking the behaviour/functionality of the component in question - i.e. ACY Mailing, as in your case. Potentially, other core/3rd-party extensions that rely on ACY's data, that would otherwise, get updated through onAfterSave() or onAfterDelete() plugin events, as they will not get called.
There's a big risk that your code to break with future Joomla/ACY Mailing updates, if/when the table structure changes.
OK, so how do we use the API?
The following example code displays everything that you should need to update a subscription record. Each step explains the code, which for reference, is summarised in doc and inline comments in the code itself. To begin, navigate to the file where you are entering your code, then...
STEP BY STEP
STEP 1: Check the existence of ACY Mailing by attempting to include it's helper class, as follows. N.B. If the include_once() fails, you should see the echo statement, indicating that ACY Mailing IS NOT installed.
// load the ACY Mailing helper - bail out if not
if(!include_once(rtrim(JPATH_ADMINISTRATOR, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'components' . DIRECTORY_SEPARATOR . 'com_acymailing' . DIRECTORY_SEPARATOR . 'helpers' . DIRECTORY_SEPARATOR . 'helper.php')){
echo 'This code can not work without the AcyMailing Component';
return false;
}
STEP 2: Set-up your parameters by inputting values into the following 3 variables. See examples in code comments.
// array $lists An array of integer IDs (primary keys) of the lists you want the user to be subscribed to (can be empty).
// e.g. array(2,4,6)
$lists = array();
// array $unsubs An array of integer IDs (primary keys) of the lists you want the user to be un-subscribed from (can be empty).
// e.g. array(2,4,6)
$unsubs = array();
// string $userID Numeric Joomla User or user e-mail. For example: '42' or 'name#domain.com'
$userID = '';
STEP 3: Add the following code to find the ACY Mailing user, from the Joomla User ID/Email address passed in to the ->subid() method, and bail out if not found.
// instantiate the ACY Mailing Subscriber (user) Class
$user = acymailing_get('class.subscriber');
// find the ACY Mailing user id (subid) from the joomla ID or email address set in $userID
$subID = $user->subid($userID);
// No ACY Mailing user/subscriber?
if(empty($subID))
return; // bail out
STEP 4: Add the following code to check, and setup the data for any of the subscriptions/unsubscriptions you've configured to update ($lists and $unsubs arrays). If any found, they will be updated. If not found, return.
// create an array to store data in
$data = array();
// Set up new newsletter subscriptions from the $lists array()
if(!empty($lists)) foreach($lists as $listId)
$data[$listId] = array("status" => 1);
// Set up un-subscriptions from the $unsubs array()
if(!empty($unsubs)) foreach($unsubs as $listId)
$data[$listId] = array('status' => 0);
// no data, bail out...
if(empty($data))
return; //there is nothing to do...
// update the user's subscription records, creating/removing subscriptions/unsubsriptions accordingly
$user->saveSubscription($subID, $data);

Two separate database queries to relate separate tables with Drupal 7 module?

I am developing a custom module for a site I'm working on and have created the following code. This is my first module, so any ideas of what I could be doing better would be appreciate.
As it is, this module works perfectly for me. But, I want to optimize it and be sure that I fix shoddy code.
Thanks!
The function in question is as follows:
// Declared variables for future incrementation
$total=0;
$countOne=0;
$countTwo=0;
$countThree=0;
$countOld=0;
// Call the native global user object from Drupal
global $user;
$userID = $user->uid;
// Check for nodes of given type owned by current user
$sql = db_query("SELECT nid FROM {node} WHERE type = 'content_type' AND uid = " . $userID);
// Iteratively checks each node id against a custom Drupal field on a separate table
foreach ($sql as $record) {
// SQL query for all custom fields attached to the node id given above
$query = db_query("SELECT * FROM {field_birth} WHERE entity_id = " . $record->nid);
$result = $query->fetchObject();
// The unmodified birth format (Y-m-d 00:00:00)
$originalBirth = $result->field_date_of_birth_value;
// The sanitized birth format for comparison (Y-m-d)
$birth = date('Y-m-d', strtotime($originalBirth));
// The current date/time (Y-m-d)
$now = date('Y-m-d');
//Future dates (Y-m-d)
$one_year = date('Y-m-d', strtotime('+1 year', strtotime($birth)));
$two_years = date('Y-m-d', strtotime('+2 years', strtotime($birth)));
$three_years = date('Y-m-d', strtotime('+3 years', strtotime($birth)));
// A count of all records returned before logical statements
$total++;
// Logic to determine the age of the records
if($now < $one_year) {
$countOne++;
}
else if($now >= $one_year && $now < $two_years) {
$countTwo++;
}
else if($now >= $two_years && $now < $three_years) {
$countThree++;
}
else {
$countOld++;
}
My question is, can I avoid having two separate database queries to hit both tables? I am not really sure how to go about that. Also, am I doing things in a manner which will be resource intensive and highly inefficient? As I am not a programmer by trade, I am not certain when code is 'good'. I do want to try my best to make this good code though since it is a module for a website I hope will last a long time.
Thank you stackoverflow community!
EDIT: The code I got working thanks to Mike is as follows. If anyone has a similar question / problem hopefully this will help!
// Join field_birth_table to nodes of given type owned by current user
$sql = db_select('node', 'n');
$sql->join('field_birth_table', 'b', 'n.nid = b.entity_id');
$sql
->fields('b', array('field_birth_field_value', 'entity_id'))
->condition('n.type', 'content_type')
->condition('n.status', '1')
->condition('n.uid', $user->uid)
->addTag('node_access');
$results = $sql->execute();
You can use a left join between the node and field_birth table:
$query = db_select('node', 'n');
$query->leftJoin('field_birth', 'b', '(b.entity_id = n.nid AND b.entity_type = :node)', array(':node' => 'node'));
$query
->fields('b', array())
->condition('n.type', 'content_type')
->condition('n.uid', $user->uid)
$results = $query->execute();

bindValue and bindParam in mysqli and PDO ignore variable type

I'm having problems in understanding a part of the meaning of binding certain variable types in PDO and mysqli if the type given, in my case, seems to be meaningless. In the following code, the type bound (like i or s) gets ignored. The table row "wert_sortierung" in the database is INT(11). Regardingless if $val_int is really integer or not and if I bind it via i,s / PDO::PARAM_INT or _STR, the query always works, no break, no error or warning, that the types in the binding and database or variable itself don't fit.
<?
class PDOTest {
protected $pdo;
function __construct(){
$usr="usr";
$pwd="pwd";
$host="localhost";
$db="db";
$val_int="I'm a string";
$val_str="OP";
$querystring="SELECT wert_langtext FROM TB_wert WHERE wert_sortierung = ? AND wert_CD = ?";
try {
$db_info = "mysql:host=$host;dbname=$db"; // usually provided via require_once and during construction
$this->pdo = new PDO($db_info, $usr, $pwd);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$stmt = $this->pdo->prepare($querystring);
$stmt->bindValue(1,$val_int,PDO::PARAM_INT);
$stmt->bindValue(2,$val_str,PDO::PARAM_STR);
$stmt->execute();
$row_return = $stmt->fetchAll(PDO::FETCH_ASSOC);
$this->varprint($row_return);
$this->pdo = NULL;
}
catch (PDOException $ex) {
printf ('Es spricht:');
$this->printerror("Fehla! (" . $ex->getMessage() . ")");
$this->pdo = NULL;
exit();
}
printf("<br />-------<br />");
//Added for comparison
$mysqli = new mysqli($host, $usr, $pwd, $db);
$m_stmt = $mysqli->prepare($querystring);
$m_stmt->bind_param('is',$val_int, $val_str);
$m_stmt->execute();
$m_stmt->bind_result($row_return);
$m_stmt->fetch();
$this->varprint($row_return);
$m_stmt->close();
$mysqli->close();
}
private function printerror($txt) {
printf("<p><font color=\"#ff0000\">%s</font></p>\n",
htmlentities($txt));
}
private function varprint($var) {
echo "<br />";
echo "<pre>";
echo var_dump($var);
echo "</pre>";
}
}
new PDOTest();
?>
Please can anyone point out my error in reasoning.
It is actually Mysql's loose-typing that that deceived you.
As a matter of fact, regular Mysql queries can accept strings for the numberic values all right:
SELECT wert_langtext FROM TB_wert WHERE wert_sortierung = '1' AND wert_CD = '1';
while prepared statement just following this behavior.
However, a contrary situation is not that harmless. Addressing a string value with a number
SELECT wert_langtext FROM TB_wert WHERE wert_sortierung = 1;
will cause infinite number of warnings in case of wert_sortierung being of string type and some unexpected behavior, like matching for ALL the rows.
So, as a general advise I would suggest to always use 's' by default. The only drawback is PDO's emulated prepares and it can be easily worked around.
So, to answer your question explicitly - prepared statements just allow the same behavior as regular queries, adding nothing to it. Everything that possible with a regular query, is possible with prepared statement as well. And no, binding do not validate your data (however it should. Imn my class I test integer placeholders and throw an exception if no numeric value given)

PDO login script won't work

I changed this login script to PDO. Now it passes the username but get's stuck fetchAll line. I need help please. thanks
<?php
session_start();
include_once"includes/config.php";
if (isset($_POST['admin_login'])) {
$admin_user = trim($_POST['admin_user']);
$admin_pw = trim($_POST['admin_pw']);
if ($admin_user == NULL OR $admin_pw == NULL) {
$final_report.="Please complete all the fields below..";
} else {
$check_user_data = $db->prepare("SELECT * FROM `admin`
WHERE `admin_user`='$admin_user'");
$check_user_data->execute();
if ($check_user_data->fetchColumn() == 0) {
$final_report.="This admin username does not exist..";
} else {
$get_user_data = $check_user_data->fetchAll($check_user_data);
if ($get_user_data['admin_pw'] == $admin_pw) {
$start_idsess = $_SESSION['admin_user'] = "".$get_user_data['admin_user']."";
$start_passsess = $_SESSION['admin_pw'] = "".$get_user_data['admin_pw']."";
$final_report.="You are about to be logged in, please wait a few moments...";
header('Location: admin.php');
}
}
}
}
?>
Not checking return value prepare() or execute() for false. You need to check for SQL errors and handle them, stopping the code instead of continuing on blithely.
Not using query parameters in the prepared statement, still interpolating $_POST content into the query unsafely. You're missing the benefit of switching to PDO, and leaving yourself vulnerable to SQL injection attack.
You're storing passwords in plaintext, which is unsafe. See You're Probably Storing Passwords Incorrectly.
Do you really need to SELECT * if you only use the admin_pw column? Hint: no.
PDOStatement::fetchAll() returns an array of arrays, not just one array for a row. Read the examples in the documentation for fetchAll().