I'm using PHP 5.1.6 and MDB2 and trying to wrap my prepare/execute/fetchAll into a class so I can execute a select query with one line. The following code shows the Class that I've created and also doing the same query directly:
<?php
include_once "MDB2.php";
class db {
private static $dsn = array(
'phptype' => "mysqli",
'username' => "username",
'password' => "pass",
'hostspec' => "localhost",
'database' => "dbname"
);
private static $instance = NULL;
private static $statement = NULL;
private static $resultset = NULL;
private function __construct() {}
private function __clone() {}
public static function getInstance() {
if (!self::$instance) {
self::$instance =& MDB2::factory(self::$dsn);
}
return self::$instance;
}
public static function execQuery($sql, $types, $values) {
if (self::$instance === NULL) {
self::getInstance();
}
self::$statement = self::$instance->prepare(
$sql, $types, MDB2_PREPARE_RESULT);
self::$resultset = self::$statement->execute(array($values));
if (PEAR::isError(self::$resultset)) {
// (this is where it fails)
echo('Execute Failed: ' . self::$resultset->getMessage());
return false;
}
return self::$resultset->fetchAll(MDB2_FETCHMODE_ASSOC);
}
}
echo "<pre>";
$dsn = array(
'phptype' => "mysqli",
'username' => "username",
'password' => "pass",
'hostspec' => "localhost",
'database' => "dbname"
);
$sql = "select * from testtable where id = ? order by id LIMIT ?"
$t = array('text','integer');
$v = array('ABC',3);
// GOING DIRECT
$db =& MDB2::factory($dsn);
$stmt = $db->prepare($sql, $t, MDB2_PREPARE_RESULT);
$res = $stmt->execute($v);
$out = $res->fetchAll(MDB2_FETCHMODE_ASSOC);
print_r($out);
// GOING THROUGH CLASS
print_r( db::execQuery($sql, $t, $v) );
?>
The output of going direct works as expected, but the second attempt, going through the class fails with the PEAR error message "MDB2 Error: not found". I can't see what the difference is between these two approaches.
The Class works properly if I only pass a SQL statement with one ? replacement and don't use 'array()' to hold the types and values. If I change these, it works:
$sql = "select * from testtable where id = ? order by id"
$t = 'text';
$v = 'ABC';
I found the problem with my code. The line in the execQuery method that reads:
self::$resultset = self::$statement->execute(array($values));
should be:
self::$resultset = self::$statement->execute( $values );
OOP is new to me so I was convinced the problem was with the class and method.
(I'm not sure if I should be answering my own question, or just put it in the comments)
Related
I recently started to resurrect an old PHP4.# website. In doing so I've been trying to figure out the conversion to PDO. After so many different attempts at PDO structure I'm getting lost and my code keeps getting more and more wild. I believe the problem is that I'm not correctly instantiating the pdo class but have failed using several different methods.
Fatal error: Uncaught Error: Call to undefined method DBInsert::__construct() in /home/folder/database/insert.php:36
mysqlhelper.php
<?php
class MySQLHelper
{
private $link;
private $db;
private $user;
private $pass;
var $pdo;
public function __construct()
{
$host = 'localhost';
$db = 'dbname';
$user = 'username';
$pass = 'supersecret';
$port = '1111';
$options = [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$dsn = "mysql:host=$host;dbname=$db;port=$port";
$this->pdo = new PDO($dsn, $user, $pass, $options);
return $this->pdo;
} catch (\PDOException $e) {
throw new \PDOException($e->getMessage(), (int)$e->getCode());
echo "Connection failed";
}
}
function __destruct()
{
// Closing connection
$this->pdo = null;
}
public function query($query)
{
// Performing SQL query
$result = $this->pdo->query($query) or die('Query failed: ' . mysql_error());
return $result;
}
insert.php
<?php
include "../helpers/mysqlhelper.php";
class DBInsert
{
private $helper;
private $nations;
private $nations_updated;
private $stagedslots;
private $stagednations_new; // nations that need to be inserted
private $stagednations_old; // nations that need to be updated
var $pdo;
//this function was previously called __construct() prior to my 11/22/20 updates
function somethingelse()
{
$this->helper = new MySQLHelper();
$this->stagedslots = array();
$this->nations_updated = array();
$this->nations = array();
$q = "SELECT * FROM `nations`";
$result = $this->helper->query($q);
$result = $this->helper->getResultArray($result);
foreach ($result as $val)
{
$this->nations[$val["id"]] = $val;
}
}
public function clearSlots()
{
$sql = "DELETE FROM `slots`;";
$object = new MySQLHelper;
$object->__construct();
line 36 $stmt = $this->__construct()->prepare($sql);
$stmt->execute();
}
For line 36 I've also tried $stmt = $this->pdo->prepare($sql); which yields errors also.
Appreciate any suggestions.
$object = new MySQLHelper;
$object->__construct();
$stmt = $this->__construct()->prepare($sql);
You are writing the same thing three times here:
You make a new object $object and then you do not need to call __construct because by making the object it automatically calls the constructor already.
$sql = "DELETE FROM `slots`;";
$object = new MySQLHelper; //automaticcally calls the constructor
$stmt = $object->prepare($sql);
$stmt->execute();
$stmt->close(); // close connection.
READ THIS
I have inherited some PHP code that is not ideal and I'm not sure what the most efficient way of rectifying it is.
Basically all DB calls are made through a custom function like this:
function dbcommand($req)
{
global $mysqli;
$backtrace = debug_backtrace();
$ret = array();
$res = mysqli_query($mysqli, $req) or die('SQL Query Error: '. $mysqli->error .', query ['. $req .'] at '. $backtrace[0]['file'] .':'. $backtrace[0]['line']);
if (strpos(strtoupper($req), 'SELECT') === 0)
{
if (mysqli_num_rows($res))
{
while ($row = mysqli_fetch_assoc($res))
$ret[] = $row;
}
else $ret = array();
mysqli_free_result($res);
return $ret;
}
if (strpos($req, 'INSERT INTO') === 0)
return $mysqli->insert_id;
return $res;
}
Now I don't think I can use mysqli_real_escape_string because of the db-connector issue. Everything goes through that function. This means that avoiding sql injection is left in the hands of filter_vars before variables are mulched into SQL statements. I would like to parameterise my SQL statements and do it properly. But I'm just not sure what the most efficient way of doing it is in this case. Removing this function and converting everything to PDO would be very time consuming. There's a lot of code.
Here is a PDO-converted version of your function. It requires some helper classes:
// This class is your connection class using a singleton (like a global)
// You would need to populate your credentials in the connect method
class DatabaseConfig
{
private static $singleton;
public function __construct()
{
if(empty(self::$singleton))
self::$singleton = $this->connect();
return self::$singleton;
}
public function connect($host = "localhost", $username = "username", $password = "password", $database = "database")
{
// Create connection
$opts = array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);
$conn = new PDO('mysql:host='.$host.';dbname='.$database, $username, $password,$opts);
return $conn;
}
}
// This is a query class that will run your sqls
class QueryEngine
{
private $results;
private static $singleton;
public function __construct()
{
if(empty(self::$singleton))
self::$singleton = $this;
return self::$singleton;
}
public function query($sql = false,$bind = false)
{
$this->results = 0;
$db = new DatabaseConfig();
try {
if(!empty($bind)) {
$query = $db ->connect()
->prepare($sql);
$query->execute($bind);
}
else {
$query = $db ->connect()
->query($sql);
}
$this->results = $query;
}
catch (PDOException $e)
{
die($e->getMessage());
}
return $this;
}
public function fetch()
{
while($row = $this->results->fetch())
$result[] = $row;
return (!empty($result))? $result : 0;
}
}
// This is your function down to the basics
// Error handling will be in the query class under try
function dbcommand($req,$bind = false)
{
// Create query instance
$qEngine = new QueryEngine();
// Run the query
$qEngine->query($req,$bind);
// If select, fetch array
if(strpos(strtoupper($req), 'SELECT') === 0)
return $qEngine->fetch();
// The query already ran, so you can return whatever you want
// For ease I am just returning true
elseif(strpos($req, 'INSERT INTO') === 0)
return true;
}
To use:
// Make sure it include the classes and function above
print_r(dbcommand("select * from `users` where `ID` = :0",array(":0"=>"1")));
This would give you something like (in my db obviously, the table and columns will be different for you):
Array
(
[0] => Array
(
[ID] => 1
[unique_id] => 20150203190700523616
[username] => tester
[password] => $2a$12$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
[first_name] => Ras
[last_name] => Clatt
[email] => ras#clatt.com
[usergroup] => 3
[user_status] => on
[reset_password] => $2y$10$xxxxxxxxxxxxxxxxxxx
[timestamp] => 2015-09-25 08:35:09
)
)
So I have a list of functions that i'm using in a web app. Most of the functions however, make calls to a database class. Here are two functions for example:
function add_post($userid,$body,$cat_id,$user_link){ //This function inserts new posts into the db
$db = new MysqliDb('localhost', 'root', 'root', 'my_db');
$now = date("Y-m-d H:i:s");
$insertData = array(
'user_id' => $userid,
'body' => $body,
'stamp' => $now,
'cat_id' => $cat_id,
'link' => $user_link
);
$db->insert('posts', $insertData);
}
function grab_username($userid){ //This function takes a user id, and returns the associated user_name
$db = new MysqliDb('localhost', 'root', 'root', 'my_db');
$params = array($userid);
$results = $db->rawQuery("SELECT username FROM users WHERE id = ?", $params);
//print_r($results);
foreach($results as $arrays){
foreach($arrays as $name){
return $name;
}
}
}
the problem is that I am constantly writing the line:
$db = new MysqliDb('localhost', 'root', 'root', 'my_db');
Is there a way I can declare the variable globally, have something more like this:
$db = new MysqliDb('localhost', 'root', 'root', 'my_db');
function add_post($userid,$body,$cat_id,$user_link){ //This function inserts new posts into the db
$now = date("Y-m-d H:i:s");
$insertData = array(
'user_id' => $userid,
'body' => $body,
'stamp' => $now,
'cat_id' => $cat_id,
'link' => $user_link
);
global $db->insert('posts', $insertData);
}
There are multiple ways to handle this but the best implementation can vary depending on the structure and needs of your action application.
You mentioned a global variable which would work, but is typically considered poor structure:
someFunction(){
global $db;
$db->insert('posts', $insertData);
}
Another method, which is slightly cleaner but still not as well organized is to pass the $db connection to the functions:
function myFunction( $db, ... ){
$db->query( // etc );
}
myFunction( $db );
A better version of this would be to organize your related functions in a class, and then inject the database:
class Posts {
public function listPosts( $db, ... ){
$db->query( // etc );
}
public function getPost( $db, ... ){
$db->query( // etc );
}
}
$posts = new Posts();
$posts->listPosts();
However, you can also decide to make these static methods, you could pass $db to the class constructor and store it, there are many choices.
You could also make a central wrapper class of your own that returns the connection. There are a million implementations but it's hard to specifically recommend one without knowing more about your application structure, how your classes share resources, how the application bootstraps everything - if it does. Etc
Try This:
db.php
class DB
{
public $conn;
private static $instance;
private function __construct()
{
$server = 'localhost';
$user = 'root';
$pass = 'root';
$database = 'my_db';
$this->conn = mysqli_connect($server,$user,$pass,$database);
}
public function query($sql)
{
$result = mysqli_query($this->conn,$sql);
return $result;
}
public static function getInstance()
{
if (!isset(self::$instance))
{
$object = __CLASS__;
self::$instance = new $object;
}
return self::$instance;
}
}
Now in any page you can do:
require("db.php");
$dbh = DB::getInstance();
$sql = "your sql query ..";
$dbh->query($sql);
I'm trying to learn OOP, and some of its concept. I've following class for users:
class Users
{
private $host = DB_HOST;
private $user = DB_USERNAME;
private $pass = DB_PASSWORD;
private $dbname = DB_NAME;
private $conn;
private $stmt;
public $error;
function __construct()
{
$dsn = 'mysql:host='.$this->host.';dbname='.$this->dbname.';charset=utf8';
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
try {
$this->conn = new PDO($dsn,$this->user,$this->pass,$options);
} catch (PDOException $e) {
$this->error = $e->getMessage();
}
}
private function mysql_execute_query($sql,$params)
{
$this->stmt = $this->conn->prepare($sql);
$this->stmt->execute($params);
return $this->$stmt;
}
public function find_user_by_provider_uid($provider,$provider_uid)
{
$sql = 'SELECT * FROM users WHERE provider = :provider AND provider_uid = :provider_uid LIMIT 1';
$params = array(
':provider' => $provider,
':provider_uid' => $provider_uid
);
$result = $this->mysql_execute_query($sql,$params);
return $result->fetch();
}
}
First of all is there some tip that comes to mind for structuring this code better? or using more features of oop?
Second, it fails with following error:
PHP Notice: Undefined variable: stmt
PHP Fatal error: Cannot access empty property
Both of this lines refer to return $this->$stmt; inside mysql_execute_query
My hunch is that it has something to do with it being private function. But I cannot tell.
Any ideas?
Here the error:
return $this->$stmt;
But should be:
return $this->stmt;
I made a db connection class like this :
class db_connector{
const host = "localhost";
const user = "root";
const password = "";
const dbname = "test";
public $db;
public function __construct()
{
$database = $this::dbname;
$db = new mysqli($this::host, $this::user, $this::password, $this::dbname);
if($db->connect_errno)
{
die (mysql_connect_error());
}
return $db;
}
}
When i create an object for the class it works fine enough, though then i want to make a query within the class i get the following error:
Fatal error: Call to undefined method db_connector::query()
The object is as follows (outside the class):
$con = new db_connector;
$con->query("SELECT * FROM test");
The connection gets established, just the query gives the error. I thought the object would inherit the mysqli methods since I returned it. Can anybody help me fix this one? Im fairly new in OOP so maybe my logic is not the best.
How about a little magic:
class db_connector{
protected $connectionData = array(
'host' => 'localhost',
'user' => 'root',
'password' => '',
'dbname' => 'test',
);
protected $db;
public function __construct($connectionData=array())
{
$cd = array_merge($this->connectionData, $connectionData);
$this->db = new mysqli($cd['host'], $cd['user'], $cd['password'], $cd['dbname']);
if($this->db->connect_errno)
{
die (mysql_connect_error());
}
}
public function foo()
{
return 'I am foo, i exist so i will be called even if mysqli::foo would exist, because i am defined in this class!';
}
public function __get($property)
{
if(property_exists($this->db, $property)){
return $this->db->$property;
}
}
public function __call($name, $args)
{
if(method_exists($this->db, $name))
return call_user_func_array(array($this->db, $name), $args);
}
}
And call it like:
$db = new db_connector();
$result = $db->query('SELECT foo FROM bar LIMIT 1');
// this should also work:
echo $result->num_rows;
The class constructor won't return the mysqli object as you expect.
$con = new db_connector;
//$con is a db_connector object not a mysqli object
$con->query("SELECT * FROM test");
Try:
$con = new db_connector;
$con->db->query("SELECT * FROM test");
instead. $con->db is a public field which holds the mysqli object.
Edit
Also change:
$db = new mysqli($this::host, $this::user, $this::password, $this::dbname);
if($db->connect_errno)
{
die (mysql_connect_error());
}
return $db;
To:
$this->db = new mysqli($this::host, $this::user, $this::password, $this::dbname);
if($this->db->connect_errno)
{
die (mysql_connect_error());
}