this has been driving me nuts (Drinking Obscene amounts of Coffee and working all night doesn't help) I want to gain access to a class from wherever I am within the application. I instantiate the Class within my index page (which auto loads my lib/classes)But it seems I cannot gain global access to it. This is my index page:
function __autoload($class)
{
require LIBS . $class .'.php';
}
$Core = new Core($server, $user, $pass, $db);
This auto load my Lib/classes perfectly and then I instantiate my Core (This is auto loaded within my Lib/core.php)
Then within my Core is where I create the usual, a database connection, get and check the URL and where I instantiate a few classes (Which are auto loaded) I create a __construct and this is where I want to instantiate a Template class. I wish to have global access for accessing the class within any of my controllers and models.
class Core {
function __construct(DATABASE VARIABLES IN HERE)
{
$this->Template = new Template();
}
}
Ok so I thought I could access the Template Object by doing the following within my parent model and parent controller:
class Controller
{
public $Core;
function __construct()
{
global $Core;
$this->Core = &$Core;
}
}
The Controller is a parent extends all my controllers, therefore I assumed I could just write $this->Core->Template->get_data(); to access the a Template Method? This Seems to throw an error.
Im sure it must be something simple that I have overlooked, if anyone can give me a hand that would be great. This problem is driving me crazy.
Also a side note within my child controllers within my __construct I construct the Parent parent::_construct();
The Error seems to be Notice: Trying to get property of non-object and Fatal error: Call to a member function get_data() on a non-object.
class Controller
{
public $Core;
function __construct(Core $core)
{
$this->Core = $core;
}
}
class ControllerChild extends Controller {
function __construct(Core $core, $someOtherStuff){
parent::__construct($core) ;
//And your $this->Core will be inherited, because it has public access
}
}
note: You dont have to use & sign when working with objects. Objects are automatically passed by reference.
You could make Core a singleton and implement a static function to receive a pointer to the object.
define ('USER', 'username');
define ('PASS', 'password');
define ('DSN', 'dsn');
class Core {
private static $hInstance;
public static function getInstance() {
if (!(self::$hInstance instanceof Core)) {
self::$hInstance = new Core(USER, PASS, DSN);
}
return self::$hInstance;
}
public function __construct($user, $pass, $dsn) {
echo 'constructed';
}
}
Then within your Controller you can use:
$core = Core::getInstance();
Which should output constructed.
Edit
Updated to demonstrate how to construct via the static function w/ output.
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 have a class base which has a property called load which is a object of the class load. The load class has a function called view that includes pages. Now I need to call,
This is similar to CodeIgniter's $this->load->view("test.php");
Load Class
class Load {
public function view($page){
//this function loads views to display
include($page);
}
public function model($class){
//loads model classes
}
public function library($class){
//loads libraries
}
}
Base Class
class Base {
function __construct(){
$this->load = new Load();
}
}
Index page
$base = new Base();
$base->load->view("test1.php");
this1.php
echo "testing1\n";
$this->load->view("test2.php");
test2.php
echo "testing2";
The output should be
testing1
testing2
What you really want I think is to follow a factory pattern. (At least, that's what you mean if you want the $view variable to actually contain an instance of the Load class)
Make the constructor protected, so that the only the class can create new instances, then in the base class add a static method, e.g. 'factory' which returns an instance of the desired class.
Then your code would look like
$view=Base::factory();
$view->view("test1.php");
NOTE: this answer was made before any edit made to the question. Please evaluate accordingly
You need to have the functions marked as public to allow them to be called from outside of the defining class (this is simplified of course)
Try the following:
class Load{
public function view($page){
include($page);
}
}
class Base{
public $load;
function __construct(){
$this->load = new Load();
}
}
(The uppercase class names are my own preference)
This should work, but it's not a good design from a clean OOP perspective, because the users of the Base class need to know how the Load class works. This is called "tight coupling" and should be avoided as much as possible.
I suggest to consider the following alternative:
class Load{
public function view($page){
include($page);
}
}
class Base{
private $load; //note the private modifier
function __construct(){
$this->load = new Load();
}
public function view($page){
$this->load->view($page);
}
}
This way I just need to know that Base has a method view($page) and i don't have to know anymore what Load does at all.
If in the future you want to change the Load class you can do it under the hood without the Base users ever noticing it, if you do it right:
Suppose you define a class:
class BetterLoad {
private function foo(){
//do something awesome
}
private function advancedView($page){
include($page);
$this->foo();
}
}
and you want to incorporate this inside Base instead of the old Load.
class Base{
private $adv_load; //note the private modifier
function __construct(){
$this->adv_load = new BetterLoad();
}
public function view($page){
$this->adv_load->advancedView($page);
}
}
That's it. You won't need to change anything else in your code. Just go on using the old $base_obj->view($page) and you're good to go, without even noticing the change.
Im having problems extending a Class.
This is what I was trying to do:
class Core
{
protected $db;
public function __construct()
{
$this->set_db_class();
}
private function set_db_class ()
{
include_once ( './classes/Database.php' );
$this->db = new Database();
}
}
class Functions extends Core
{
public function __construct()
{
parent::__construct();
}
public static function create_user ()
{
$this->db->query ( "INSERT ..." );
}
}
So, that's the estructure, but my problem is that I'm getting the following error:
Fatal error: Using $this when not in object context in /Applications/XAMPP/xamppfiles/htdocs/own/newsite/classes/class.Functions.php on line 10
What can I do to solve this?
Declare create_user as non-static and call it from an instance, otherwise (as the error message says) you cannot access $this, since $this is always a reference to the current instance. In a static context, there isn't one.
$functions = new Functions();
$functions->create_user();
instead of
Functions::create_user();
If you want to bundle functions that are not logically related to each other, use a namespace and not a class. You can go with an all-static class (every tiny property and method is static so that you don't need an instance at any time), but that's a horrible solution and not what classes should be used for.
I'm working on a sql class, and trying to figure out how to retrieve data from an external php file, to be available in the entire class.
Im guesing i have to do something like this:
class sqlQuery {
protected $database = array();
function __construct(){
require_once (config.php);
}
}
class model extends sqlQuery {
function __construct() {
$this->connect($this->database['hostname'], $this->database['user'], $this->database['pass'], $this->database['database']);
}
}
The file might contain other information in the future, so I want it available to more then just the extended class.
Firstly you should call parent constructor in class model:
function __construct() {
parent::__construct();
// your other logic there
}
Then just change constructor of your parent class for filling $this->database array