php pdo: getting more detail from an update statement - php

I've been trying to track down a bug in my software for months now, to no success. It all comes down to one update query in PHP PDO seeming to run, with the correct data and yet it doesn't seem to update as "rowcount" returns 0.
Seems entirely random, 99% of the time it runs perfectly. 1% just doesn't update and I've literally no idea why.
For reference, here's the code in case someone spots me doing something really dumb. The part that is having issues is where it's updating the "saved_sessions" table.
// if they have a stay logged in cookie log them in
function stay_logged_in()
{
if (isset($_COOKIE['session']))
{
$session_check = $this->db->run("SELECT `session_id`, `device-id`, `user_id`, `expires` FROM `saved_sessions` WHERE `session_id` = ? AND `expires` > NOW()", array($_COOKIE['session']))->fetch();
if ($session_check)
{
// login then
$this->user_details = $this->db->run("SELECT ".$this::$user_sql_fields." FROM `users` WHERE `user_id` = ?", array($session_check['user_id']))->fetch();
$this->check_banned();
// update IP address and last login
$this->db->run("UPDATE `users` SET `ip` = ?, `last_login` = ? WHERE `user_id` = ?", array(core::$ip, core::$date, $this->user_details['user_id']));
// update their stay logged in cookie with new details
$generated_session = md5(time() . mt_rand() . $this->user_details['user_id'] . $_SERVER['HTTP_USER_AGENT']);
$expires_date = new DateTime('now');
$expires_date->add(new DateInterval('P30D'));
$update_session_sql = "UPDATE
`saved_sessions`
SET
`session_id` = ?,
`expires` = ?
WHERE
`session_id` = ? AND `user_id` = ?";
$update_session_db = $this->db->run($update_session_sql, array($generated_session, $expires_date->format('Y-m-d H:i:s'), $_COOKIE['session'], $session_check['user_id']));
$check_update = $update_session_db->rowcount();
// database was updated, so we can update the cookie
if($check_update == 1)
{
$cookie_domain = false; // allows cookies for localhost dev env
$secure = 0; // allows cookies for localhost dev env
if (!empty($this->core->config('cookie_domain')))
{
$cookie_domain = $this->core->config('cookie_domain');
$secure = 1;
}
setcookie('session', $generated_session, time()+$this->cookie_length, '/', $cookie_domain, $secure);
}
else
{
// logging for me to attempt to check the details match up (which they always seem to do!)
error_log("Couldn't update saved session for user_id " . $session_check['user_id'] . "\n" . "Current user session data: \n" . print_r($session_check, true) . "\nUser cookie data: " . $_COOKIE['session'] . "\n Database info: " . print_r($update_session_db, true));
}
$this->register_session($generated_session, $session_check['device-id']);
return true;
}
else
{
setcookie('session', "", time()-60, '/');
setcookie('device', "", time()-60, '/');
return false;
}
}
else
{
return false;
}
}

Related

Multiple Database requests for login

I have three files that are relevant for this part of my login scenario:
/project/index.html
/project/api/user/login.php
/project/api/objects/user.php
The index.html has a simple login form in it, calling the ./api/user/login.php.
In this form I have a checkbox that is an option for the user in order to stay logged in or not.
If the user has selected this option, with every login, I would like to check if the credentials are correct (login function -> stmt1 in user.php) as well as to update the lastlogin (datetime), the identifier and securitytoken if the checkbox was set (login function -> stmt2 in user.php).
The user.php is included_once in the login.php that gets the values out of the index.html form and sends them to the login() function in the user.php.
Depending on the functions return value, the login.php decides if the login was successful or not.
The login itself (stmt1) works, but the update of lastlogin, identifier and securitytoken (stmt2) doesn't.
login.php
session_start();
// include database and object files
include_once '../config/database.php';
include_once '../objects/user.php';
// get database connection
$database = new Database();
$db = $database->getConnection();
// prepare user object
$user = new User($db);
// set ID property of user to be edited
$user->username = isset($_GET['username']) ? $_GET['username'] : die();
$user->password = base64_encode(isset($_GET['password']) ? $_GET['password'] : die());
$user->remember = isset($_GET['remember']) ? $_GET['remember'] : die();
$stmt1 = $user->login();
if($stmt1->rowCount() > 0){
// get retrieved row
$row1 = $stmt1->fetch(PDO::FETCH_ASSOC);
$_SESSION['userid'] = $row1['uid'];
// create array
$user_arr=array(
"status" => true,
"message" => "Login erfolgreich!",
"uid" => $row1['uid'],
"username" => $row1['username']
);
$stmt2 = $user->login();
$row2 = $stmt2->fetch(PDO::FETCH_ASSOC);
print_r($row2);
// create array
$user_arr=array(
"lastlogin" => $row2['lastlogin']
);
}
else{
$user_arr=array(
"status" => false,
"message" => "Benutzername und/oder Passwort nicht korrekt!",
);
}
// make it json format
print_r(json_encode($user_arr));
?>
user.php
function login(){
// select all query
$query1 = "SELECT
`uid`, `username`, `email`, `password`, `created`, `lastlogin`
FROM
" . $this->table_name . "
WHERE
username='".$this->username."' AND password='".$this->password."'";
// prepare query statement
$stmt1 = $this->conn->prepare($query1);
// execute query
$stmt1->execute();
return $stmt1;
// set up the remain logged in function
if(isset($this->remember)) {
$identifier = random_string();
$securitytoken = random_string();
$remember = ",identifier='".$identifier."',securitytoken='".$securitytoken."'";
setcookie("identifier",$identifier,time()+(3600*24*365)); //1 year valid
setcookie("securitytoken",$securitytoken,time()+(3600*24*365)); //1 year valid
} else {
$remember = "";
}
// update last login
$query2 = "UPDATE
" . $this->table_name . "
SET
`lastlogin` = '".date("Y-m-d H:i:s")."'
".$remember."
WHERE
username='".$this->username."' AND password='".$this->password."'";
// prepare query statement
$stmt2 = $this->conn->prepare($query2);
// execute query
$stmt2->execute();
return $stmt2;
}
function random_string(){
if(function_exists('random_bytes')) {
$bytes = random_bytes(16);
$str = bin2hex($bytes);
} else if(function_exists('openssl_random_pseudo_bytes')) {
$bytes = openssl_random_pseudo_bytes(16);
$str = bin2hex($bytes);
} else if(function_exists('mcrypt_create_iv')) {
$bytes = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
$str = bin2hex($bytes);
} else {
//secret key should have >12 random chars
$str = md5(uniqid('SECRET KEY', true));
}
return $str;
}
In the user.php after return $stmt1;
The code is returned and the cookies are not set
I would do this... Check login... If true, save cookies with id and token
And then periodically check if token and id correspond... If so... Just UPDATE the last login time.
Note: your prepared statement is vulnerable!! Dont append the parameters with '.' use placeholders instead, and dont encode the password, is better to hash it... Then compare hashes

How to update MySQL column with thousands of data in PHP automatically using json data

I have a "users" table with columns:
Id | user_id | name | ip_address | lat | lng | active
The latitude(lat) and longitude(lng) columns are empty, and I need to populate them with the approximate location (I do not need a precise location). Using this forum, in another question, I discovered that I would have to separate the IPs, and use a location library. Well. I created a function to extract the IP and added the IP2Location library to my project. It works if I take IP for IP, get latitude and longitude, and add one by one in MySQL, however, I have 125,000 records, and there is no way to do the job manually.
Would anyone know how to do this?
Using this function
function List_ip() {
global $sqlConnect, $db;
if ($db['loggedin'] == false) {
return false;
}
$ips = mysqli_query($sqlConnect, "SELECT `user_id` , `ip_address` FROM " . T_USERS . " WHERE `active` = '1'");
while ($fetched_data = mysqli_fetch_assoc($ips)) {
$list_ip[] = array_map('utf8_encode', $fetched_data);
}
return $list_ip;
}
I have the following json result
{
"api_status": 200,
"ips": [
{
"user_id": "1",
"ip_address": "177.198.86.7x"
},
{
"user_id": "21",
"ip_address": "177.18.246.9x"
},
{
"user_id": "52",
"ip_address": "177.36.60.1x"
}
]
}
I have this script that can give me latitude and longitude based on IP
<?php
require_once './assets/libraries/vendor/ip2location/ip2location-php/IP2Location.php';
$reader = new \IP2Location\Database('./IP2LOCATION-LITE-DB9.BIN', \IP2Location\Database::FILE_IO);
$records = $reader->lookup('8.8.8.8', \IP2Location\Database::ALL);
echo '<pre>';
echo 'IP Number : ' . $records['ipNumber'] . "\n";
echo 'Latitude : ' . $records['latitude'] . "\n";
echo 'Longitude : ' . $records['longitude'] . "\n";
?>
How to insert latitude and longitude data for each of the IPs automatically?
Sorry for the amount of code, I'm trying to be as clear as possible.
So here is the function that gets the distinct (unique) IP addresses and pass them to second function to get Lat/Long and update database.
function List_ip() {
global $sqlConnect, $db;
if ($db['loggedin'] == false) {
return false;
}
$ips = mysqli_query($sqlConnect, "SELECT DISTINCT `ip_address` FROM " . T_USERS . " WHERE `active` = '1'");
while ($fetched_data = mysqli_fetch_assoc($ips)) {
ip2LocationAndUpdate($fetched_data["ip_address"]); //passing to other function
}
}
The global stuff
require_once './assets/libraries/vendor/ip2location/ip2location-php/IP2Location.php';
$reader = new \IP2Location\Database('./IP2LOCATION-LITE-DB9.BIN', \IP2Location\Database::FILE_IO);
The second function that does the IP2Location and update sql
function ip2LocationAndUpdate($ip_address){
global $sqlConnect, $reader;
$records = $reader->lookup($ip_address, \IP2Location\Database::ALL);
$updateSQL = "UPDATE " . T_USERS . " SET `lat` = '$records["latitude"]', `lat` = '$records["latitude"]' WHERE `ip_address` = '$ip_address'";
//execute the sql with mysqli or whatever you are using
}
If you have too many rows to insert I would recommend to create a cron task to run in the background, its faster and does nor affect to your website (in case you have it)
Search crontab.
Or shell_exec
Then if you want to show a progress you can use broadcasting or pusher for example
Nawed Khan's answer solved my problem with a few minor modifications.
This is my code currently running 100%.
Thanks to everyone for the answer, this was very useful to my learning the PHP language.
function List_ip() {
global $sqlConnect, $db;
if ($db['loggedin'] == false) {
return false;
}
$ips = mysqli_query($sqlConnect, "SELECT DISTINCT `ip_address` FROM " . T_USERS . " WHERE `lat` = '0'");
while ($fetched_data = mysqli_fetch_assoc($ips)) {
ip2LocationAndUpdate($fetched_data["ip_address"]); //passing to other function
}
}
require_once 'assets/libraries/vendor/ip2location/ip2location-php/IP2Location.php';
$reader = new \IP2Location\Database('IP2LOCATION-LITE-DB9.BIN', \IP2Location\Database::FILE_IO);
function ip2LocationAndUpdate($ip_address){
global $sqlConnect, $reader;
$records = $reader->lookup($ip_address, \IP2Location\Database::ALL);
//execute the sql with mysqli or whatever you are using
$latitu = $records['latitude'];
$longitu = $records['longitude'];
$updateSQL = mysqli_query($sqlConnect, "UPDATE " . T_USERS . " SET `lat` = '$latitu', `lng` = '$longitu' WHERE `ip_address` = '$ip_address'");
return $updateSQL;
}
Use flowing algorithm to solve your problem
<?php
for($i=1;$i<125000;$i++)
{
// here run what you want to do
}
?>
Note: after doing 1000 record stop for 10 second if you use server of online website

Integer Not Adding PHP

I've got a function on my website so that when people click on a link, they are supposed to have 5 points added to their profile, but that doesn't seem to be working, instead their points just stick to 5.
The following code is what I have used to set up a function:
$points = $user_data['points'];
function pointAdd($user_id, $points) {
$user_id = (int)$user_id;
$pointsPlus = $points += 5;
mysql_query("UPDATE `users` SET `points` = '$pointsPlus' WHERE `user_id` = $user_id");
}
And I have added this to the page, so that when they enter it, the code is put into play:
$target = $_GET['target'];
if(logged_in() === true){
pointAdd($session_user_id, $pointsPlus);
header('Location: ' . $target);
exit();
}
But, when they enter the page, the function doesn't work and the user is just set to 5 points instead of having 5 extra added to their profile.
Your pointsPlus variable that you pass into your function is empty. I don't know where user_data comes from, but the solution is something like this. And also the name 'pointsPlus' is logically incorrect, because there are the base points that the user already has.
$target = $_GET['target'];
if(logged_in() === true){
$points = $user_data['points'];
pointAdd($session_user_id, $points);
header('Location: ' . $target);
exit();
}

optimizing php code for less php processing

I am having some problem with my apache server when handling big amount of traffic. after some optimizations I did. I still have the same problem. I check my log file and it turned out that I have a lot of php processing. The following code is getting processed about 800 times a minute (when I have high traffic) and casing my server to crash.
1) is there any parts of the code that I need to rewrite that would make it take less php processing ?
2) is it a good idea to have all of this code before the html starts ?
<?php
$ip = $_SERVER['REMOTE_ADDR'];
mysql_connect('', '', '');
mysql_select_db('');
if(empty($_GET['i']) == false){
$get_image = mysql_real_escape_string($_GET['i']);
$check_if_image = mysql_query("SELECT `id`, `image_name`, `image_type`, `image_caption`, `image_voteup`, `image_votedown`, `image_views`, `fb_userid` FROM images_new WHERE image_name = '$get_image'");
if(mysql_num_rows($check_if_image) == 1){
$result = mysql_fetch_assoc($check_if_image);
$image_id = $result['id'];
$image_name = $result['image_name'];
$image_type = $result['image_type'];
$image_caption = stripslashes($result['image_caption']);
$image_voteup = $result['image_voteup'];
$image_votedown = $result['image_votedown'];
//$image_date = $result['image_date'];
$image_views = $result['image_views'];
$fb_username = $result['fb_username'];
$fb_userid = $result['fb_userid'];
//next image
$next_image_id = $image_id + 1;
$check_next_image = mysql_query("SELECT `image_name` FROM images_new WHERE id = '$next_image_id'");
if(mysql_num_rows($check_next_image) == 1){
$next_image_result = mysql_fetch_assoc($check_next_image);
$next_image_name = $next_image_result['image_name'];
}
// pre image
$pre_image_id = $image_id - 1;
$check_pre_image = mysql_query("SELECT `image_name` FROM images_new WHERE id = '$pre_image_id'");
if(mysql_num_rows($check_pre_image) == 1){
$pre_image_result = mysql_fetch_assoc($check_pre_image);
$pre_image_name = $pre_image_result['image_name'];
}
//shares, comments, and likes
$fb_page_url = "http://www.xxx.com/images.php?i=".$get_image;
$fb_url = "http://api.facebook.com/restserver.php?method=links.getStats&urls=".urlencode($fb_page_url);
$fb_xml = file_get_contents($fb_url);
$fb_xml = simplexml_load_string($fb_xml);
$fb_shares = $fb_xml->link_stat->share_count;
$fb_likes = $fb_xml->link_stat->like_count;
$fb_likes_and_shares = $fb_likes + $fb_shares;
$fb_comments = $fb_xml->link_stat->commentsbox_count;
//facebook
require_once('scripts/facebook.php');
$config = array('appId' => '','secret' => '');
$params = array('scope'=>'user_likes,publish_actions,email,offline_access,user_birthday');
$facebook = new Facebook($config);
$user = $facebook->getUser();
if($user){
try{
$user_profile = $facebook->api('/me','GET');
$user_id = $user_profile['username'];
$expire_time = time() + 30758400;
//insert cookie id
if (!isset($_COOKIE['id'])){
$cookie_id = $user_profile['username'];
setcookie("id", $cookie_id, $expire_time, '/');
}
//insert cookie name
if (!isset($_COOKIE['name'])){
$user_name = $user_profile['first_name'];
setcookie("name", $user_name, $expire_time, '/');
}
//check if the user like the fan page
$isFan = $facebook->api(array(
"method" => "pages.isFan",
"page_id" => ''
));
}catch(FacebookApiException $e) {
error_log($e->getType());
error_log($e->getMessage());
}
}else{//if no user
if(isset($_COOKIE['name'])){
$user_name = $user_profile['first_name'];
setcookie("name", $user_name, time() - 30758400, '/');
}
}
//increase views
if($facebook->getUser()){
mysql_query("UPDATE images_main SET image_views = image_views + 1 WHERE image_name='$image_name'");
mysql_query("UPDATE images_new SET image_views = image_views + 1 WHERE image_name='$image_name'");
}
}else{//image was not found in the database.
header('Location: index.php');
}
}else{//redirect if get is empty
header('Location: index.php');
}
?>
I would say the key factor is your call to the Facebook API, such things are always expensive and easily cacheable, put that code in a separate page/include and cache it as you like.
Also as a side note, you should consider reducing the number of db queries and you may wish to update your db driver... as invariably everyone points out #Madara Uchiha
I see a few items right off the bat.
First query:
$check_if_image = mysql_query("SELECT `id`, `image_name`, `image_type`, `image_caption`, `image_voteup`, `image_votedown`, `image_views`, `fb_userid` FROM images_new WHERE image_name = '$get_image'");
If you only need one result back, put a 'LIMIT 1' at then end (unless this field has a UNIQUE index, in which case this shouldn't matter). Also make sure this field is indexed and preferably a VARCHAR field instead of TEXT or BLOB.
Next, you are running 2 queries to get the previous and next images. I would combine this into 1 query like this:
SELECT `image_name` FROM images_new WHERE id IN ('$next_image_id', '$pre_image_id')
Also, you can apply the first optimization I mentioned to these 2 queries:
if($facebook->getUser()){
mysql_query("UPDATE images_main SET image_views = image_views + 1 WHERE image_name='$image_name'");
mysql_query("UPDATE images_new SET image_views = image_views + 1 WHERE image_name='$image_name'");
}
Lastly, going through the Facebook API is going to add load time that you cannot do much about. Hopefully this gets you started down the right path.

session_set_save_handler - Why isn't this code working?

I've been trying to save PHP session data in a MySQL database, but can't get it to work.
For a simple example, here's code that should increment a counter with each visit. I've seen other examples, etc. but can someone please tell me why this code isn't working? (Apache/2.2.11 (Win32) DAV/2 mod_ssl/2.2.11 OpenSSL/0.9.8i PHP/5.2.9 MySQL client version: 5.0.51a)
Here is the mysql database table:
CREATE TABLE IF NOT EXISTS `sessions` (
`session_ID` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
`session_data` mediumblob NOT NULL,
`access` int(10) unsigned NOT NULL,
PRIMARY KEY (`session_ID`),
KEY `access` (`access`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
And the PHP code (just plug in your db credentials):
<?PHP
function mysession_open()
{
global $sdbc; // Session Database Connection
if ($sdbc) {
return true;
} else {
return false;
}
}
function mysession_close()
{
global $sdbc;
return mysqli_close($sdbc);
}
function mysession_read($session_id)
{
global $sdbc;
$session_id = mysqli_real_escape_string($sdbc, $session_id);
$sql_sel = "SELECT session_data FROM sessions WHERE session_id = '$session_id'";
$data_sel = mysqli_query($sdbc, $sql_sel);
$row_sel = mysqli_fetch_array($data_sel);
if (isset($row_sel['session_data'])) {
return $row_sel['session_data'];
} else {
return '';
}
}
function mysession_write($session_id, $session_data)
{
global $sdbc;
$access = time();
$session_id = mysqli_real_escape_string($sdbc, $session_id);
$access = mysqli_real_escape_string($sdbc, $access);
$session_data = mysqli_real_escape_string($sdbc, $session_data);
$sql_write = "REPLACE INTO sessions (session_ID, session_data, access) " .
"VALUES ('$session_id', '$session_data', '$access')";
return mysqli_query($sdbc, $sql_write);
}
function mysession_destroy($session_id)
{
global $sdbc;
$session_id = mysqli_real_escape_string($sdbc, $session_id);
return mysqli_query($sdbc, $sql_del);
}
function mysession_gc($max)
{
global $sdbc;
$old = time() - $max;
$old = mysqli_real_escape_string($sdbc, $old);
$sql_old = "DELETE FROM sessions WHERE access < '$old'";
return mysqli_query($sdbc, $sql_old);
}
global $sdbc;
$sdbc = mysqli_connect('localhost', '...', '...', '...') or die('Could not connect to SDBC');
session_set_save_handler('mysession_open','mysession_close','mysession_read','mysession_write','mysession_destroy','mysession_gc');
session_start();
if (isset($_SESSION['counter'])) {
echo "counter is already set and it is " . $_SESSION['counter'] . '<br />';
$_SESSION['counter']++;
} else {
echo "counter is not set. setting to 1<br />";
$_SESSION['counter'] = 1;
}
echo "<br />Dumping SESSION data:<br />";
var_dump($_SESSION);
session_write_close();
?>
Thanks in advance for your help.
If you comment out the session_set_save_handler line of code, it works fine (it increments). But using the save handler it does not.
None of your query calls have any error checking. Instead of blindly assuming the database portion works, do some basic error checking at each stage, e.g:
function mysession_write($session_id, $session_data) {
global $sdbc;
[...snip...]
$stmt = mysqli_query($sdbc, $sql_write);
if ($stmt === FALSE) {
error_log("Failed to write session $session_id: " . mysqli_error($sdbc);
}
return($stmt);
}
There's only way way for a query to succeed, but zillions of ways to fail.
From the manual:
"Warning
As of PHP 5.0.5 the write and close handlers are called after object destruction and therefore cannot use objects or throw exceptions. The object destructors can however use sessions.
It is possible to call session_write_close() from the destructor to solve this chicken and egg problem. "

Categories