I am trying to create a really simple database connection class in PHP, but I ran into a bit of problem.
First off, here's the code I wrote:
class db {
protected $db;
public function __construct() {
$this->db = new PDO('mysql:host=127.0.0.1;dbname=main', 'root', '');
}
}
Whenever I try to call $db from another class that extends my database class, I get an error like this one:
Fatal error: Call to a member function prepare() on a non-object in class.php
I call the database link in the other class like the following example:
class example extends db {
public function challenge_exists($challenge) {
$query = $this->db->prepare('SELECT * FROM challenges WHERE challenge = :challenge');
$query->bindParam(':challenge', $challenge, PDO::PARAM_STR);
$query->execute();
if($query->rowCount() > 0) {
return true;
}
}
}
I have no idea what I have done wrong and would appreciate any suggestions on how to make this work.
To the question, if there is a __construct() method in the child class then you need to call the __construct() of the parent class explicitly, or else it is not executed and $db is never instantiated. Though you don't show the child constructor in your example code it is safe to assume that there is one based on the behavior, so:
class example extends db {
public function __construct() {
parent::__construct();
//more stuff
}
}
If you don't need a constructor in the child class then remove it and the parent constructor will be called when you instantiate an object of the child class.
As for the design, this is bad and you should consider one database object and inject that into objects that need it or similar approach:
class example {
public function __construct($db) {
$this->db = $db;
}
}
$db = new db(); // do this once
$example = new example($db); // inject when needed
You should never extend an applications class from a database class in the first place.
User is not a database. It's a user. They have nothing in common. Database have to be used as a service by a user, not being a parent.
Not to mention that creating a hundred connections from the same script is a very bad idea.
Regarding the "accepted" answer, it's just wrong. As long as there is no constructor in the child class defined (like in the OP), then parent counstructor is used. Welcome to Stack Overfraud.
Related
I have a database class that has a series of functions in it, and I have a Main class that has the dependencies injected into it with other classes such as Users, Posts, Pages etc extending off of it.
This is the main class that has the database dependency injected into it.
class Main {
protected $database;
public function __construct(Database $db)
{
$this->database = $db;
}
}
$database = new Database($database_host, $database_user, $database_password, $database_name);
$init = new Main($database);
And then, this is the Users class I'm extending off of it.
class Users extends Main {
public function login() {
System::redirect('login.php');
}
public function view($username) {
$user = $this->database->findFirst('Users', 'username', $username);
if($user) {
print_r($user);
} else {
echo "User not found!";
}
}
}
But, whenever trying to call the view function for the User class, I'm getting this error Catchable fatal error: Argument 1 passed to Main::__construct() must be an instance of Database, none given. And, if I remove the Database keyword from the _construct parameters, I get this error instead Warning: Missing argument 1 for Main::_construct().
If I pass a variable to the User class from main class it works, but not if I'm trying to pass the Database object, I just can't work out why.
The User class is instantiated via a router with no parameters passed to it.
You can make the $database variable in the Main class static. Then you need to initialize it only once:
class Main {
static $database = null;
public function __construct($db = null)
{
if (self::$database === null && $db instanceof Database) {
self::$database = $db;
}
}
}
The class User extends Main and therefore it inherits the __construct function of the Main class.
You can't instantiate the User class without passing its dependency, the Database instance.
$database = new Database($database_host, $database_user, $database_password, $database_name);
$user = new User($database);
However, you can do it like matewka hinted:
class Main {
static $database = null;
public function __construct($db = null)
{
if (self::$database === null && $db instanceof Database) {
self::$database = $db;
}
}
}
class User extends Main{
function test()
{
return parent::$database->test();
}
}
class Database{
function test()
{
return "DATABASE_TEST";
}
}
$db = new Database();
$main = new Main($db);
$user = new User();
var_dump($user->test());
As clearly noted in the other answers/comments you are not passing the expected dependency into your Users class to the __construct() method it inherits from Main.
In other words, Users expects to be instantiated in the same manner as Main; i.e:
$database = new Database();
$users = new Users($database);
However, I would like to add some detail because constructors in PHP are a little different to other methods when you are defining inheritance relationships between classes, and I think it's worth knowing.
Using inheritance, you can of course override a parent's method in an inheriting class, with a specific caveat in PHP: if you don't match the argument signature of the overridden method you trigger a E_STRICT warning. This is not a showstopper but it's best avoided when endeavouring to write stable robust code:
E_STRICT Enable to have PHP suggest changes to your code which will ensure the best interoperability and forward compatibility of your code. Source
An example:
class Dependency
{
// implement
}
class Foo
{
function doSomething(Dependency $dep)
{
// parent implementation
}
}
class Bar extends Foo
{
function doSomething()
{
// overridden implementation
}
}
$bar = new Bar();
$bar->doSomething();
If you run this code or lint it on the command line with php -l you'll get something like:
PHP Strict standards: Declaration of Bar::doSomething() should be compatible with Foo::doSomething(Dependency $dep) in cons.php on line 22
The reason why I am pointing this out is because this does not apply to __construct:
Unlike with other methods, PHP will not generate an E_STRICT level error message when __construct() is overridden with different parameters than the parent __construct() method has. Source (#Example 1)
So you can safely override __construct in an inheriting class and define a different argument signature.
This example is completely valid:
class Foo
{
function __construct(Dependency $dep)
{
// parent constructor
}
}
class Bar extends Foo
{
function __construct()
{
// overridden constructor
}
}
$bar = new Bar();
Of course, this is dangerous because in the highly likely event inheriting class Bar calls code that relies on Dependency you're going to get an error because you never instantiated or passed the dependency.
So you really should ensure the Dependency in the case above is passed to the parent class. So you are right back where you started, passing the dependency in the constructor, or doing the following, using the parent keyword to call the parent's __construct method:
class Bar extends Main
{
public function __construct()
{
// call the Main constructor and ensure
// its expected dependencies are satisfied
parent::__construct(new Dependency());
}
}
However, best practice dictates that you should pass dependencies into your object instead of instantiating them in your classes, a.k.a dependency injection. So we arrive back at the original solution provided by the other answers :)
$database = new Database();
$users = new Users($database);
Allowing overriding of __construct with different method signatures simply allows greater flexibility when defining classes - you might have am inheriting class that requires more information to be instantiated. For a (ridiculously contrived) example:
class Person
{
public function __construct($name)
{
// implement
}
}
class Prisoner extends Person
{
public function __construct($name, $number)
{
// implement
}
}
$p1 = new Person('Darragh');
$p2 = new Prisoner('Darragh', 'I AM NOT A NUMBER!');
I am pointing this out because __construct is actually quite flexible, if you find yourself in a situation where you want to instantiate an inheriting class with different arguments to the parent, you can, but with the big caveat that you must somehow ensure that you satisfy all expected dependencies of the parent class.
Hope this helps :)
I have a database class that has a series of functions in it, and I was advised the best thing to do to access those functions from within another class is dependency injection. What I want to do is have one main class that has the database dependency "injected" into it and then other classes extend off of this class, such as Users, Posts, Pages etc.
This is the main class that has the database dependency injected into it.
class Main {
protected $database;
public function __construct(Database $db)
{
$this->database = $db;
}
}
$database = new Database($database_host,$database_user,$database_password,$database_name);
$init = new Main($database);
And then this is the Users class I'm trying to extend off of it.
class Users extends Main {
public function login() {
System::redirect('login.php');
}
public function view($username) {
$user = $this->database->findFirst('Users', 'username', $username);
if($user) {
print_r($user);
} else {
echo "User not found!";
}
}
}
But whenever trying to call the view function for the User class, I'm getting this error Fatal error: Using $this when not in object context. This error is in regards to trying to call $this->database in the Users class. I've tried initializing a new user class and passing the database to it instead, but to no avail.
When you use call_user_func_array and you pass it a callable that is composed of a string name for the class and a string name for the method on the class it does a static call: Class::method(). You need to first define an instance and then pass the instance as the first part of the callable as demonstrated below:
class Test
{
function testMethod($param)
{
var_dump(get_class($this));
}
}
// This call fails as it will try and call the method statically
// Below is the akin of Test::testMethod()
// 'this' is not defined when calling a method statically
// call_user_func_array(array('Test', 'testMethod'), array(1));
// Calling with an instantiated instance is akin to $test->testMethod() thus 'this' will refer to your instnace
$test = new Test();
call_user_func_array(array($test, 'testMethod'), array(1));
I am switching my MVC to use PDO (I know, overdue). My application has, in the past, used the following class hierarchy:
Database.class>Main.class>User.class
(each one extending the other). But before any object is created, the mysql connection was made (mysql_connect). Once the connection was open I could use Database.class as a wrapper class through which all my queries were performed. Through extention, a query in the User.class could be made simply by calling the "query" function ($this->query).
Using PDO, I've tried to imitate the process but find errors. I created a singleton function in the Database.class:
function __construct()
{
$this->db = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8', DB_USER, DB_PASSWORD);
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}
public static function getInstance()
{
if (!isset(self::$instance)){
$object = __CLASS__;
self::$instance = new $object;
}
return self::$instance;
}
function query($qry,$params=NULL){
$qry = $this->db->prepare('SELECT * FROM users WHERE userID = :userID');
$qry->execute(array(':userID' => 1));
$results = $qry->fetchAll(PDO::FETCH_ASSOC);
return $results;
}
Then in Main.class I get the instance:
function __construct()
{
$this->db = parent::getInstance();
}
So in User.class I try to call
function __construct(){
parent::__construct();
}
function test(){
return $this->db->query("test");
}
So I can run any queries fine from the Main.class object. But if I try to run queries from User.class object I get the error: "Call to a member function query() on a non-object" In other words, if User.class extends main I should be able to access the variable $db in Main from User.class (I call the constructor for Main when the User object is created). Part of the issue is that Main.class is created earlier in the application as it's own object, I believe causing two instances of PDO to be created - which is why it doesn't work through extension (through a second object that also extends the database.class)
So my question is: is there a way to make this happen? Or is my best option to use injection for every object I create (because some scripts incorporate multiple objects that extend Main.class - which try to create an instance of PDO each time) and pass the pdo object to the constructor? I'd rather not have to do that (the less markup the better) So another option would be to use a STATIC variable that all classes use? What's the best method? (let me know if this is confusing)
I've seen people using injection for this, and I've seen examples of extending the pdo wrapper class (but only once).
Thanks! (I love stack overflow!)
You dont want any of these to extend the database class because that will essentially make them all singletons of which you can only have one instance... you Want to make them USE the database class instead. So you would put you most abstract db methods on the Database and then methods that create queries for specific things would be on the User or what have you. This means your Database actually wraps PDO and is what all other classes work with for db operations. The Main or Base class may not even be needed unless you are trying to implement active record or something.
class Database {
static protected $instance;
/**
* #var PDO
*/
protected $connection;
protected function __construct($dsn, $user, $pass, $attrs = array()) {
// create pdo instance and assign to $this->pdo
}
public static function getInstance() {
if(!self::$instance) {
// get the arguments to the constructor from configuration somewhere
self::$instance = new self($dsn, $user, $pass);
}
return self::$instance;
}
// proxy calls to non-existant methods on this class to PDO instance
public function __call($method, $args) {
$callable = array($this->pdo, $method);
if(is_callable($callable)) {
return call_user_func_array($callable, $args);
}
}
}
class Main {
protected $db;
public function __construct() {
$this->db = Database::getInstance();
}
}
class User extends Main{
public function __construct() {
parent::__construct();
}
public function findById($id) {
$qry = $this->db->prepare('SELECT * FROM users WHERE userID = :userID');
$qry->execute(array(':userID' => $id));
$results = $qry->fetchAll(PDO::FETCH_ASSOC);
return $results
}
}
Bear with me cos I'm new to oop!
Is it possible to assign the result of a function to an attribute?
This probably doesn't make sense so here's what I think the code might look like!
Class b extends a
{
public $conn= $this->connect();
public function operation() { ...}
}
connect() is a function from class a that connects to a database and returns $result if successful.
Yes that is possible but you need to do that in a method like the constructor:
Class b extends a {
public $conn;
public function __construct() {
$this->conn = $this->connect();
}
public function operation() { /* ... */ }
}
You need to put the call to connect within a method, and then call the method once you've instantiated the class to initialize the connection - or use the constructor method to do so automatically.
Generally, this is bad form, as you cannot be certain what implementation of the connect() method will be called. Is it the one from the current class or the one from the superclass? In fact, I would be surprised if PHP even allowed this.
Initialisation should be done in the constructor, but even then it depends on what the initialisation involves. If it requires calling another method, then the same issue as above exists: which version of the method to call?
However, for the scenario you describe, I would neither initialise the member variable at declaration, nor from within a constructor. Instead, I would pass $conn to the constructor. This is the basis of dependency-injection.
i usually do something like this
$link = mysql_connect(.....,......,......);
Class b extends a {
public $conn;
public function __construct($link) {
$this->conn = $link;
}
public function operation() { /* ... */ }
}
$a = new b($link);
then if your connection details change you change them in one place and if you need to connect to a different server you can and pass in the other link variable.
Another tip for oop database integration is always have a DBable base class to do your basic save() load() find() count() etc, much quicker to work with db savable objects
BaseDBClass
A
B
C
D
X
Y
Z
Hello i want to make something on classes
i want to do a super class which one is my all class is extended on it
____ database class
/
chesterx _/______ member class
\
\_____ another class
i want to call the method that is in the database class like this
$this->database->smtelse();
class Hello extends Chesterx{
public function ornekFunc(){
$this->database->getQuery('popularNews');
$this->member->lastRegistered();
}
}
and i want to call a method with its parent class name when i extend my super class to any class
I'm not quite sure what you mean by your last sentence but this is perfectly valid:
class Chesterx{
public $database, $member;
public function __construct(){
$this->database = new database; //Whatever you use to create a database
$this->member = new member;
}
}
Consider the Singleton pattern - it usually fits better for database interactions. http://en.wikipedia.org/wiki/Singleton_pattern.
you could also consider using methods to get the sub-Objects
The advantage would be that the objecs are not initialized until they are need it, and also provides a much more loosely coupled code that lets you change the way the database is initialized more easy.
class Chesterx{
public $database, $member;
public function getDatabase() {
if (!$this->database ) {
$this->database = new database; //Whatever you use to create a database
}
return $this->database;
}
public function getMember() {
if (!$this->member) {
$this->member = new member;
}
return $this->member;
}
}