Here is my db class for connecting with database using pdo ext.
class db
{
private $host;
private $dbname;
private $username;
private $password;
public function __construct($host,$db,$name,$pass)
{
$this->host=$host;
$this->dbname=$db;
$this->username=$name;
$this->password=$pass;
$dsn = 'mysql:'.$this->dbname.';'.$this->host;
try
{
$conn = new PDO($dsn, $this->username, $this->password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
echo 'ERROR: ' . $e->getMessage();
}
}
}
I call db class in login class like this...
$host='localhost';
$db='techy_issue_tracker';
$name='root';
$pass='';
$base= new db($host,$db,$name,$pass);
Here is the problem, to make a pdo query inside login class (extending db class) if I use a line this...
$stmnt = $conn->prepare('SELECT id FROM users WHERE name :name OR email = :email');
It generates two errors, saying..
Notice: Undefined variable: conn... and
Fatal error: Call to a member function prepare() on a non-object...
I can fix this by simply putting all pdo stuff inside login class but still I am just curious...how do you guys call an object (which is an instance of pdo class?) from another class.
PHP Class Based User System With PDO - Call to a member function prepare() on a non-object This question is interesting but didn't understand much :/
Didn't practice OOP much so some good explanation would be appreciated! Thanks.
You need to intialize $conn as a public property of your db class.
After the private properties in your db class, add this:
public $conn;
Then, inside your try catch, change $conn to $this->conn:
$this->conn = new PDO($dsn, $this->username, $this->password);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Then, assuming you've correctly extended the db class, you can access conn in this manner:
$stmt = $db->conn->prepare('SELECT id FROM users WHERE name :name OR email = :email');
Why don't you just add a method to the db class that takes in a sql string and returns an array of object. Just do all the statement thingy in that method. Then the login class can just call this method to do any query. It does not need to know how the database connection is implemented. This way, if you want to change the underlying implementation in future. It only affects the db class. Not much changes is to be done to the login class as all database accesses goes through the db class.
There's an error in your WHERE clause:
WHERE name :name
You're missing an equal sign:
WHERE name = :name
Related
I work now on a project for learning i'm a beginner, i use singelton pattern to make sure, that there is just one PDO connection to the Data Base.
I made var_dump() for all possible connections to the Mysql and I founded that there were 10 objects of pdo.
how can i found, from where come all this objects?
Are 10 objects of PDO normal?
I wanted to use just one for all the project.
my singelton
<?php
namespace App\Database;
use PDO;
use PDOException;
class DataBase
{
private static $instance;
private PDO $pdo;
private function __construct()
{
try {
$db= parse_ini_file("..//..//..//config.ini");
#$db= parse_ini_file("..//..//config.ini");
$type = $db['type'];
$host = $db['host'];
$name = $db['name'];
$user = $db['user'];
$password = $db['password'];
$this->pdo = new PDO($type . ':host=' . $host . ';dbname=' . $name, $user, $password);
}
catch (PDOException $e) {
echo "there is an error";
die();
}
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
return $this->pdo;
}
private function __clone() {}
public static function getInstance()
{
if (!static::$instance) {
static::$instance = new DataBase();
}
return static::$instance;
}
public function getPdo(): PDO
{
return $this->pdo;
}
}
?>
then I made a connection of the pdo like this in another place, where I need to use pdo init.
function makePdo(){
$db= DataBase::getInstance();
$pdo= $db->getPdo();
var_dump($pdo);
return $pdo;
}
Thank you
10 connections are not normal, you should have only one to one DB. Use singleton pattern for database connection:
https://gist.github.com/jonashansen229/4534794
https://phpenthusiast.com/blog/the-singleton-design-pattern-in-php
In this case, you will have one connection and always will get it instead of creating duplicates.
No it should be only one object, the best way to implment it, is to create a separate config file to store your DBMS credentials as associative array, like mysql db_name, host .. etc.
Then create a config class to get those values by creating a static get method.
and finally use this get method inside your db constructor to get the credentials.
I'm pretty sure If you do that you'll get only one instance of PDO object.
I wanted to insert a row and at the same time get the id generated by that query.
I'm just testing it first and so far here is what I got.
I created a class which has a database configuration like this
class Database{
private $host;
...
...
protected function connect(){
$this->host = 'localhost';
...
...
$conn = new mysqli($this->host, ...,);
if ($conn->connect_errno){
echo 'error';
};
return $conn;
}
}
A User class for the actual query and where I extend my Database class.
class User extends Database{
function createUser(...,){
$sql = "INSERT INTO t (field) VALUES ('value');";
$this->connect()->query($sql);
return $this->connect()->insert_id;
}
}
I already have data inserted in my table but the function returns 0.
Note:
I have my column ID on user table of AUTO_INCREMENT attribute.
The query is successful but not returning the correct ID.
I tried doing this without the Database class and it works fine, returning the ID generated by the query.
$mysqli = new mysqli('host', ...,);
if(result = $mysqli->query("INSERT INTO t (field) VALUES ('value');")){
return $mysqli->insert_id;
}
Problem is inside your Database class.
you need a $conn field.
calling connect() causes to create new connection every time.
class Database{
private $host;
private $conn;
...
...
protected function connect(){
$this->host = 'localhost';
...
...
$this->conn = new mysqli($this->host, ...,);
if ($this->conn->connect_errno){
echo 'error';
};
}
}
class User extends Database{
function createUser(...,){
$sql = "INSERT INTO t (field) VALUES ('value');";
$this->conn->query($sql);
return $this->conn->insert_id;
}
}
Don't do it this way! Your Datebase class is useless and it doesn't follow the best practices of OOP.
Your problem is that each time you call $this->connect() you create a new connection. The connections do not share state between themselves so you can't access the last inserted ID using a different connection. It has to be the same connection!
Don't write such classes like Database class unless you are actually going to write abstraction library, and never inherit from a Database class.
mysqli is a class in itself. To open a database connection using mysqli you only need 3 lines of code.
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli('localhost', 'user', 'pass', 'db');
$mysqli->set_charset('utf8mb4'); // always set the charset
You can then pass the instance to any class that needs it.
class User {
private mysqli $db;
public function __construct(mysqli $mysqli) {
$this->db = $mysqli;
}
function createUser(/* ... */) {
$stmt = $this->db->prepare("INSERT INTO t (field) VALUES (?)");
$stmt->bind_param('s', $value);
$stmt->execute();
return $this->db->insert_id;
}
}
$user = new User($mysqli);
You must always use prepared statements because it allows you to parameterize your queries. Never concatenate data directly into SQL.
Lastly, do not use mysqli on its own. It is not suitable for beginners nor to be used on its own. If you are only starting to learn PHP then you should learn PDO instead of mysqli. PDO is much easier and more suitable for beginners. Start here https://phpdelusions.net/pdo
I am using below code for database connection
class Database extends PDO{
function __construct(){
try {
parent::__construct(DB_TYPE.':host='.DB_HOST.';dbname='.DB_NAME,DB_USER,DB_PASS);
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
} catch(PDOException $e){
Logger::newMessage($e);
logger::customErrorMsg();
}
}
}
every thing like login , fetching data was working fine . Now suddenly I am having a exception error message
Message: SQLSTATE[08004] [1040] Too many connections
Code: 1040
How to fix this error ?
I have a model class there I am creating new database.
class Model {
protected $_db;
public function __construct(){
//connect to PDO here.
$this->_db = new Database();
}
}
and every model I make , I am extending from model class.
Because your Model class instantiates a new Database object in its constructor, each time you instantiate a Model (or any class extending it), you are in effect opening a new database connection. If you create several Model objects, each then has its own independent database connection, which is uncommon, usually unnecessary, not a good use of resources, but also actively harmful as it has used up all the server's available connections.
For example, looping to create an array of Model objects:
// If a loop creates an array of Model objects
while ($row = $something->fetch()) {
$models[] = new Model();
}
// each object in $models has an independent database connection
// the number of connections now in use by MySQL is now == count($models)
Use dependency injection:
The solution is to use dependency injection and pass the Database object into the Model::__construct() rather than allow it to instantiate its own.
class Model {
protected $_db;
// Accept Database as a parameter
public function __construct(Database $db) {
// Assign the property, do not instantiate a new Database object
$this->_db = $db;
}
}
To use it then, the controlling code (the code which will instantiate your models) should itself call new Database() only once. That object created by the controlling code must then be passed to the constructors of all models.
// Instantiate one Database
$db = new Database();
// Pass it to models
$model = new Model($db);
For the use case where you actually need a different independent database connection for a model, you can hand it a different one. In particular, this is useful for testing. You can substitute a test database object, or a mock object.
// Instantiate one Database
$db = new Database();
$another_db = new Database();
// Pass it to models
$model = new Model($db);
$another_model = new Model($another_db);
Persistent connections:
As mentioned in the comments, using a persistent connection is possibly a solution, but not the solution I would recommend. PDO will attempt to reuse an existing connection with the same credentials (as all yours will have), but you don't necessarily want the connection to be cached across script execution. If you did decide to do it this way, you need to pass the attribute to the Database constructor.
try {
// Set ATTR_PERSISTENT in the constructor:
parent::__construct(DB_TYPE.':host='.DB_HOST.';dbname='.DB_NAME,DB_USER,DB_PASS, array(PDO::ATTR_PERSISTENT => true));
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
}
The relevant documentation is here: http://php.net/manual/en/pdo.connections.php#example-950
Singleton solution:
Using a singleton pattern (also not recommended), you could at least reduce this to a search/replace in the model code. The Database class needs a static property to keep a connection for itself. Models then call Database::getInstance() instead of new Database() to retrieve the connection. You would need to do a search and replace in the Model code to substitute Database::getInstance().
Although it works well and isn't difficult to implement, in your case it would make testing a little more difficult since you would have to replace the entire Database class with a testing class of the same name. You can't easily substitute a test class on an instance by instance basis.
Apply singleton pattern to Database:
class Database extends PDO{
// Private $connection property, static
private static $connection;
// Normally a singleton would necessitate a private constructor
// but you can't make this private while the PDO
// base class exposes it as public
public function __construct(){
try {
parent::__construct(DB_TYPE.':host='.DB_HOST.';dbname='.DB_NAME,DB_USER,DB_PASS);
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
} catch(PDOException $e){
Logger::newMessage($e);
logger::customErrorMsg();
}
}
// public getInstance() returns existing or creates new connection
public static function getInstance() {
// Create the connection if not already created
if (self::$connection == null) {
self::$connection = new self();
}
// And return a reference to that connection
return self::$connection;
}
}
Now you would need to change only the Model code to use Database::getInstance():
class Model {
protected $_db;
public function __construct(){
// Retrieve the database singleton
$this->_db = Database::getInstance();
}
}
Sorry for the stupid question am a newbie to oop in php...i have a proble wiriting a mysqli class thet returns a connection handler that enable me the connection handler in other classes/methods. i run a small procedural blog n wanted to convert to oop.
heres a my working code example. please refactor for me where necessary. Thank in advance.
$db_link = mysqli_connect(DB,HOST,DB_USER,DB,PASS,DB_NAME) or die(mysql_error());
class myDB extends mysqli
{
public function getConfig($id, $db_link) // thanks cbuckley
{
$db_link = $this->db_link;
$res = mysqli_query($this->db_link,"SELECT * from config where id='1'");
$row = mysqli_fetch_array($res);
return $row['option'];
}
}
i have defined the constants already and the connection is successful but the select is not working. on the index page this goes
include('inc/dbc.php');
$db = new MyDB();
echo $db->getConfig(1, $db_link);
any help is appreciated
Your question is not very specific, and I have the feeling you're not really aware what your concrete problems are. You've already accept an answer that gives you some code but puts you in the wrong direction as it does not solve your underlying problem and wastes code.
The first thing you should know if you want to make use and benefit of the object oriented interface of PHP's mysqli extension is, that the Mysqli class represents the database connection (the "link" as it was named for a procedural approach) already:
require('inc/dbc.php');
$dbConnection = new Mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
That's already it. Sure you might want to use some error handling here:
if ($dbConnection->connect_error)
{
throw new Exception(
sprintf('(#%d) %s', $dbConnection->connect_errorno,
$dbConnection->connect_error)
);
}
Throwing exception instead of let's say die('message') is done, because you can easily create an exception handler so that you can display a more useful response to the user from within a central place instead of handling each exceptional error-case where it appears (actually at the place where die would be).
You can also log and mail the backtrace so you can fix things more easily. Naturally you do not need to use exceptions, however the rule of thumb is to use die only in scripts you throw away in let's say a week and it does not work well with object oriented design.
As you will need this code anyway in all places where you will need your database connection, you can create your own connection object to bring those parts together as they belong together:
class DatabaseException extends Exception
{
}
class DatabaseConnection extends Mysqli
{
public function __construct($host, $user, $password, $database = "", $port = NULL, $socket = NULL) {
parent::__construct($host, $user, $password, $database, $port, $socket);
$this->throwConnectionExceptionOnConnectionError();
}
private function throwConnectionExceptionOnConnectionError() {
if (!$this->connect_error) return;
$message = sprintf('(%s) %s', $this->connect_errno, $this->connect_error);
throw new DatabaseException($message);
}
}
The usage is actually pretty straight forward and very much the same, only the name of the class varies and it's definition needs to be loaded:
require('inc/dbc.php');
require('inc/database.php');
$dbConnection = new DatabaseConnection(DB_HOST, DB_USER, DB_PASS, DB_NAME);
As written, the connection object already represents your database connection. So every part in your application that needs it, has to ask for it. Let's review your example function:
function getOption($id, $db_link)
{
// $db_link = $this->db_link;
$res = mysqli_query($this->db_link,"SELECT * from config where id='1'");
$row = mysqli_fetch_array($res);
return $row['option'];
}
I renamed the function and I commented the first line, even this might be what you want. Actually, if that function would be part of the DatabaseConnection object, it could work like this:
class DatabaseConnection extends Mysqli
{
...
public function getOption($id) {
$statement = $this->prepare('SELECT `option` FROM config WHERE id=?');
$statement->bind_param('i', $id);
$statement->execute();
$statement->bind_result($option);
$statement->fetch();
$statement->close();
return $option;
}
As this example demonstrates, the database connection is already there. However, this is not advisable. Imagine you not only have options but this and that and such and what not more. You would create one function after the other all in one class. Well for a little application that might even so work right, but imagine more and more. You would get one very large class that is responsible for many things. So it would be bad to do this, even if you can use $this already to prepare the statement.
Also take note that you should prepare statements. This has been answered here numerous times, if you're not used to it, read about it, it's worth the lines. There are better ways to not repeat code (DRY: Don't repeat yourself) while stepping into object oriented (you should even already do this with procedural).
So as to have this all in one class would be a problem, you instead put it in a class of it's own:
class DatabaseModelBase
{
protected $connection;
public function __construct(Connection $connection) {
$this->connection = $connection;
}
protected function prepare($query) {
$connection = $this->connection;
$statement = $connection->prepare($query);
if (!$statement) {
throw new DatabaseException(
sprintf('(%s) %s', $connection->error, $connection->errno)
);
}
return $statement;
}
}
class Option extends DatabaseModelBase
{
public function find($id) {
$statement = $this->prepare('SELECT `option` FROM config WHERE id=?');
$statement->bind_param('i', $id);
$statement->execute();
$statement->bind_result($option);
$statement->fetch();
$statement->close();
return $option;
}
}
This has some extended error handling again, because most often mistakes are made in the SQL query. And as you can see the individual function to fetch some specific data is placed in it's own class. You can use such classes to group the fetching and updating for specific datatypes.
Usage in full:
$dbConnection = new Connection(DB_HOST, DB_USER, DB_PASS, DB_NAME);
$option = new Option($dbConnection);
$optionValue = $option->find(1);
echo $optionValue; # value for option with ID1
The names of the Option object probably is not well, I tried to keep the example lightweight but also offer some separation. For other scenarious you might want to prefer some different kind how to access the db connection, because it is injected into Option via it's constructor, Option does not deal any longer with the details how the database connection is being created.
For example you can make the database connection object more smart only to connect to the database the first time prepare or query is actually used with it. So that requests to your website that do not need a database connection would not needlessly connect to the database.
You find more examples in other questions and you might want to learn about dependency injection. Also you always want to keep things apart from each other, so you have some objects that are only lightly connected to each other.
How to successfully rewrite old mysql-php code with deprecated mysql_* functions?
Dependency Injection simple implementation
I would suggest moving the connect to the database also inside the class:
class myDB extends mysqli
{
private $link;
public function Connect()
{
$this->link = mysqli_connect(DB,HOST,DB_USER,DB,PASS,DB_NAME) or die(mysql_error());
}
public function getConfig($id) // thanks cbuckley
{
$res = mysqli_query($this->link,"SELECT * from config where id='$id'");
$row = mysqli_fetch_array($res);
return $row['option'];
}
}
And use it like this:
include('inc/dbc.php');
$db = new MyDB();
$db->Connect();
echo $db->getConfig(1);
I recommend you to Wrap the mysqli class in a new Class instead extending it:
For those Classes, where you need the Configuration, just include the Configuration with Dependency Injection. This brings you the Benefit, that the Code, where you need the Configuration, don't need to know about the Way, Configuration get its values.
If you later decide to parse Configuration from a iniFile, you only need to change the Configuration-Class or even better, write a new Class which implements ConfigurationInterface and inject the new ConfigurationClass.
class Configuration implements ConfigurationInterface{
private $mysqli;
public function construct(mysqli $mysqli) {
$this->mysqli = $mysqli;
}
public function get($key)
{
// You should escape this query to prevent SQL-injection
$res = $this->mysqli->query("SELECT * from config where id='$key'");
$row = $res>fetch_array();
return $row['option'];
}
}
interface ConfigurationInterface {
public get($key);
}
Use it like this:
$mysqli = new mysqli('localhost', 'root', 'pw', 'my_db');
$config = new Configuration($mysqli);
var_dump($config->get(0));
This question already has answers here:
Executing mysqli_query inside a function
(2 answers)
Closed 7 months ago.
I am trying to write a PHP function. It is very simple. It is just a prepared statement that queries the database, but I can not get this to work. I keep recieving the error Call to a member function prepare() on a non-object. here is the code:
$DBH = new mysqli("host", "test", "123456", "dbname");
function selectInfo($limit, $offset){
$stmt = $DBH->prepare("SELECT * FROM information LIMIT ?,?");
$stmt->bind_param("ii", $limit, $offset);
$stmt->execute();
}
selectInfo();
Any time I call the function i get that error. Can someone please help?
It's a scoping error. You're making $DBH a global variable. So when you enter the function, the global variable is not available. You have 5 real options.
1. Use the global keyword
function doSomething() {
global $DBH;
//...
This is not a good idea, since it makes maintenance and testing a PITA. Imagine trying to debug that function call. You now need to go find out where $DBH is defined to try to figure out what's going on...
2. Make $DBH a parameter to the function
function doSomething(MySQLi $DBH) {
It has the advantage of being explicit. But it's still not great since the calling code then needs to keep track of the global variable.
3. Create a function to "get" the $DBH object
function getDBH() {
static $DBH = null;
if (is_null($DBH)) {
$DBH = new mysqli(...);
}
return $DBH;
}
function doSomething() {
$DBH = getDBH();
}
This has the advantage of getting around the global variable problem completely. But it's also hard to have multiple connections or re-use any of the code for other connections.
4. Create a class to wrap database access
class Database {
public function __construct($host, $user, $pass) {
$this->DBH = new MySQli($host, $user, $pass);
}
public function doSOmething() {
$this->DBH->foo();
}
}
This encapsulates everything for you. All database access will go through a single class, so you don't need to worry about global variable access or anything else.
5. Use a pre-built class/framework
This is the best option, since you don't need to worry about doing it yourself.
Database Access Classes:
A quick google search to get you started
Doctrine ORM - A complete database access library with full ORM (Object Mapping)
ADODB - A database agnostic database access library
Pear MDB2 - Another database access library
Full Frameworks:
Zend Framework
Lithium Framework
Code Igniter
(really there are a lot more, I'm not going to bother listing any more since that's another question all together...)
Really, the choices are endless. Find something you like, and stick with it. It really will make your life easier...
$DBH is not in scope. You either want to define $DBH as global in the function:
$DBH = new mysqli("host", "test", "123456", "dbname");
function selectInfo($limit, $offset){
global $DBH;
$stmt = $DBH->prepare("SELECT * FROM information LIMIT ?,?");
$stmt->bind_param("ii", $limit, $offset);
$stmt->execute();
}
or as ircmaxell pointed out in his excellent answer have a function which returns a static instance of $DBH.
Try to add global $DBH; in the function, or add it to the function's parameters.
selectInfo($DBH);
function selectInfo($DBH,$limit, $offset){
$stmt = $DBH->prepare("SELECT * FROM information LIMIT ?,?");
$stmt->bind_param("ii", $limit, $offset);
$stmt->execute();
}
That's simply. $DBH doesn't exist within selectInfo() function. Variable defined in global scope won't be visible within function and vice-versa. Read more about variables scope on manual pages.
How to solve it? Pass that variable as a argument of the function:
$dbh = new MySQLi(...);
function selectInfo(MySQLi $dbh, $limit, $offset) {
$stmt = $dbh->prepare(...);
...
}
Make sure the connection is successful.
$DBH = #new mysqli("host", "test", "123456", "dbname");
if ($DBH->connect_errno) {
die('Connect Error: ' . $DBH->connect_errno);
}
or
$DBH = #mysqli_connect("host", "test", "123456", "dbname");
if (!$DBH ) {
die('Connect Error: ' . mysqli_connect_errno());
}
Making $DBH global is not healthy...
except that you can make your $DBH protected in class and set it to null..
and use it..
class PDOconnect extends PDO{
protected $con=null;
public function __construct(){
try {
$this->con= new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); //our new PDO Object
$this->con->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$this->con->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$this->con->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
echo "hi.. you are connected succcessfully...";
}