1

I'm newbie on PDO. I watch several videos. And i want to use my own database class as previous times. I want to use PDO instead of mysql* functions. I know that mysql* functions are deprecated in PHP 5.5

I am not working on my old database class. Because it'll be more harder for a newbie. Now i only create a connection. And i want to select my products.

Here is my code :

class Database extends PDO {
    private $_server         = "localhost";
    private $_user           = "root";
    private $_password       = "";
    private $_db             = "demo";

    private $_engine = 'mysql';
    private $link = NULL;


    public function __construct() {
        $dsn = $this->_engine.':dbname='.$this->_db.';host='.$this->_server.';charset=utf8';

        try {
            $this->link = new PDO($dsn, $this->_user, $this->_password);
            $this->link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch (PDOException $e) {
            echo 'Connection failed: ' . $e->getMessage();
        }
    }
}

header('Content-Type: text/html; charset=utf-8');
$DB = new Database();

$sql = "SELECT * FROM product";
$q  = $DB->query($sql);
while($r = $q->fetch(Database::FETCH_ASSOC)){
    $products[] = $r;
}

echo "<pre>";
print_r($products);
echo "</pre>";

I couldn't fetch my products. I'm recieving errors.

Warning: PDO::query(): SQLSTATE[00000]: No error: PDO constructor was not called in /var/www/dt/includes/class/class.database.php on line 52

Fatal error: Call to a member function fetch() on a non-object in /var/www/dt/includes/class/class.database.php on line 53

What is my mistake ? And what should i do ?

cihanblog
  • 58
  • 1
  • 9
  • 1
    remove the `or die("Failed!")` `PDO` will then throw an exception (hence `PDO::ERRMODE_EXCEPTION`). Update your question with this result. – AlexP Jul 30 '13 at 11:26
  • i don't think you need a class to use PDO unless this is for learning. – Erdem Ece Jul 30 '13 at 11:27
  • 1
    Your class extends `PDO` and also instantiates a new PDO instance in the constructor. This new instance is not actually set to a property within the Database object you create. In essence your Database class does not actually create the connection to the Database. Use `parent::__construct($dsn, $this->user, $this->password);` instead of creating a new PDO instance. – David Barker Jul 30 '13 at 11:28
  • Dear @AlexP, i updated my question. Now i'm getting an internal server error 500. – cihanblog Jul 30 '13 at 11:32
  • 2
    A 500 Internal Server Error means that your script is throwing an error but you haven't configured PHP to display error messages. That's something you need to fix before you go further; it's impossible to code without the aid of error messages. Here's a [brief explanation](http://stackoverflow.com/a/5680885/13508). – Álvaro González Jul 30 '13 at 11:36
  • @cihanblog Alvaro is correct, look into `error_reporting`. – AlexP Jul 30 '13 at 11:39
  • thank you for helping about error_reporting. Now i edited my question. You can see errors. – cihanblog Jul 30 '13 at 11:50

2 Answers2

3

Some random thoughts about your code.

Misuse of class inheritance

class Database extends PDO {
    private $link = NULL;

    public function __construct() {
            $this->link = new PDO($dsn, $this->_user, $this->_password);

The Database instance itself will be a PDO object so it doesn't make any sense to have a PDO object inside another PDO object (the outer of which will not even work properly since it hasn't been initialized). All you need is this:

class Database extends PDO {
    public function __construct() {
        parent::__construct($dsn, $this->_user, $this->_password);

Uninitialised attributes

public function __construct() {
    $dsn = $this->_engine.':dbname='.$this->_db.';host='.$this->_server.';charset=utf8';

Where do all those $this-> variables come from? You probably want to pass them as constructor parameters.

Break error handling

try {
    $this->link = new PDO($dsn, $this->_user, $this->_password);
    $this->link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}

Your class degrades connection errors into plain strings on standard output. Now, there's no way to log or detect connection errors.

Edit: You ask how to detect or log connection errors from your class. There're many things you could do but the simplest way is to do... nothing. Seriously. If you just remove the try/block from the constructor, PDO exceptions will bubble up automatically and you'll be able handle exceptions wherever you need them. E.g.:

try{
    $DB = new Database();
}catch(PDOException $e){
    // I want to handle connection errors in a special way
    // I'll log them here and I'll redirect the user to a static error page
    // That's something I couldn't do from the class constructor because I'd be missing context
}

... or even:

try{
    $DB = new Database();
    $sql = "SELECT * FROM product";
    $q  = $DB->query($sql);
    while($r = $q->fetch(Database::FETCH_ASSOC)){
        $products[] = $r;
    }
}catch(Exception $e){
    // I prefer to handle all exceptions the same way, no matter the source
}

... but I'd go for the first approach: connection errors are the only PDOException instances you can expect to find in production and there's often nothing you can do about it so it makes sense to give them a special treatment.

Álvaro González
  • 142,137
  • 41
  • 261
  • 360
  • I was going to yell at the OP for misuse of dependencies and not injecting them (and also explaining `parent::`), but this is much better. – Amelia Jul 30 '13 at 11:53
  • how can i change my class to detect or log connection errors. Any idea ? – cihanblog Jul 30 '13 at 12:01
  • @cihanblog - See my edit. Please note these are just ideas. The important bit is that you understand the techniques and make your own informed decisions :) – Álvaro González Jul 30 '13 at 12:15
  • @ÁlvaroG.Vicario thanks for your edit. But i confused a litte bit. Because i have class which name is Database. And i create a construct function. Inside construct function we used PDO construct. And after this line. I'll use try/catch block. But you wrote $DB = new Database(); You mean i'll use database class inside itself ? Or i misunderstood ? – cihanblog Jul 30 '13 at 12:24
  • @cihanblog - That's your own code. It doesn't belong to the class code, it's how you actually *use* the class. – Álvaro González Jul 30 '13 at 12:33
2

First get your error reporting sorted. You need to be able to see the errors if you want to debug!

To do so you need to edit the following settings in php.ini

error_reporting display_errors

Make sure this is done in the php.ini file (rather than using ini_set() in code) and then restart the server.

With regards to your database abstraction, I cannot see any real benefit of extending the PDO class. Generally, I would only extend the class if there was some functionality within the parent that I wanted to overload.

What might be more useful is to encapsulate all calls to the PDO instance by wrapping it in your own custom class. The main benefit here is that there is no client code that deals directly with the PDO instance and you can change or swap out the implementation of the class without effecting the rest of your code.

One contrived example:

class Database
{
  protected $config = array();

  protected $pdo;

  public function __construct(array $config)
  {
    $this->config = $config;
  }

  protected function getPdo()
  {
    if (null == $this->pdo) {
      try {
        $config = $this->config;

        $dsn = $config['engine'].':dbname='.$config['database'].';host='.$config['host'].';charset=utf8';

        $this->pdo = new PDO($dsn, $config['user'], $config['password']);
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

      } catch (PDOException $e) {
        echo 'Connection failed: ' . $e->getMessage();
      }
    }
    return $this->pdo;
  }

  public function query($sql)
  {
    return $this->getPdo()->query($sql);
  }

}

$db = new Database(array(
  'engine' => 'mysql',
  'database' => 'databasename',
  'host' => 'localhost',
  'user' => 'fred',
  'password' => 'mypassword'
));
$results = $db->query('SELECT * FROM sometable');
AlexP
  • 9,906
  • 1
  • 24
  • 43
  • when i try your example. I got this error. Notice: Undefined property: Database::$_config in /var/www/dt/includes/class/class.database.php on line 40 Connection failed: could not find driver Fatal error: Call to a member function query() on a non-object in /var/www/dt/includes/class/class.database.php on line 56 – cihanblog Jul 30 '13 at 12:18
  • @cihanblog Fixed the typo (`$this->_config` should have been `$this->config`). It will now work, however I is only really meant to be an example of what I was trying to explain. – AlexP Jul 30 '13 at 12:21
  • thank you. Now it's working. But now i didn't decide that extending to PDO is a better way ? Or your method is better way ? – cihanblog Jul 30 '13 at 12:28
  • What we have created here is a `proxy`. A class that proxies all calls to the `PDO` instance. A good answer to if this is "better" than `inheritance` is here: http://stackoverflow.com/questions/3965334/proxy-pattern-vs-overriding#answer-3965590 – AlexP Jul 30 '13 at 12:33