I'm completely new to PHP. I am creating a website.
In several pages, I start my code with :
<?php
$dsn = "mysql:host=localhost;dbname=db;";
$options = [
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
];
try {
$pdo = new PDO($dsn,'root','', $options);
} catch (Exception $e) {
die('Erreur : ' . $e->getMessage());
}
//rest of the php + html code here
I would like to unify this code in one php file and call $pdo in every page. But I want to do it in a safe way. Can anyone help.
Better using a singleton for this. This way you avoid opening multiple database connections:
<?php
class Database
{
public static $pdo;
public static function getPDO()
{
if (null === self::$pdo) {
$dsn = "mysql:host=localhost;dbname=db;";
$options = [
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
];
try {
self::$pdo = new PDO($dsn,'root','', $options);
} catch (Exception $e) {
die('Erreur : ' . $e->getMessage());
}
}
return self::$pdo;
}
//protected constructor
protected function __construct()
{
}
}
Then use
require_once('path/to/database.php'); //the file with Database class
$pdo = Database::getPDO();
Of course, you need to include this file/class in every page.
Also, isn't a good practice to hardcode your database credentials. Better load from some .env, getenv() or .ini file.
Save your connection code in a file. Let's call it db_connect.php. Then, using the proper path add the following to each file needing a database connection:
include('path/to/db_connect.php');
You may also use include_once(), require() or require_once() depending on what your needs are.
Related
I have multiple classes and most of them need to connect to database,
How can I set PDO options/host/dbname etc only once and then use it in every class while having the following in mind:
I don't want to wrap PDO
I need to close the PDO connection after each query ($db=null), so I simply cannot just use $db = new PDO(...) and then pass $db to my classes
I want to skip having this in every class that needs to connect to the database:
<?php
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
throw new \PDOException($e->getMessage(), (int)$e->getCode());
}
If you want complete control over opening and closing connections then I suggest we only centralise the $dsn, $user, $pass and $options variables. I'm assuming there are also variables like $host, $db and $charset which you did not reveal to us but lets add them.
Lets call this file global_db.php:
<?php
$host = "127.0.0.1";
$db = "mydb";
$charset = "UTF-8";
$user = "root";
$pass = "";
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
That looks like we have all the goods there, keep in mind I am not testing this so we might get a syntax error or two.
Now in our classes or other php files where we want to open a connection.
Lets call this page fooClass.php
<?php
require_once 'global_db.php';
class FooClass {
public function __construct() {
try {
$pdo = new PDO(
$GLOBALS['dsn'],
$GLOBALS['user'],
$GLOBALS['pass'],
$GLOBALS['options']);
} catch (\PDOException $e) {
throw new \PDOException($e->getMessage(), (int)$e->getCode());
}
}
}
That should do the trick or at least give you a general idea on where to go from here on your own route. There are many other ways to accomplish similar but no need to overcomplicate things.
nJoy!
I've seen variations of this code all over the place, including many S.O. posts:
class db extends PDO {
public function __construct( $dbconf ) {
$options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => $dbconf['persist'] ? true : false
);
try {
parent::__construct('mysql:host='. $dbconf['dbhost'] .';port=3306;dbname='. $dbconf['dbname'] .';' , $dbconf['dbuser'],
$dbconf['dbpass'],
$options);
} catch (PDOException $e) {
$this->myerror( $e->getMessage() );
// echo 'Connection failed ... '. $e->getMessage();
}
}
...
private function myerror( $error ) {
echo 'Connection failed ... '. $error;
}
}
The class is instantiated with $db = new db( $config );, and it works great if the connection is valid, but it seems PDOException doesn't actually work if the connection fails. The catch totally fails to execute the $this->myerror(...) function! Instead of the useful $e->getMessage() that says "Connection failed ... Access denied for user blah", I get a PHP Fatal error: Call to a member function myerror() on a non-object in /.../lib/pdo.class.php on line 16.
If I comment out the first line in the catch and uncomment the echo, it works as expected, reporting the reason for the connection error. Why would the message be available in the catch but not in the simple myerror class function?
This post ... PHP, PDO, and Exceptions ... seems applicable, but doesn't explain much. Is catch (PDOException $e) obsolete, or dysfunctional under certain circumstances? How do I keep get this to work?
The reason being is PDO::__construct only creates the object upon successful connection. So when you call the parent constructor and it fails your object doesn't exist anymore. You should use self::myerror() to access the error function statically in this case.
There shouldn't be myerror() function at all. A database layer scarcely need it's own error handler.
And constructor have to be
public function __construct( $dbconf ) {
$dsn = "mysql:";
$dsn .= "host=". $dbconf['dbhost'].";";
$dsn .= "dbname=". $dbconf['dbname'].";";
$dsn .= "port=". $dbconf['dbport'].";";
$dsn .= "charset=".$dbconf['dbcharset'];
$options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES '.$dbconf['charset'],
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => (bool)$dbconf['persist'],
);
parent::__construct($dsn, $dbconf['dbuser'], $dbconf['dbpass'],$options);
}
So, I'm working on a project that requires a database connection. I've chosen to use PDO for its versatility and need to figure out how to set up the connection. Currently I'm going for something like this:
class Database {
private static $db;
static function initDB() {
if(!is_object(self::$db) || get_class(self::$db) != 'PDO') {
include('core/db.php');
try {
$db = new PDO($database, $username, $password);
} catch(PDOException $e) {
print("<br />Could not establish database connection. Error message: ".$e->getMessage()."<br />");
die();
}
}
//Try the transaction
/*
if($transaction = $db::query(PDO::quote($value)))
$db::query(PDO::quote("INSERT INTO log VALUES ('".Authorization::$user."','".PDO::quote($value)."', 'Success')"));
else
$db::query(PDO::quote("INSERT INTO log VALUES ('".Authorization::$user."','".PDO::quote($value)."', 'Failure')"));*/
}
}
So, this pretty much reveals one of the concepts I don't really know: singletons and static classes/objects. Any way to set up a database connection using OO best practices that initializes with the script via some kind of __construct method?
A database connection should not be either static or a singleton. This merely introduces another form of global state, which is bad for unit-testing and does hide obvious dependencies.
The right way here would be is to inject an instance of PDO into the classes that need it. You adhere the Single-Responsibility Principle and Dependency Injection.
Note, you should never log errors and do include() inside PDOAdapter constructor because its masked violation of the Single-Responsibility Principle
So, this would look like this:
final class PDO_Provider extends PDO
{
/**
* Constructor. Inits PDO
*
* #param array $params
* #return void
*/
public function __construct(array $params)
{
try {
extract($params);
parent::__construct(sprintf('mysql: host=%s; dbname=%s', $host, $database), $user, $password);
$this->setAttribute(parent::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES UTF8');
$this->setAttribute(parent::ATTR_ERRMODE, parent::ERRMODE_EXCEPTION);
$this->setAttribute(parent::ATTR_EMULATE_PREPARES, false);
$this->setAttribute(parent::ATTR_DEFAULT_FETCH_MODE, parent::FETCH_ASSOC);
} catch(PDOException $e) {
die($e->getMessage());
}
}
}
And you would use it like this,
<?php
$sql_config = array(
'host' => 'localhost',
'user' => 'root',
'password' => '',
'database' => '_DB_NAME_',
);
// <- Or you can include that, like
$sql_config = include(__DIR__ . '/core/db_params.php');
$pdoProvider = new PDO_Provider($sql_config);
$user = new User_Login($pdoProvider); // the point here is to inject an instance of $pdoProvider. User_Login is actually irrelevant
if you want to use a normal object instead of sigleton, try something like this:
class PDOConnector{
protected $connection;
function __construct($host, $user, $pass, $db_name)
{
//create database connection
try{
$this->connection = new PDO('mysql:host='.$this->host.';dbname='.$this->db_name.';charset=utf8', $this->user, $this->pass,array(PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
}
catch(PDOException $ex) {
echo "An Error occured : ".$ex->getMessage();
}
}
}
I've seen variations of this code all over the place, including many S.O. posts:
class db extends PDO {
public function __construct( $dbconf ) {
$options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => $dbconf['persist'] ? true : false
);
try {
parent::__construct('mysql:host='. $dbconf['dbhost'] .';port=3306;dbname='. $dbconf['dbname'] .';' , $dbconf['dbuser'],
$dbconf['dbpass'],
$options);
} catch (PDOException $e) {
$this->myerror( $e->getMessage() );
// echo 'Connection failed ... '. $e->getMessage();
}
}
...
private function myerror( $error ) {
echo 'Connection failed ... '. $error;
}
}
The class is instantiated with $db = new db( $config );, and it works great if the connection is valid, but it seems PDOException doesn't actually work if the connection fails. The catch totally fails to execute the $this->myerror(...) function! Instead of the useful $e->getMessage() that says "Connection failed ... Access denied for user blah", I get a PHP Fatal error: Call to a member function myerror() on a non-object in /.../lib/pdo.class.php on line 16.
If I comment out the first line in the catch and uncomment the echo, it works as expected, reporting the reason for the connection error. Why would the message be available in the catch but not in the simple myerror class function?
This post ... PHP, PDO, and Exceptions ... seems applicable, but doesn't explain much. Is catch (PDOException $e) obsolete, or dysfunctional under certain circumstances? How do I keep get this to work?
The reason being is PDO::__construct only creates the object upon successful connection. So when you call the parent constructor and it fails your object doesn't exist anymore. You should use self::myerror() to access the error function statically in this case.
There shouldn't be myerror() function at all. A database layer scarcely need it's own error handler.
And constructor have to be
public function __construct( $dbconf ) {
$dsn = "mysql:";
$dsn .= "host=". $dbconf['dbhost'].";";
$dsn .= "dbname=". $dbconf['dbname'].";";
$dsn .= "port=". $dbconf['dbport'].";";
$dsn .= "charset=".$dbconf['dbcharset'];
$options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES '.$dbconf['charset'],
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => (bool)$dbconf['persist'],
);
parent::__construct($dsn, $dbconf['dbuser'], $dbconf['dbpass'],$options);
}
I'm using this function to connect to my MySQL db when needed, and also to re-use the same connection object for any further query I might need in the same php script.
function cnn() {
static $pdo;
if(!isset($pdo)) {
try {
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
$pdo->setAttribute(PDO::ATTR_TIMEOUT, 30);
$pdo->setAttribute(PDO::ATTR_PERSISTENT, true);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
return $pdo;
} catch(PDOException $e) {
http_response_code(503);
echo $e->getCode.': '.$e->getMessage();
die(); //or whatever error handler you use
}
} else {
return $pdo;
}
}
First query (object is created)
echo cnn()->query('SELECT firstname FROM user WHERE id=4;')->fetch(PDO::FETCH_COLUMN)
Second query (object is reused)
echo cnn()->query('SELECT title FROM news WHERE id=516;')->fetch(PDO::FETCH_COLUMN)
Do you agree on this approach? Do you think it can be optimized? Thanks for your opinions.
I agree with the method, though many people will tell you that this "singleton" approach is bad, bad.
However, I disagree with your implementation. It should be:
function cnn() {
static $pdo;
if(!$pdo) {
$conf = array(PDO::ATTR_TIMEOUT => 30,
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
);
$dsn = 'mysql:host='.DB_HOST.';dbname='.DB_NAME;
$pdo = new PDO($dsn, DB_USER, DB_PASS, $conf);
}
return $pdo;
}
Also, it looks sensible to move handler code into handler (and of course without echoing the error unconditionally!)
function my_exceptionHandler($exception) {
http_response_code(503);
if (ini_get('display_errors')) {
echo $e->getMessage().$e->getTrace();
} else {
log_error($e->getMessage().$e->getTrace());
}
die(); //or whatever error handler you use
}
set_exception_handler("my_exceptionHandler");
Also, I'd extend it to accept a parameter to make several connections possible.