I'm starting to build a little PDO wrapper I will be using for my application. However, when I started coding, I bumped into an issue I can't seem to resolve.
The problem I have is that PDOStatement's execute() is returning false and I don't know if there's something wrong with the value binding or the execution. I've tested the query (which anyway is very simple) and it works fine. Connection to the server is also working fine.
I hope you can help! Here's my code:
<?php
class DataBase {
private $PDO;
private static $instancia;
public static function getInstance() {
if (!self::$instancia instanceof self) {
self::$instancia = new self;
}
return self::$instancia;
}
function __construct() {
$configuracion = Configuracion::getInstance();
// echo "mysql:host={$configuracion->dbHost};dbname=mysql", $configuracion->dbUser, $configuracion->dbPassword;
try {
$this->PDO = new PDO("mysql:host={$configuracion->dbHost};dbname=mysql", $configuracion->dbUser, $configuracion->dbPassword);
debug("conectado a la db", __FILE__, __LINE__);
} catch (PDOException $e) {
debug($e->getMessage(), __FILE__, __LINE__);
}
}
function selectDistanceFromDistances($a, $b) {
$sentencia = $this->PDO->prepare('SELECT distance FROM distances WHERE a = ? AND b = ?;');
// debug($sentencia->execute(array($a, $b)));
$sentencia->bindValue(1, 15, PDO::PARAM_INT);
$sentencia->execute();
$this->PDO->errorInfo();
$resultado = $sentencia->fetchAll();
return $resultado;
}
}
?>
Thanks!
$sentencia->execute();
$this->PDO->errorInfo();
You're doing the execute, and then asking for error info, but you aren't actually doing anything with the error info! You seem to have a debug function, so that seems like a good idea to use here.
Your query has two placeholders, but you've only bound one of them, so that's probably what the error is.
You might want to consider turning on exceptions mode and using try/catch. PDO is silent by default, outside of the constructor.
Related
In anticipation of mysql_query being deprecated PHP 5.5.0, I have been working on a class to handle all my DB queries :
class DataBaseClass {
//.....some other function and variables declared here....
function GetConnection() {
try {
$this->conn = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME, DB_USER, DB_PASS);
$this->conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
}
catch(PDOException $e) {
echo $e->getMessage();
}
return $this->conn;
}
function Query($str_sql, $arr_parameters = array()) {
try {
$this->str_mysql_error = $this->int_num_rows = $this->int_num_affected_rows = $this->int_mysql_insert_id = '';
if (count($arr_parameters) > 0) {
$obj_result = $this->conn->prepare($str_sql);
$obj_result->execute($arr_parameters);
} else {
$obj_result = $this->conn->query($str_sql);
}
}
catch(PDOException $e) {
$this->str_mysql_error = $e->getMessage() . $str_sql;
}
}
}
Then I have another class to create new user:
class AddNewUser {
//.....some other function and variables declared here....
function InsertUser() {
$str_sql = "INSERT INTO (uname, name, email, pass, user_regdate, theme) VALUES )";
$_SESSION['db_connection']->Query($str_sql, '');
}
}
Now on my main user creation page I have :
$_SESSION['db_connection'] = new DataBaseClass;
//Reason I used $_SESSION to store my DB object, is so that it can be accessible everywhere.
//Did not want to use "global" everywhere. Not sure if this is he best way???
$cls_new_user = new AddNewUser ();
$cls_new_user->InsertUser(); //Does not raise PDOExecption although SQL cleary wrong inside this method
if ( $_SESSION['db_connection']->str_mysql_error) {
//show error in error div
}
$str_sql = "SELECT some wrong SQL statment";
$_SESSION['db_connection']->Query($str_sql); // This does raise PDOExecption
if ( $_SESSION['db_connection']->str_mysql_error) {
//show error in error div
}
I'm not sure why the DB class function "Query" would not raise an exception on clearly wrong SQL when called from another class. But same function called from main page code (not inside function / class) raises and exception error.
Also, the "InsertUser" function does not execute / insert anything into DB even if SQL correct.
Could it be scope related, or the fact that I'm trying to enforce global scope of my DB object by putting it in $_SESSION ??
Am I going about this the wrong way? Reason for going class route to encapsulate all my DB calls was to avoid any deprecation issues in future - only having to update class.
Make your function this way.
function Query($str_sql, $arr_parameters = array()) {
$stmt = $this->conn->prepare($str_sql);
$stmt->execute($arr_parameters);
}
I am pretty sure that exception would be thrown
The only issue can be with catching exceptions, not throwing. And it could be caused by Namespace, not scope. To be certain, you can always prepend all PDO calls with a slash:
\PDO::FETCH_ASSOC
\PDOException
etc.
I have been looking in to this almost all day.. and can't seem to find the values returned anywhere. Can somebody tell me:
What values do PDO::getAttribute(PDO::ATTR_CONNECTION_STATUS); return?
Is it possible to rely on its result to determinate if the connection is still alive?(And eventually, what could I use to check if the connection is still alive?)
Finally! it turns out that the mysqli::ping() function could be implemented within PDO as follows:
class PDOExtended extends PDO {
public function __construct($dsn, $user, $pass, $options = array())
{
$this->link = parent::__construct($dsn, $user, $pass, $options);
$this->link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)
}
// some methods
public function isConnected()
{
try {
return (bool) $this->link->query('SELECT 1+1');
} catch (PDOException $e) {
return false;
}
}
//some other methods
}
REASON:
PDO::query(); returns array containing the results or false, In the current case it won't return nothing, cuz the connection is dead and PDO should throw an exception at us. And that is what we are expecting. The catch block will return false and and will not stop the execution of our script. The query used
SELECT 1+1;
will return 2 always and it is good to rely on due to the fact that it is calculated on the DB side. No connection, no result! It is not an overkill because it is very simple query and most of the databases (on normal shared host) are on localhost it will not take more than 0.0000s which is not much of a performance issue. Have not tested it with transactions yet, but should do the trick still.
I'm writing a web application (PHP) for my friend and have decided to use my limited OOP training from Java.
My question is what is the best way to note in my class/application that specific critical things failed without actually breaking my page.
My problem is I have an Object "SummerCamper" which takes a camper_id as it's argument to load all of the necessary data into the object from the database. Say someone specifies a camper_id in the query string that does not exist, I pass it to my objects constructor and the load fails. I don't currently see a way for me to just return false from the constructor.
I have read I could possibly do this with Exceptions, throwing an exception if no records are found in the database or if some sort of validation fails on input of the camper_id from the application etc.
However, I have not really found a great way to alert my program that the Object Load has failed. I tried returning false from within the CATCH but the Object still persists in my php page. I do understand I could put a variable $is_valid = false if the load fails and then check the Object using a get method but I think there may be better ways.
What is the best way of achieving the essential termination of an object if a load fails? Should I load data into the object from outside the constructor? Is there some sort of design pattern that I should look into?
Any help would be appreciated.
function __construct($camper_id){
try{
$query = "SELECT * FROM campers WHERE camper_id = $camper_id";
$getResults = mysql_query($query);
$records = mysql_num_rows($getResults);
if ($records != 1) {
throw new Exception('Camper ID not Found.');
}
while($row = mysql_fetch_array($getResults))
{
$this->camper_id = $row['camper_id'];
$this->first_name = $row['first_name'];
$this->last_name = $row['last_name'];
$this->grade = $row['grade'];
$this->camper_age = $row['camper_age'];
$this->camper_gender = $row['gender'];
$this->return_camper = $row['return_camper'];
}
}
catch(Exception $e){
return false;
}
}
A constructor in PHP will always return void. This
public function __construct()
{
return FALSE;
}
will not work. Throwing an Exception in the constructor
public function __construct($camperId)
{
if($camperId === 1) {
throw new Exception('ID 1 is not in database');
}
}
would terminate script execution unless you catch it somewhere
try {
$camper = new SummerCamper(1);
} catch(Exception $e) {
$camper = FALSE;
}
You could move the above code into a static method of SummerCamper to create instances of it instead of using the new keyword (which is common in Java I heard)
class SummerCamper
{
protected function __construct($camperId)
{
if($camperId === 1) {
throw new Exception('ID 1 is not in database');
}
}
public static function create($camperId)
{
$camper = FALSE;
try {
$camper = new self($camperId);
} catch(Exception $e) {
// uncomment if you want PHP to raise a Notice about it
// trigger_error($e->getMessage(), E_USER_NOTICE);
}
return $camper;
}
}
This way you could do
$camper = SummerCamper::create(1);
and get FALSE in $camper when the $camper_id does not exist. Since statics are considered harmful, you might want to use a Factory instead.
Another option would be to decouple the database access from the SummerCamper altogether. Basically, SummerCamper is an Entity that should only be concerned about SummerCamper things. If you give it knowledge how to persist itself, you are effectively creating an ActiveRecord or RowDataGateway. You could go with a DataMapper approach:
class SummerCamperMapper
{
public function findById($id)
{
$camper = FALSE;
$data = $this->dbAdapter->query('SELECT id, name FROM campers where ?', $id);
if($data) {
$camper = new SummerCamper($data);
}
return $camper;
}
}
and your Entity
class SummerCamper
{
protected $id;
public function __construct(array $data)
{
$this->id = data['id'];
// other assignments
}
}
DataMapper is somewhat more complicated but it gives you decoupled code which is more maintainable and flexible in the end. Have a look around SO, there is a number of questions on these topics.
To add to the others' answers, keep in mind that you can throw different types of exceptions from a single method and handle them each differently:
try {
$camper = new SummerCamper($camper_id);
} catch (NoRecordsException $e) {
// handle no records
} catch (InvalidDataException $e) {
// handle invalid data
}
Throwing an exception from the constructor is probably the right approach. You can catch this in an appropriate place, and take the necessary action (e.g. display an error page). Since you didn't show any code, it's not clear where you were catching your exception or why that didn't seem to work.
try {
$camper = new SummerCamper($id);
$camper->display();
} catch (NonexistentCamper $ex) {
handleFailure($ex);
}
I've been playing around with PDO for the last few days, I'm working on a small CMS system to teach myself OOP skills, but even though it's only a small CMS, I want it to be able to handle whatever the web can throw at it.
This is what I've come up with so far, I'm going to add connection pooling to the constructor to enable large amounts of concurrent connects on demand. I'm very new to this OOP stuff so I'm wanting a little advise and critism, no doubt I've done something terribly wrong here.
I took the top answer to Global or Singleton for database connection? as the base design, although I've added a private constructor as I want to use $this->dbConnectionInstance throughout the class for numerous helper functions to use.
Thanks very much for your time, I really will appreciate any advise you can give me,
-Drew
// Usage Example: $dbconn = dbManager::getConnection();
// $dbconn->query("SELECT * FROM accounts WHERE id=:id", "':id' => $id");
<?php
class dbManager {
private static $dbManagerInstance;
private $dbConnectionInstance;
private $stime;
private $etime;
public $timespent;
public $numqueries;
public $queries = array();
public static function getManager(){
if (!self::$dbManagerInstance){
self::$dbManagerInstance = new dbManager();
}
return self::$dbManagerInstance;
}
// Server details stored in definition file
private function __construct($db_server=DB_SERVER, $db_user=DB_USER, $db_pass=DB_PASS, $db_params=array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")) {
if(!$this->dbConnectionInstance)
{
try{
$this->dbConnectionInstance = new PDO($db_server, $db_user, $db_pass, $db_params);
$this->dbConnectionInstance->setAttribute(PDO::ATTR_PERSISTENT, PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
$this->dbConnectionInstance = null;
die($e->getMessage());
}
}
return $this->dbConnectionInstance;
}
private function __destruct(){
$this->dbConnectionInstance = null;
}
private function query($sql, $params = array()) {
$this->queries[] = $sql;
$this->numqueries++;
$this->sTime = microtime();
$stmt = $this->dbConnectionInstance->prepare($sql);
$stmt->execute($params);
$this->eTime = microtime();
$this->timespent += round($this->eTime - $this->sTime, 4);
return $stmt;
}
}
?>
Thank you both for your suggestions, I've now added the rollback and commit into my exception handling, I'm just researching the use of buffered queries, I'm not entirely sure what ths will give me?
Looks good, I would add rollback functionality, along with the buffered query/errorInfo suggestions (If you're using a RDBMS that supports transactions):
try {
$this->dbConnectionInstance->beginTransaction();
$stmt = $this->dbConnectionInstance->prepare($sql);
$stmt->execute($params);
$this->dbConnectionInstance->commit();
}catch(PDOException $e){
$this->dbConnectionInstance->rollback();
}
commit() , beginTransaction()
EDIT: added links below for more info on buffered queries:
mysql performance blog
pdo mysql buffered query support
stack overflow: pdo buffered query problem
The code you have dosent look too bad. however if i could make a couple small changes (mainly error handling).
both the prepare and execute statements will return false on error. and you can access the error from $this->dbConnectionInstance->errorInfo() in your example above.
also if you plan on using any large queries I suggest using a buffered query: PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true
looks like a good start. Good luck on your CMS.
I built this class to work with PDO, to make SQL queries 'easier' and less to worry about.
Here are my thoughts
Should it be more like class DB extends PDO?
Is the query method too big? Should it be split into private methods which are called.. is this what is known as loose coupling?
Is my way for detecting a SELECT query too ugly for it's own good?
What other problems are evident? As I am sort of learning-as-I-go, I'm sure I could have overlooked a lot of potential problems.
Thank you
`
class Db
{
private static $_instance = NULL;
private function __construct() {
// can not call me
}
private function __clone() {
// no!
}
public static function getInstance() {
if (!self::$_instance)
{
try {
self::$_instance = new PDO('mysql:host=' . CONFIG_MYSQL_SERVER . ';dbname=' . CONFIG_MYSQL_DATABASE, CONFIG_MYSQL_USERNAME, CONFIG_MYSQL_PASSWORD);;
self::$_instance-> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
trigger_error($e->getMessage());
}
}
return self::$_instance;
}
public static function query($query /*string*/, $bindings = NULL)
{
$queryPortion = substr($query,0, 6);
try {
if ($bindings) {
$prepared = self::getInstance()->prepare($query);
foreach($bindings as $binding=>$data) { // defaults to string
if (!is_array($data)) {
$prepared->bindParam($binding, $data);
} else {
switch(count($data)) {
case 1:
$prepared->bindParam($binding, $data['value']);
break;
case 2:
$prepared->bindParam($binding, $data['value'], $data['dataType']);
break;
case 3:
$prepared->bindParam($binding, $data['value'], $data['dataType'], (int)$data['length']);
break;
default:
trigger_error('An error has occured with the prepared statement bindings.');
return false;
break;
}
}
}
$prepared->execute();
return $prepared->fetchAll(PDO::FETCH_ASSOC);
} else if (String::match($queryPortion, 'select')) { // if this is a select query
$rows = self::getInstance()->query($query);
return $rows->fetchAll(PDO::FETCH_ASSOC);
} else {
return self::getInstance()->exec($query);
}
}
catch(PDOException $e)
{
trigger_error($e->getMessage());
}
}
public static function getLastInsertId()
{
try {
self::getInstance()->lastInsertId();
}
catch(PDOException $e)
{
trigger_error($e->getMessage());
}
}
public static function disconnect()
{
// kill PDO object
self::$_instance = NULL;
}
}
It's not bad and as it's been said it might help for small applications although it's mostly a very thin abstraction on another abstraction. It's not bringing a lot of others functionalities.
Something you might want to consider, amongst other things:
As this is PHP5 code, use exceptions instead of trigger_error and set_exception_handler if necessary until exceptions are more widespread, but it's definitely cleaner and more future-proof.
You are using a singleton, it's not a bad thing necessarily but in this case, for example, one shortcoming will be that you'll only be able to handle one connection to one database.
I don't know if you make use of stored procedures, but a stored procedure might return a result set through the query() method too.
You have two semi-colons (;;) at the end of your new PDO line.
That being said, I don't think your query method is too big and there's not much that could be recalled from elsewhere in there at the moment. Though as soon as you see two or three lines that could be called from another function, split it. That's a good way to DRY.
Yes and No.
It is good code for a simple quick and dirty application.
The problem comes when you use this in a more complex structured application.
Where the error handling will vary depending on which sql you are executing.
Also any severe errors will show up as "problem at line 999" type errors
where 999 is in your super duper routine and you will have difficulty tracing it back
to a particular sql request.
Having said that I do this sort of thing myself all the time on small projects.
Here's what I've used (just replace the references to Zzz_Config with $GLOBALS['db_conf'] or something):
/**
* Extended PDO with databse connection (instance) storage by name.
*/
class Zzz_Db extends PDO
{
/**
* Named connection instances.
*
* #var array
*/
static private $_instances;
/**
* Retrieves (or instantiates) a connection by name.
*
* #param string $name Connection name (config item key).
* #return Zzz_Db Named connection.
*/
static public function getInstance($name = null)
{
$name = $name === null ? 'db' : "db.$name";
if (!isset(self::$_instances[$name])) {
if (!$config = Zzz_Config::get($name)) {
throw new RuntimeException("No such database config item: $name");
}
if (!isset($config['dsn'])) {
if (!isset($config['database'])) {
throw new RuntimeException('Invalid db config');
}
$config['dsn'] = sprintf('%s:host=%s;dbname=%s',
isset($config['adapter']) ? $config['adapter'] : 'mysql',
isset($config['host']) ? $config['host'] : 'localhost',
$config['database']);
}
$db = self::$_instances[$name] = new self(
$config['dsn'],
isset($config['username']) ? $config['username'] : null,
isset($config['password']) ? $config['password'] : null);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//$db->setAttribute(PDO::ATTR_STATEMENT_CLASS, 'Zzz_Db_Statement');
if ($db->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql') {
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$db->exec('SET CHARACTER SET utf8');
}
}
return self::$_instances[$name];
}
}
Usage whould be:
$db = Zzz_Db::getInstance(); // or Zzz_Db::getInstance('some_named_db')
$stmt = $db->prepare('SELECT ...
The goal is to keep the db configuration in an *.ini file (editable by a non-coder).
I went the other way and made a class that extends PDO with a bunch of wrapper functions around prepare()/execute(), and it's much nicer than the built in functions (though that's a bit subjective...).
One other thing: you should set PDO::ATTR_EMULATE_PREPARES to false unless you're using a really old version of mysql (<=4.0). It defaults to true, which is a huge headache and causes things to break in obscure ways... which I'm guessing is the reason you've got a huge wrapper around bindParam() in the first place.
To answer your question, if it is a good code or not, ask yourself:
What is the added value of my code compared to using PDO directly?
If you find a good answer, go for using your code. If not, I would stick with PDO.
Also try considering implementing Zend Framework's DB class which works on its own and supports PDO.