PHP Object being passed to class, not being accepted - php

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 :)

Related

How to use an instance of a class within another class in PHP 7

I have what seems like a simple query but I have not found an answer elsewhere.
I have 2 classes, one called DB which essentially connects to the database and can then run queries. I instantiate it at the top of the document $db= new DB; and I can then run a series of queries on the database throughout the page.
The issue I am having is that I want to use this instance within another class I have called User.
I know I can either instantiate again but this does not make sense OR pass the instance of DB when instantiating User, for instance $user = new User($db); but considering the $db instance will be used by most classes I am going to create I am thinking there is a better solution to import it into other classes.
I have looked at the global route but I read it is bad practice + I am getting unexpected global error
class User{
global $db;
public function __construct()
{
var_dump($this-> db);
}//end constructor
}//end class
Since your DB client will be instantiated once and then used everywhere else my initial thought was to recommend passing it as a constructor parameter (dependency injection), but since you are not fan of this approach, I would recommend making your DB client a singleton class, which means it can only be instantiated once and any subsequent attempt would return the same instance everywhere.
You can see a detailed response about singleton classes in PHP at Creating the Singleton design pattern in PHP5.
As a quick example, your DB would look like similar to this:
final class DB
{
public static function getInstance()
{
static $inst = null;
if ($inst === null) {
$inst = new self();
}
return $inst;
}
private function __construct()
{
// your code here ...
}
// your code here ...
}
And then, on your User class you would just get the DB class instance:
class User {
// your code here ...
public function doSomething() {
$db = DB::getInstance();
// your code here ...
}
}
PHP does not handle scopes like Javascript does, your $db is undefined.
The scope of a variable is the context within which it is defined. For the most part all PHP variables only have a single scope. This single scope spans included and required files as well […] Within user-defined functions a local function scope is introduced. Any variable used inside a function is by default limited to the local function scope.
Source: http://php.net/manual/en/language.variables.scope.php
This means that there is only a global scope and a function/method scope in PHP. So, either pass the $db instance into the method as a collaborator
class User{
public function __construct() {}
public function getInfo(Database $db) {
$db->query( /* ... */ );
}
}
$user = new User();
$db = new Database();
$user->getInfo($db);
or pass it in the constructor (dependency injection)
class User{
private $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function getInfo() {
$this->db->query( /* ... */);
}
}
$db = new Database();
$user = new User($db);
$user->getInfo();

Why does this come up as non-object?

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.

PHP use a class variable over several files?

I have 2 files.
One is index.php, which checks for a user login
In this file i create a dbManager class which handles a database.
If dbManager can validate login data, i forward the user to lobby.php (via header).
In lobby.php, i create a Manager class which manages the lobby.
I would like to be able to use the Manager class to forward a query to the DB Manager like this:
index.php
$dbManager = new dbManager();
$userid = $dbManager->validateLogin(($_POST["name"], $_POST["pass"];
if ($userid){
$_SESSION["userid"] = $userid;
header("Location: lobby.php");
}
lobby.php
session_start();
if (isset($_SESSION["userid"])){
$manager = new Manager($_SESSION["userid"]);
$manager->getGames();
}
Class dbManager {
things
}
Class Manager {
public userid;
function __construct($id){
$this->userid = $id;
}
function getGames(){
$ret = $dbManager->queryDB($this->userid);
}
}
I am getting the following notices:
Notice: Undefined variable: dbManager in D:\SecureWAMP_Portable\htdocs\projectX\gameManager.php on line 11
and
Fatal error: Call to a member function getGamesForPlayer() on a non-object in D:\SecureWAMP_Portable\htdocs\projectX\gameManager.php on line 11
What am i doing wrong ?
You should define each class in a new file and include those files (good practice is using autoloading). If you only require one object of a class, you should take a look at the Singleton pattern, as you will always get the same class instance.
For that you define a static protected variable which will hold our class instance and use a static public method to return the class instance and if necessary, create a new class instance. We will define the constructor of each class as private, so the static public method has to be used.
If you have arguments you pass to the constructor, define them for the static public method too and pass the arguments to the constructor in the static public method.
The file DBManager.php will define the class DBManager and will use singleton pattern, as this class will handle connections to the database.
Class DBManager {
static protected $instance = NULL
private __construct() {
//Write the code you want to execute when a new class instance is requested
self::$instance = &$this; //Put a reference to this instance in our static variable
}
//Our static public method to retrieve a class instance
static public app() {
if(self::$instance === NULL OR !is_a(self::$instance, 'DBManager')) { //With is_a we are making sure our self::$instance variable holds a class instance of DBManager
$instance = new DBManager();
}
return self::$instance;
}
/* All your other methods... */
}
Our Manager.php will hold the class Manager and to retrieve a class instance of DBManager we will call our static public method app(). It is valid to also make the class Manager singleton, but only if always only one class instance should exist.
Class Manager {
public userid;
function __construct($id){
$this->userid = $id;
}
function getGames() {
$ret = DBManager::app()->queryDB($this->userid);
}
}
Now our basic index.php just instead of using new DBManager() we will use the static method to return a class instance.
require_once 'DBManager.php';
$dbManager = DBManager::app();
$userid = $dbManager->validateLogin($_POST['name'], $_POST['pass']);
if($userid) {
$_SESSION['userid'] = $userid;
header("Location: lobby.php");
}
In our lobby.php we will use the new Manager class for the first time.
session_start();
require_once 'DBManager.php';
require_once 'Manager.php';
$dbManager = DBManager::app();
if(isset($_SESSION['userid'])) {
$manager = new Manager($_SESSION['userid']);
$manager->getGames();
}
you should not use global vars because it is bad style and will make your life really hard when the application gets bigger.
In your case you want to use the dbManager in different classes.
Since db transaction are likely to be used often, look at the singleton pattern, so there can be only one instance of this class.
But be aware that you should keep the number of singletons as low as possible.
Look into this page to see how the singleton pattern can be implemented in PHP:
http://www.phptherightway.com/pages/Design-Patterns.html
You seem to have a misconception about how php code is distributed and used over multiple files and how these files are used.
For example if you use include() or require() instead of header(), you can refer to the data you have defined so far.
A more advanced approach is to use __autoload() or [spl_autoload_register()](http://php.net/manual/de/function.spl-autoload-register.php) to load classes into your script and have an index.php that starts your script.
(Also read the link #blacksheep_2011 provided)
A good practice is to declare each class in separate file. After that, you need to include the needed class into a file in which you are planning to use that class functionality.
Create file DBManager.php:
Class DBManager {
things
}
Create file Manager.php which will contain Manager class declaration:
include('DBManager.php');
Class Manager {
public userid;
function __construct($id){
$this->userid = $id;
}
function getGames(){
$dbManager = new DBManager();
$ret = $dbManager->queryDB($this->userid);
}
}
Include your classes where they are needed:
index.php
require_once 'DBManager.php';
$dbManager = new DBManager();
$userid = $dbManager->validateLogin(($_POST["name"], $_POST["pass"];
if ($userid){
$_SESSION["userid"] = $userid;
header("Location: lobby.php");
}
lobby.php
session_start();
require_once 'Manager.php';
if (isset($_SESSION["userid"])){
$manager = new Manager($_SESSION["userid"]);
$manager->getGames();
}

PHP Dependency injection and extending from an umbrella class

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));

PHP Static v Singleton class - ConnectionFactory explained

I'm new to this level of PHP programming and I've been reading a post about singleton and static classes. I'm in the process of writing a class that would facilitate my DB connections.
I came across the following code by Jon Raphaelson (here) :
class ConnectionFactory
{
private static $factory;
public static function getFactory()
{
if (!self::$factory)
self::$factory = new ConnectionFactory(...);
return self::$factory;
}
private $db;
public function getConnection() {
if (!$this->db) // this line was modified due to comment
$this->db = new PDO(...); // this line was modified due to comment
return $db;
}
}
function getSomething()
{
$conn = ConnectionFactory::getFactory()->getConnection();
.
.
.
}
It seemed like I had found what I was looking for, however I have a few questions about it.
self::$factory = new ConnectionFactory(...); - I don't see a constructor in this class. Do I just create this constructor and pass in the db details ('dbname', 'user', 'pass', etc)?
the getSomething() function, I'm assuming that the intent was to put all of the actual functions that would retrieve data within the ConnectionFactory class - and this is the reason for this function being in this class. Otherwise, I would have expected this function to be within another class. [edit] SKIP THIS QUESTION, I didn't see the one bracket.
What happens when two users are logged into the site and are requesting the DB connection (both are doing updates, etc)? Will it be an issue that this is a singleton?
Thanks!
This is mostly to expand on my comments under the question...
The better "singleton" pattern would be this:
class ConnectionFactory {
protected static $connection;
public function getConnection() {
if (!self::$connection) {
self::$connection = new PDO(...);
}
return self::$connection;
}
}
The users of this factory should expect an instance of it, not call it themselves:
class Foo {
protected $connectionFactory;
public function __construct(ConnectionFactory $factory) {
$this->connectionFactory = $factory;
}
public function somethingThatNeedsAConnection() {
$connection = $this->connectionFactory->getConnection();
...
}
}
$foo = new Foo(new ConnectionFactory);
This allows Foo to get a connection itself when needed, but also allows you to inject some alternative connection into Foo, for example for testing purposes.
As a convenience measure and to cut down on instantiation complexity, this pattern is also good:
class Foo {
protected $connectionFactory;
public function __construct(ConnectionFactory $factory = null) {
if (!$factory) {
$factory = new ConnectionFactory;
}
$this->connectionFactory = $factory;
}
...
}
This still allows the dependency to be injected and overridden, but it doesn't require you to inject a factory every time you instantiate Foo.
The factory is a singleton because you're not want more than one factory.
You don't need a constructor, you can setup whatever state you need for the factory the getFactory() method. The getFactory() function doesn't get a connection, so don't do any connection related setup here, but not the connection object itself. Factories 'build instances' of classes for you to use, so you'd use the getConnection() method to set the state and construct the connection object the factory is supposed to generate.
getSomething() isn't a method in the ConnectionFactory class, it's just a example of using the factory class to get a factory, then get a connection.
I personally hate method chaining in PHP. What's really happening in getSomething() is:
function getSomething()
{
$factory = ConnectionFactory::getFactory();
$conn = $factory->getConnection();
.
.
.
}
A class doesn't need an explicit constructor in order to be instantiated with new ClassName(). However, if the class is supposed to be a singleton (and it looks like it in this case), the entire point of the pattern is making such instantiations impossible from outside of the class which is why I believe there should be a constructor declared with private keyword.
The get getSomething() is a "standalone" function with an example of using your class.
No. Multiple users use multiple instances of PHP processing your script and nothing is shared among them.

Categories