$GLOBAL Setting a table in array Call to member function - php

Okay, I'm using GLOBALS to set some settings within my whole site
$tmp = $GLOBALS['_ODB']->query("SELECT * FROM `options`");
$GLOBALS['options'] = NameToTop($tmp->fetchAll(PDO::FETCH_ASSOC));
I have this as my query, then I use this function to put the returned data in an array
So I can call it by using $GLOBALS['settings']['setting1']
function NameToTop($arr)
{
$output = array();
foreach ($arr as $val) {
$output[$val['name']] = $val['value'];
}
return $output;
}
Then here is the settings table, I don't see why this is going wrong; I really need some help.
CREATE TABLE IF NOT EXISTS `options` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`name` text NOT NULL,
`value` text NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
--
-- Dumping data for table `options`
--
INSERT INTO `options` (`ID`, `name`, `value`) VALUES
(1, 'setting1', 'Name'),
(2, 'email', 'webmaster#gmail.com'),
(3, 'site_title', 'Title of Site'),
I'm getting
Call to a member function fetchAll() on a non-object

You're expecting $tmp to be a PDOStatement object in order to call fetchAll() on it but it isn't, hence the error message you're seeing.
PDO::query() returns false on failure, so this is most likely what is happening.
This comment from the PDO manual talks about the return value of query():
The handling of errors by this function is controlled by the
attribute PDO::ATTR_ERRMODE.
Use the following to make it throw an exception:
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
You need to read up on error handling in PDO. Most people do tend to go down the exceptions route.
That would make your example something like this:
try {
$tmp = $GLOBALS['_ODB']->query("SELECT * FROM `options`");
$GLOBALS['options'] = NameToTop($tmp->fetchAll(PDO::FETCH_ASSOC));
} catch (PDOException $e) {
// handle the error in some way
echo $e->getMessage();
}

Related

PHP PDO does not throw exception on duplicate key insert

I have a strange problem with PDO not throwing an exception when a duplicate value is inserted. In this case I did expect an error.
The relevant code:
try
{
$db_conn = new PDO("mysql:host=".$config["database"]["hostname"].";charset=utf8", $config["database"]["username"], $config["database"]["password"], []);
$db_conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db_conn->exec(file_get_contents("some_file_with_data.sql");
}
catch(Exception $e)
{
// PDOException extends RuntimeException extends Exception so exceptions should be catched here
// however for the duplicate key entry it will not throw an exception
}
The file with SQL data contains multiple inserts like this:
INSERT INTO `a` (`b`, `c`) VALUES
(1, 1),
(2, 2),
(3, 2);
INSERT INTO `a` (`b`, `c`) VALUES
(1, 1);
The field b in table a is set to being the primary key. When I insert the exact same data in the exact same structure using phpMyAdmin I get this error: #1062 - Duplicate entry '65533' for key 'PRIMARY'
Why does PDO not throw an error in this case? Even when I set the error mode to exception?
Edit:
This is the table structure used for this specific table
CREATE TABLE IF NOT EXISTS `a` (
`b` smallint(5) unsigned NOT NULL,
`c` smallint(5) unsigned NOT NULL,
PRIMARY KEY (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Update 2018: DEVs do not consider this a bug, but intended behaviour.
So, PHP-Users have to live with that, Report is closed for any future questions...
This has often been reported as bug with PDO: https://bugs.php.net/bug.php?id=61613
It will only throw an exception if the FIRST Statement is invalid. If the first statement runs smooth, you won't get any error - And your first statement is valid:
INSERT INTO `a` (`b`, `c`) VALUES
(1, 1),
(2, 2),
(3, 2);
as a workaround - or according to user deleted the right way of doing it - you need to process the rowsets one-by-one (taken from the bug reports comments):
$pdo->beginTransaction();
try {
$statement = $pdo->prepare($sql);
$statement->execute();
while ($statement->nextRowset()) {/* https://bugs.php.net/bug.php?id=61613 */};
$pdo->commit();
} catch (\PDOException $e) {
$pdo->rollBack();
throw $e;
}

How to lock a table during an operation use php/yii

My table:
CREATE TABLE IF NOT EXISTS `detail_transaction` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`created_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`code` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`id_product` int(11) NOT NULL,
`id_store` int(11) NOT NULL,
`input_quality` int(11) NOT NULL,
`output_quality` int(11) NOT NULL,
`quality_in_store` int(11) NOT NULL,
PRIMARY KEY (`id`)
)
I have the problem is below:
I get record have max_id, and then before i insert new record, having
another process insert new record was inserted.
I think: I will lock table during "I select a record have max_id" to "I completed to insert new next record"(don't run any task work with this table). And to do this method. Please help me! How to do this by php code or Yii.
You could use transactions:
$transaction = Yii::app()->db->beginTransaction();
try {
foreach ($items as $item) {
$item->attr = value;
$item->save();
}
$transaction->commit();
// actions to do on success (redirect, alert, etc.)
} catch (Exception $e) {
$transaction->rollBack();
// other actions to perform on fail (redirect, alert, etc.)
}
This source code is from this post: using transaction in a loop in yii
I'm not exactly sure what you want to achieve, but I'm sure it will work out if you just use transactions - http://www.yiiframework.com/doc-2.0/yii-db-transaction.html. Otherwise, you can always call a LOCK TABLE query - http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html.
$connection = Yii::app()->db;
$lock = $connection-> createCommand('LOCK TABLES `detail_transactions` WRITE');
// do your magic
$unlock = $connection-> createCommand('UNLOCK TABLES');
In Yii2, you can lock/unlock a table like this
$db = Yii::$app->getDb();
$db ->createCommand('LOCK TABLES `YOUR_TABLE` WRITE')->execute();
// access YOUR_TABLE here
// something like YOUR_TABLE_MODEL::find()->where(["something" => "blah"])->one()
$db ->createCommand('UNLOCK TABLES')->execute();

Error on inserting Yii ActiveRecord model to mysql table

I have the following kind of error that happens on my website from time to time.
2012/07/12 21:21:48 [error] [system.db.CDbCommand] Error in executing
SQL: INSERT INTO `seo_page` (`grouped`, `results_count`, `page`, `operation`, `subtype`, `type`, `state`, `zone`, `city`, `district`, `custom`, `property`, `date_add`, `date_upd`) VALUES (:yp0, :yp1, :yp2, :yp3, :yp4, :yp5, :yp6, :yp7, :yp8, :yp9, :yp10, :yp11, :yp12, :yp13)
Basically the code works, and it saves the model correctly, I think I only get this kind of errors when it is trying to do two inserts at the same time with the same primary key, or maybe it doesn't ensure the uniqueness of the keyword field before inserting.
The script it is soposed to insert or update a keyword in the database, the code is like this:
static public function quickAdd($keyword) {
if (strlen($keyword['name'])==0)
return;
$seo_keyword = SeoKeyword::model()->find("keyword=:keyword", array("keyword"=>$keyword['name']));
if ($seo_keyword) {
// return;
} else {
$seo_keyword=new SeoKeyword();
}
$seo_keyword->keyword=$keyword['name'];
if (is_numeric($keyword['position'])) {
$seo_keyword->position=$keyword['position'];
}
try {
$seo_keyword->save();
} catch (Exception $e) {
}
return $seo_keyword;
}
The mysql table looks like this:
CREATE TABLE IF NOT EXISTS `seo_keyword` (
`id_seo_keyword` int(11) NOT NULL AUTO_INCREMENT,
`keyword` varchar(255) NOT NULL,
`position` int(11) DEFAULT NULL,
`date_add` datetime NOT NULL,
`date_upd` datetime NOT NULL,
PRIMARY KEY (`id_seo_keyword`),
UNIQUE KEY `keyword` (`keyword`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;
I am also getting this kind of error with other tables, all of them are tables that have many rows and are mostly used for stathistical data.
The fact is that I can only see that it could not insert, but there's no error like: primary key problem, servery busy or anything.
Try
var_dump($seo_keyword->getErrors());
after
$seo_keyword->save();
and lookout what is the actually problem so that insert query fails.

Capture mysql database error and use it in php

I created the following table for user to user subscriptions.
CREATE TABLE IF NOT EXISTS `subscriptions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`subscribed_to` int(11) NOT NULL,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_subscription` (`user_id`,`subscribed_to`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=76 ;
I'm disallowing identical rows by making the columns user_id and subscribed_to unique. If a user tries to resubmit the same data I get:
A Database Error Occurred Error Number: 1062
Duplicate entry '62-88' for key 'unique_subscription'
INSERT INTO subscriptions (user_id, subscribed_to, date) VALUES ('62',
'88', '2011-07-11 19:15:13')
Line Number: 330
I'm preventing the database error by checking if an identical row exists before trying to insert data.
$query = "SELECT COUNT(*) FROM subscriptions WHERE (user_id = '62' AND subscribed_to = '88')";
if ($query > 0)
{
//display already subscribed message
}
else
{
//insert new row
}
The database already checks the table and returns an error. The select count(*) query above seems redundant. Do I really need to check the table once more in my application? Is there a way to capture the database error if it occurs, and do something with that in my application?
If you have an idea how please share an example. I haven't a clue how this is done..!
Check out PHP function mysql_error
You can wrap the db call within a try catch statement for functions that throw exceptions to prevent your application from crashing.
PHP function mysql_query does not throw exception on error, but returns FALSE. You can check the return value and execute mysql_error to find out the trouble or log it.

function fetch() on a non-object in PHP

I have this url,
http://webworks.net/ww.incs/forgotten-password-verification.php?verification_code=974bf747124c69f12ae3b36afcaccc68&email=myemail#gmail.com&redirect=/ww.admin/index.php
And this gives the following error.
Fatal error: Call to a member function fetch() on a non-object in
/var/www/webworks/ww.incs/basics.php on line 23
Call Stack: 0.0005 338372 1. {main}()
/var/www/webworks/ww.incs/forgotten-password-verification.php:
0 0.0020 363796 2. dbRow()
/var/www/webworks/ww.incs/forgotten-password-verification.php:18
The forgotten-password-verification.php
require 'login-libs.php';
login_check_is_email_provided();
// check that a verification code was provided
if(
!isset($_REQUEST['verification_code']) || $_REQUEST['verification_code']==''
){
login_redirect($url,'novalidation');
}
// check that the email/verification code combination matches a row in the user table
// $password=md5($_REQUEST['email'].'|'.$_REQUEST['password']);
$r=dbRow('select * from user_accounts where
email="'.addslashes($_REQUEST['email']).'" and
verification_code="'.$_REQUEST['verification_code'].'" and active'
);
if($r==false){
login_redirect($url,'validationfailed');
}
// success! set the session variable, then redirect
$_SESSION['userdata']=$r;
$groups=json_decode($r['groups']);
$_SESSION['userdata']['groups']=array();
foreach($groups as $g)$_SESSION['userdata']['groups'][$g]=true;
if($r['extras']=='')$r['extras']='[]';
$_SESSION['userdata']['extras']=json_decode($r['extras']);
login_redirect($url);
And login-libs,
require 'basics.php';
$url='/';
$err=0;
function login_redirect($url,$msg='success'){
if($msg)$url.='?login_msg='.$msg;
header('Location: '.$url);
echo 'redirect';
exit;
}
// set up the redirect
if(isset($_REQUEST['redirect'])){
$url=preg_replace('/[\?\&].*/','',$_REQUEST['redirect']);
if($url=='')$url='/';
}
// check that the email address is provided and valid
function login_check_is_email_provided(){
if(
!isset($_REQUEST['email']) || $_REQUEST['email']==''
|| !filter_var($_REQUEST['email'], FILTER_VALIDATE_EMAIL)
){
login_redirect($GLOBALS['url'],'noemail');
}
}
// check that the captcha is provided
function login_check_is_captcha_provided(){
if(
!isset($_REQUEST["recaptcha_challenge_field"]) || $_REQUEST["recaptcha_challenge_field"]==''
|| !isset($_REQUEST["recaptcha_response_field"]) || $_REQUEST["recaptcha_response_field"]==''
){
login_redirect($GLOBALS['url'],'nocaptcha');
}
}
// check that the captcha is valid
function login_check_is_captcha_valid(){
require 'recaptcha.php';
$resp=recaptcha_check_answer(
RECAPTCHA_PRIVATE,
$_SERVER["REMOTE_ADDR"],
$_REQUEST["recaptcha_challenge_field"],
$_REQUEST["recaptcha_response_field"]
);
if(!$resp->is_valid){
login_redirect($GLOBALS['url'],'invalidcaptcha');
}
}
basics.php is,
session_start();
function __autoload($name) {
require $name . '.php';
}
function dbInit(){
if(isset($GLOBALS['db']))return $GLOBALS['db'];
global $DBVARS;
$db=new PDO('mysql:host='.$DBVARS['hostname'].';dbname='.$DBVARS['db_name'],$DBVARS['username'],$DBVARS['password']);
$db->query('SET NAMES utf8');
$db->num_queries=0;
$GLOBALS['db']=$db;
return $db;
}
function dbQuery($query){
$db=dbInit();
$q=$db->query($query);
$db->num_queries++;
return $q;
}
function dbRow($query) {
$q = dbQuery($query);
return $q->fetch(PDO::FETCH_ASSOC);
}
define('SCRIPTBASE', $_SERVER['DOCUMENT_ROOT'] . '/');
require SCRIPTBASE . '.private/config.php';
if(!defined('CONFIG_FILE'))define('CONFIG_FILE',SCRIPTBASE.'.private/config.php');
set_include_path(SCRIPTBASE.'ww.php_classes'.PATH_SEPARATOR.get_include_path());
I am not sure how to solve the problem.
My db:
CREATE TABLE IF NOT EXISTS `user_accounts` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`email` text,
`password` char(32) DEFAULT NULL,
`active` tinyint(4) DEFAULT '0',
`groups` text,
`activation_key` varchar(32) DEFAULT NULL,
`extras` text,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=10 ;
INSERT INTO `user_accounts` (`id`, `email`, `password`, `active`, `groups`, `activation_key`, `extras`) VALUES
(2, 'bla#blabla.com', '6d24dde9d56b9eab99a303a713df2891', 1, '["_superadministrators"]', '5d50e39420127d0bab44a56612f2d89b', NULL),
(3, 'user#blabla.com', 'e83052ab33df32b94da18f6ff2353e94', 1, '[]', NULL, NULL),
(9, 'myemail#gmail.com', '9ca3eee3c43384a575eb746eeae0f279', 1, '["_superadministrators"]', '974bf747124c69f12ae3b36afcaccc68', NULL);
The answer is, I believe, in this:
the table user_accounts:
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`email` text,
`password` char(32) DEFAULT NULL,
`active` tinyint(4) DEFAULT '0',
`groups` text,
`activation_key` varchar(32) DEFAULT NULL,
`extras` text,
PRIMARY KEY (`id`)
and
the 'forgotten-password-verification.php':
// check that the email/verification code combination matches a row in the user table
// $password=md5($_REQUEST['email'].'|'.$_REQUEST['password']);
$r=dbRow('select * from user_accounts where
email="'.addslashes($_REQUEST['email']).'" and
verification_code="'.$_REQUEST['verification_code'].'" and active'
);
where verification_code is not a valid part of user_accounts.
Change it and it should work ;)
Line 23 of basics.php is probably:
return $q->fetch(PDO::FETCH_ASSOC);
This means that $q is not the object you expected it to be (seems like a PDOStatement). Apparently, it is returned from the dbQuery function, which returns the result of PDO::query. PDO::query will return a PDOStatement on success, or FALSE on error.
It means that you query is erroneous. Most likely this one:
$r=dbRow('select * from user_accounts where
email="'.addslashes($_REQUEST['email']).'" and
verification_code="'.$_REQUEST['verification_code'].'" and active'
);
The problem is probably the end of your query, which does not look like valid SQL:
and active
Also, since you are using PDO, you should take advantage of prepared statements, since your code is actually open to SQL injection. addslashes is not a proper mechanism for escaping database parameters, and you should not use $_REQUEST unless you know what you are doing. You should use $_GET, $_POST or $_COOKIE directly.
For your securing your queries, use prepared statements, and check the return values:
function dbQuery($query, array $params = array()){
$db=dbInit();
$q=$db->prepare($query); // use prepare() instead of query()
$q->execute($params); // automatically bind the parameters with execute()
$db->num_queries++;
return $q;
}
function dbRow($query, array $params = array()) {
$q = dbQuery($query, $params);
if (!$q) {
// check for errors
throw new Exception('A database error has occured!');
}
return $q->fetch(PDO::FETCH_ASSOC);
}
Then just do:
$r=dbRow('select * from user_accounts where email=? and verification_code=?',
array($_GET['email'], $_GET['verification_code'])
);
There is a problem in password_reminder.php.
In stead of verificatio_code, it was using activation_code.

Categories