Modifying / Adding extra stuff to PDO bindParam()? - php

does anyone know by any chance if there is a clean way (or any way at all) to change PDO's bindParam?
We're implementing extra security measures for our websites (filters for inputs) and so far it seems that the best way to add it to every single website we have efficiently (every website we have is different but the thing they have in common is they all use PDO) would be to somehow make PDO bindParam call our function on it's parameters, so that every single input in the bindParam would be filtered appropriately.
Thanks!

Solved this by extending PDO classes:
class CustomDBConnection {
private static $conn;
// either create a new connection or return an existing one
public static function getInstance() {
if (self::$conn == null) {
global $db_hostname, $db_database, $db_username, $db_password; // probably better to store these within this class but this was quicker
self::$conn = new CustomPDO("mysql:host=$db_hostname;dbname=$db_database;charset=utf8", $db_username, $db_password, array(PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
}
return self::$conn;
}
}
class CustomPDO extends PDO {
public function __construct($dsn, $username = null, $password = null, $driver_options = array()) {
parent::__construct($dsn, $username, $password, $driver_options);
// Attach customised PDOStatement class
$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('CustomPDOStatement', array($this)));
}
}
class CustomPDOStatement extends PDOStatement {
private $conn;
protected function __construct($conn) {
$this->conn = $conn; // this is most likely useless at this moment
}
public function bindParam($parameter, &$variable, $data_type = PDO::PARAM_STR, $length = null, $driver_options = null) {
$variable = InputProtection::detachEvilHTML($variable);
parent::bindParam($parameter, $variable, $data_type, $length, $driver_options);
}
public function bindValue($parameter, $value, $data_type = PDO::PARAM_STR) {
$value = InputProtection::detachEvilHTML($value);
parent::bindValue($parameter, $value, $data_type);
}
}
So I basically do $db = CustomDBConnection::getInstance(); now instead of $db = new PDO(.......);

Related

Migrate class from mysql to mysqli

I have one quick question. I have that class written in mysql extension my question is how can I migrate that class from mysql to mysqli object oriented style not procedural.
I'm new at this mysqli extension.
<?php
class DBController {
private $host = "";
private $user = "";
private $password = "";
private $database = "";
function __construct() {
$conn = $this->connectDB();
if(!empty($conn)) {
$this->selectDB($conn);
}
}
function connectDB() {
$conn = mysql_connect($this->host,$this->user,$this->password);
return $conn;
}
function selectDB($conn) {
mysql_select_db($this->database,$conn);
}
function runQuery($query) {
$result = mysql_query($query);
while($row=mysql_fetch_assoc($result)) {
$resultset[] = $row;
}
if(!empty($resultset))
return $resultset;
}
function numRows($query) {
$result = mysql_query($query);
$rowcount = mysql_num_rows($result);
return $rowcount;
}
}
?>
Having such class is a really good idea, but unfortunately moving from mysql_* API to mysqli class is not so simple. It requires a large code rewrite and change in thinking.
If you are planning the upgrade from mysql_* API it probably means you were using PHP 4 until now. If you were using PHP 5, you were using PHP 4 way of thinking. PHP has come a long way since then. We now have proper classes, namespacing, better error reporting, etc. When it comes to database interactions we now have two new extensions: mysqli and PDO. Out of these two you should be using PDO. However, if you are using a wrapper class like the one you are trying to create changing from mysqli to PDO should be very easy. For this reason alone such a wrapper class is a good idea.
The primary reason for mysqli extension was to keep a familiar syntax of the old extension and add necessary new features such as prepared statements and proper error reporting. If you were used to putting PHP variables directly into SQL queries, you must change your way of thinking. You should now use parameterized queries and bind the values separately.
A simple example of what your improved class could look like is this:
<?php
class DBController {
/**
* mysqli instance
*
* #var \mysqli
*/
private $mysqli;
public function __construct(
$host = null,
$username = null,
$passwd = null,
$dbname = null,
$charset = 'utf8mb4',
$port = null,
$socket = null
) {
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$this->mysqli = new \mysqli($host, $username, $passwd, $dbname, $port, $socket);
$this->mysqli->set_charset($charset);
}
public function runQuery(string $sql, array $params = []): ?array {
$stmt = $this->mysqli->prepare($sql);
if ($params) {
$stmt->bind_param(str_repeat("s", count($params)), ...$params);
}
$stmt->execute();
if ($result = $stmt->get_result()) {
return $result->fetch_all(MYSQLI_BOTH);
}
return null;
}
}
Warning: This is not a full implementation of such wrapper class. You most likely need to implement other methods and add more logic as per your needs. This is only for demonstration purposes.
First, in the construct method, we should execute 3 steps. Enable error reporting, create an instance of the mysqli class and set the correct charset (use utf8mb4 which was added in MySQL 5.5 and is now the standard charset).
Your runQuery() method now accepts 2 arguments. The first one is your SQL query with placeholders instead of interpolated values, and the second one is an array of the values to be bound.
Most likely you do not need selectDB() and you definitely do not need numRows(). If you want to know the number of rows in your retrieved array, you can count them in PHP using count().
Using such class is very simple.
$db = new DBController('localhost', 'username', 'password', 'db_name');
$result = $db->runQuery('SELECT Id, Name FROM table1 WHERE uuid=?', ['myuuid']);
if ($result) {
// Get the name of found record
echo $result[0]['Name'];
} else {
echo 'No records found';
}
If you wanted to switch the implementation to PDO, you can replace the class without changing the way you use it.
class DBController {
/**
* PDO instance
*
* #var \PDO
*/
private $pdo;
public function __construct(
$host = null,
$username = null,
$passwd = null,
$dbname = null,
$charset = 'utf8mb4',
$port = null,
$socket = null
) {
$dsn = "mysql:host=$host;dbname=$dbname;charset=$charset;port=$port;socket=$socket";
$options = [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_EMULATE_PREPARES => false,
];
$this->pdo = new PDO($dsn, $username, $passwd, $options);
}
public function runQuery(string $sql, array $params = []): ?array {
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll();
}
}
Warning: This is not a full implementation of such wrapper class. You most likely need to implement other methods and add more logic as per your needs. This is only for demonstration purposes.

PDO connection Not Working as required [duplicate]

This question already has answers here:
PHP parse/syntax errors; and how to solve them
(20 answers)
Closed 7 years ago.
Hi i am using PDO DB class to connect to database. But i really wonder if i am doing it correct or not. All connection set up is fine but i get error when i run a query
My Directory Structure is
/root
/dbops <-- Directory contains `config.php` -->
/dbfunctions <-- Directory contains `DBclass.php` & `DBFuncts.php` -->
Now contents of config.php are:
define( 'DB_HOST', 'localhost' );
define( 'DB_USERNAME', 'root');
define( 'DB_PASSWORD', '');
define( 'DB_NAME', 'testDB');
define('DB_CHAR', 'utf8');
function __autoload($class){
$parts = explode('__', $class);
$path = implode(DIRECTORY_SEPARATOR,$parts);
require_once $path . '.php';
}
DBclass.php contains:
class dbdunctions__DBclass{
public $instance = null;
public function __construct() {}
final private function __clone() {}
public static function instance()
{
if (self::$instance === null)
{
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => TRUE,
PDO::ATTR_STATEMENT_CLASS => array('myPDOStatement'),
);
$dsn = 'mysql:host='.DB_HOST.';dbname='.DB_NAME.';
charset='.DB_CHAR;
self::$instance = new PDO($dsn, DB_USERNAME, DB_PASSWORD,
$opt);
}
return self::$instance;
}
public static function __callStatic($method, $args) {
return call_user_func_array(array(self::instance(), $method), $args);
}
}
class myPDOStatement extends PDOStatement
{
function execute($data = array())
{
parent::execute($data);
return $this;
}
}
DBFuncts.php contains below:
class dbfunctions__DBFuncts
{
protected $_con;
public function __construct()
{
$db = new dbfunctions__DBclass();
$this->_con = $db->con;
}
function gotodb(array $data){
$result =
$this->_con::instance()->prepare($qry)->execute(array(/*parameters*/));
}
}
Now when query is fired with $result then i get following error
Parse error: syntax error, unexpected '::' (T_PAAMAYIM_NEKUDOTAYIM) in dbops/dbfunctions/DBFuncts.php on line 12
Please guide. I have already spent 2 hrs on this issue and googling around.
Instead of
$this->_con::instance()
You should be able to just do
$this->_con->instance()->prepare($qry)->execute(array(/*parameters*/));
Not sure if it's a typo for when you put the code in - but I did notice that in the DBclass.php you have class dbdunctions__DBclass - surely this should be class dbfunctions__DBclass() ?
Also there seems to be some other errors in your example code ... But lets tackle them one at a time :)
Try adjusting this way. I have it working on my server with some adjustments. Notably instantiating the connection in the __construct() of the dbdunctions__DBclass() class and assigning $this->_con to the self::$instance (dbdunctions__DBclass::$instance;):
class dbdunctions__DBclass
{
// Make the instance static
public static $instance = null;
public function __construct()
{
// Create the static connection in the construct
$this->init();
}
private function init()
{
if (self::$instance === null) {
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => TRUE,
PDO::ATTR_STATEMENT_CLASS => array('myPDOStatement'),
);
self::$instance = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.'; charset='.DB_CHAR, DB_USERNAME, DB_PASSWORD, $opt);
}
}
final private function __clone()
{
}
public static function __callStatic($method, $args)
{
return call_user_func_array(array(self::instance(), $method), $args);
}
}
class myPDOStatement extends PDOStatement
{
public function execute($data = array())
{
parent::execute($data);
return $this;
}
}
class dbfunctions__DBFuncts
{
protected $_con;
public function __construct()
{
// Create instance of database
$database = new dbdunctions__DBclass();
// Assign the connection to the $this->_con
$this->_con = dbdunctions__DBclass::$instance;
}
public function gotodb($statement = false,$bind = false)
{
// Incase the statement or bind is empty, return 0
if(empty($statement) || empty($bind))
return 0;
// Create the query with method chain
$query = $this ->_con->prepare($statement)
->execute($bind);
// Fetch results
while($row = $query->fetch())
$result[] = $row;
// If results return array else return 0 for consistency
return (!empty($result))? $result : 0;
}
}
// Instantiate
$dbFunc = new dbfunctions__DBFuncts();
// Use whatever you use to return a result here. This statement happens
// to work for my database...but likely not yours
print_r($dbFunc->gotodb("select * from `users` where `ID` = :ID",array(":ID"=>"29")));
class dbfunctions__DBFuncts
{
protected $_con;
public function __construct()
{
$db = new dbfunctions__DBclass();
$this->db = $db;
}
function gotodb(array $data){
$result =
$stmt = $this->db->prepare($qry);
$stmt->execute(array(/*parameters*/));
}
The PDO object gets created then made into a "member object". The gotodb object uses the "member object" PDO instance. Below is a sample of code from a site I'm working on which should help explain it better:
try {
$sql="
SELECT
id
, name
, description
FROM
ue_bug_project
";
// $stmt objected created by preparing the SQL query for execution, using the PDO object, which in this case is $this->db
$stmt = $this->db->prepare($sql);
// The execute method of the $stmt object is run executing the query, in this case no query data is bound as there is no user submitted data being used
$stmt->execute();
// The result set is grabbed in one hit and placed into the projects array (I really should have set up the $projects variable as an empty array beforehand)
$projects = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $projects;
}
// If the query fails with an error, the catch block is run, in this case i log the error.
catch (PDOException $e) {
error_log('Error when getting the list of projects!');
error_log(' Query with error: '.$sql);
error_log(' Reason given:'.$e->getMessage()."\n");
return false;
}
}
From the looks of your code there's probably no real need to place your own wrapper around the PDO class

PHP DataMapper pattern: My class needs an instance of PDO, I want to wrap it inside a Db class

here's what I have:
class Entry
{
public $id;
public $name;
public $seoName;
public $timeCreated;
public function someFunction()
{
}
}
class EntryMapper
{
protected $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
public function saveEntry(Entry &$entry)
{
if($entry->id){
$sql = "";
}
else {
$sql = "INSERT INTO tbl_entry (name, seo_name, time_created) VALUES (:name, :seo_name, :time_created)";
$stmt = $this->db->prepare($sql);
$stmt->bindParam("name", $entry->name);
$stmt->bindParam("seo_name", $entry->seoName);
$stmt->bindParam("time_created", $entry->timeCreated);
$stmt->execute();
$entry->id = $this->db->lastInsertId();
}
}
}
Now, here's how I use it in my view file (currently just testing insert command):
$entry = new Entry();
$entry->name = "Some Company LLC";
$entry->seoName = "some-company-llc";
$entry->timeCreated = date("Y-m-d H:i:s");
$entryMapper = new EntryMapper(new PDO("mysql:host=....."));
$entryMapper->saveEntry($entry);
I want to have the $entryMapper line like this:
$entryMapper = new EntryMapper(new Database());
meaning I should have a separate class Database.php where I would establish the connection.
I tried that, but since my class EntryMapper.php needs an instance of PDO directly, i'm getting an error. I have tried Database extend from PDO but that also raises error saying that PDO constructor was not called in EntryMapper
Any thoughts?
EDIT: if you see any signs of code coupling or similar, let me know because I want to learn to code properly. Thank you very much!
You can use Factory pattern and create the PDO object within a function in the Database class.
class Database {
private const connStr = 'mysql:host=.....';
public static function createPDODatabase() {
return new PDO(connStr);
}
}
So you may call your EntryMapper constructor as:
$entryMapper = new EntryMapper(Database::createPDODatabase());
EDIT: If you want to do it by instantiating the Database object, you should call the PDO constructor in the constructor of the Database class.
class Database extends PDO {
public function __construct($dbname='db_name', $server='localhost', $username='db_user', $password='db_password') {
parent::__construct("mysql:host=$server;dbname=$dbname", $username, $password);
parent::setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
}
}
Then you may just instantiate the Database object.
$entryMapper = new EntryMapper(new Database());
This is how I finally solved it (if a better implementation arises, I will for sure recode). It is an implementation of solution under the accepted answer here: Global or Singleton for database connection?
My ConnFactory.php
include('config/config.php');
class ConnFactory
{
private static $factory;
public static function getFactory()
{
if(!self::$factory){
self::$factory = new ConnFactory();
return self::$factory;
}
}
private $db;
public function pdo()
{
if(!$this->db){
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
);
$this->db = new PDO("mysql:host=".DB_HOST.";port=".DB_PORT.";dbname=".DB_SCHEMA."", DB_USER, DB_PASS, $options);
}
return $this->db;
}
}
Usage in my view/html file (just a test of insert functionalty):
$entry = new Entry();
$entry->name = "Kartonaža ad Gradačac";
$entry->seoName = "kartonaza-ad-gradacac";
$entry->timeCreated = date("Y-m-d H:i:s");
$entryMapper = new EntryMapper(ConnFactory::getFactory()->pdo());
$entryMapper->saveEntry($entry);

PDO Wrapper Returns NULL

I have the following PDO Initialization set in my constructor for a PDO wrapper:
public function __construct($engine, $host, $username, $password, $dbName)
{
$this->host = $host;
$this->dsn = $engine.':dbname='.$dbName.';host='.$host;
$this->dbh = parent::__construct($this->dsn, $username, $password);
$this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
}
My main problem is that when I set dbh to initialize as a parent in a constructor, it returns NULL.
and that creates a chain reaction.
Is there anything specific that I'm doing wrong?
You are mixing up wrapping a class and inheriting a class.
Either do this (wrapping):
class YourDB
{
public function __construct($engine, $host, $username, $password, $dbName)
{
$this->host = $host;
$this->dsn = $engine.':dbname='.$dbName.';host='.$host;
// here we are wrapping a PDO instance;
$this->dbh = new PDO($this->dsn, $username, $password);
$this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
}
// possibly create proxy methods to the wrapped PDO object methods
}
Or (inheriting):
class YourDB
extends PDO // extending PDO, and thus inheriting from it
{
public function __construct($engine, $host, $username, $password, $dbName)
{
$this->host = $host;
$this->dsn = $engine.':dbname='.$dbName.';host='.$host;
// here we are calling the constructor of our inherited class
parent::_construct($this->dsn, $username, $password);
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
}
// possibly override inherited PDO methods
}
You don't understand the parent::__construct() call.
Calling parent::__construct() doesn't return anything:
<?php
class Obj {
public $deja;
public function __construct() {
$this->deja = "Constructed";
}
}
$obj = new Obj();
class eObj extends Obj {
public $parent;
public function __construct() {
$this->parent = parent::__construct();
}
}
$eObj = new eObj();
if($eObj->parent==null) {
echo "I'm null";
echo $eObj->deja; // outputs Constructed
}
?>
Calling parent::__construct() simply calls the parent constructor on your object. Any variables defined in the parent will be set, etc. It doesn't return anything.

Extending the MySQLi class

I want to be able to make classes which extend the MySQLi class to perform all its SQL queries.
$mysql = new mysqli('localhost', 'root', 'password', 'database') or die('error connecting to the database');
I dont know how to do this without globalising the $mysql object to use in my other methods or classes.
class Blog {
public function comment() {
global $mysql;
//rest here
}
}
Any help would be appreciated.
Thanks.
I was working on something similar. I'm happy about this singleton class that encapsulates the database login.
<?php
class db extends mysqli
{
protected static $instance;
protected static $options = array();
private function __construct() {
$o = self::$options;
// turn of error reporting
mysqli_report(MYSQLI_REPORT_OFF);
// connect to database
#parent::__construct(isset($o['host']) ? $o['host'] : 'localhost',
isset($o['user']) ? $o['user'] : 'root',
isset($o['pass']) ? $o['pass'] : '',
isset($o['dbname']) ? $o['dbname'] : 'world',
isset($o['port']) ? $o['port'] : 3306,
isset($o['sock']) ? $o['sock'] : false );
// check if a connection established
if( mysqli_connect_errno() ) {
throw new exception(mysqli_connect_error(), mysqli_connect_errno());
}
}
public static function getInstance() {
if( !self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
public static function setOptions( array $opt ) {
self::$options = array_merge(self::$options, $opt);
}
public function query($query) {
if( !$this->real_query($query) ) {
throw new exception( $this->error, $this->errno );
}
$result = new mysqli_result($this);
return $result;
}
public function prepare($query) {
$stmt = new mysqli_stmt($this, $query);
return $stmt;
}
}
To use you can have something like this:
<?php
require "db.class.php";
$sql = db::getInstance();
$result = $sql->query("select * from city");
/* Fetch the results of the query */
while( $row = $result->fetch_assoc() ){
printf("%s (%s)\n", $row['Name'], $row['Population']);
}
?>
My suggestion is to create a Singleton DataAccess class, instantiate that class in a global config file and call it in your Blog class like $query = DataAccess::query("SELECT * FROM blog WHERE id = ".$id).
Look into the Singleton pattern, it's a pretty easy to understand designpattern. Perfect for this situation.
Your DataAccess class can have several methods like query, fetchAssoc, numRows, checkUniqueValue, transactionStart, transactionCommit, transactionRollback etc etc. Those function could also be setup as an Interface which gets implemented by the DataAccess class. That way you can easily extend your DataAccess class for multiple database management systems.
The above pretty much describes my DataAccess model.
You can use PHP's extends keyword just for any other class:
class MyCustomSql extends mysqli {
public function __construct($host, $user, $password, $database) {
parent::__construct($host, $user, $password, $database);
}
public function someOtherMethod() {
}
}
$sql = new MyCustomSql('localhost', 'root', 'password', 'database') or die('Cannot connect!');
or better use object aggregation instead of inheritance:
class MySqlManipulator {
private $db;
public function __construct($host, $user, $password, $database) {
$this->db = new mysqli($host, $user, $password, $database);
}
public function someOtherMethod() {
return $this->db->query("SELECT * FROM blah_blah");
}
}
$mysqlmanipulator = new MySqlManipulator('localhost', 'root', 'password', 'database') or die('Cannot connect!');
My standard method is to make a singleton class that acts as the database accessor, and a base class that everything requiring such access inherits from.
So:
class Base {
protected $db;
function __construct(){
$this->db= MyDBSingleton::get_link();
//any other "global" vars you might want
}
}
class myClass extends Base {
function __construct($var) {
parent::__construct();// runs Base constructor
$this->init($var);
}
function init($id) {
$id=(int) $id;
$this->db->query("SELECT * FROM mytable WHERE id=$id");
//etc.
}
}
Have a look at PDO, which throw exceptions for you to catch if a query fails. It's widely used and tested so you shouldn't have a problem finding existing solutions whilst using it.
To inject it into your blog class:
class Blog {
private $_db;
public function __construct(PDO $db) {
$this->_db = $db
}
public function comment() {
return $this->_db->query(/*something*/);
}
}

Categories