2

I am attempting to create my first object orientated web app and have a question regarding how to globally reference an object. Not sure if it is necessary or not, but here is a bit of background. The app is a single page app.

Index.php creates a new instance of the "Page" class. This instance checks for a url parameter (page=???) and then checks for the existence of an appropriate content class and calls a function within it. If that page class doesn't exist, it calls another 404 class and displays that instead.

Each page content class is extended from a BasePage class. So for example index.php?page=Home would work like this

+---------+                              +----------+
|         | creates instance of Page.php |          |
|index.php|----------------------------->| page.php |  
|         |                              |          |
+---------+     +----------+             +----------+
                |          |            /
                | base.php |           /
                |          |          / "Page" loads the relevant class
                +----------+         /  and calls a function within it
 home.php                  |       |/_
 extends base.php       +----------+
                        |          |
                        | home.php |
                        |          |
                        +----------+

Hopefully that makes sense so far. The problem I have hit now is I need a "User" object which is created from an instance of a "User" class. This User object will be referenced in almost every page, Index.php, page.php and home.php. What has been happening up to now is I have created instances of the User class in every other class. This works but often results in multiple calls to a database and multiple User objects being created. I think the best approach would be to make the User class a singleton class, but where do I create that singleton object. If I create the instance in base.php, home.php has reference to it, but page.php and index.php do not. If I create the instance in index.php, no other page has access to it unless I pass it to every class - which gets messy. (I know, i have tried).

So my question is, how do I make a singleton object that is reachable by every page? I thought putting it in the global scope would help, but a) i don't know how to do that and b) i have read scores of articles about why globals are a bad thing.

Can anyone offer any advice? How should I be creating my User object?

Thanks

Typhoon101
  • 2,063
  • 8
  • 32
  • 49
  • `What has been happening up to now is I have created instances of the User class in every other class.` can you not create the user out of your classes and construct them with the user object as a parameter? that would prevent multiple users from being instanciated – Félix Adriyel Gagnon-Grenier Mar 24 '15 at 15:17
  • 1
    Passing the user object as a parameter to the constructor of each other class is a much cleaner solution than using a Singleton. Also, are you sure that each page must extend the base page class? – BenM Mar 24 '15 at 15:19
  • 2
    I would encourage you to learn about [autoloading in PHP](http://php.net/manual/en/language.oop5.autoload.php) and [dependency injection](http://stackoverflow.com/questions/130794/what-is-dependency-injection). Both of these will play a part in your solution. – Jason McCreary Mar 24 '15 at 15:20
  • Yes, I have tried passing the user object to the constructor of other classes, but in reality, there are loads of other classes that are used. It got very messy passing User objects as parameters, and didn't feel like the right thing to be doing. Some of the other classes contain static methods so not only would i need to pass the object to constructor classes, but also to every method within them – Typhoon101 Mar 24 '15 at 15:22
  • [this](http://www.sitepoint.com/dependency-injection-with-pimple/) article, while directed at pimple, helped me understand useful principles of dependency injection – Félix Adriyel Gagnon-Grenier Mar 24 '15 at 15:24
  • 1
    Passing objects to your classes is the way to go however, in addition to not using static methods. You can use `$myClassInstance::staticMethod()` in most newer PHP versions though. – ToBe Mar 24 '15 at 15:37

2 Answers2

2

What you are looking for is a Singleton. But the Singleton Pattern is deemed VERY bad practice and should be avoided at all costs. You should try to avoid any solution that requires you to have global stuff.

A better approach to a very simplistic application structure would be to create some kind of main class for you application and assign your component classes as properties to this main class. That's already half way to a nice little Dependency Injection.

Some small articles on why a singleton is bad:

Community
  • 1
  • 1
ToBe
  • 2,667
  • 1
  • 18
  • 30
  • Can you provide some references about why `singleton pattern` is _bad practice_? – Jon Surrell Mar 24 '15 at 15:25
  • 1
    @JonSurrell [examples](http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons) [are](http://programmers.stackexchange.com/questions/40373/so-singletons-are-bad-then-what) [numerous](http://blogs.msdn.com/b/scottdensmore/archive/2004/05/25/140827.aspx). – Félix Adriyel Gagnon-Grenier Mar 24 '15 at 15:29
  • It basically come down to this: You have to use a class whose instantiation is out of your hands and can not be changed or replaced for whatever the future brings. If you have more then one application which share this class, they cant use their own version of it. It also makes mocking stuff nearly impossible. – ToBe Mar 24 '15 at 15:31
  • 1
    @FélixGagnon-Grenier providing references to back up claims made in an answer is a big win for everyone, IMO. – Jon Surrell Mar 24 '15 at 15:31
1

Should you choose to try and implement a singleton, this would be an example of how to ensure that your user is always the same:

class User {
    private static $instance = null;
    private $name;
    public function __construct($name) {
        $this->name = $name;
    }
    public static function get($name) {
        if (self::$instance === null) self::$instance = new self($name);
        return self::$instance;
    }
    public function sayHello() {
        echo 'Hello my name is '.$this->name;
    }
}

$user = User::get('Bob');
$user->sayHello(); // Hello my name is Bob

be aware that such practices are prone to errors, raging and hate by mixing static classes and dynamic instances.

If you want to learn about dependency injection, pimple was my introduction to it, and by replicating their code I was able to create much more solid code; that is accessible objects created with their associated good dependencies, wherever you access them.

the principle of such container is to define your objects then retrieve them, constructed only those necessary. Say you are using pimple:

$cnt = new Pimple();
$cnt['page'] = function($c) { 
// the class pimple passes the container itself to the functions you define
    return new page($c['session']); 
// defines the page key to return a new page
};
$cnt['session'] = function() {
    return new session();
};
$page = $cnt['page']; 
// constructs the session because it is called to construct the page
  • 1
    Thanks everyone. Lots of good advice here. It looks like the consensus is dependency injection, so I will look further into that. I already do use an autoloader, and am familiar with DI, so maybe I just need to refine my knowledge a bit. Thanks again for all the help. Very useful. – Typhoon101 Mar 24 '15 at 15:41
  • Interesting approach to OOP, but wouldnt you now define the functionality of your class from outside the class and thus shifting responsibility and logic from the class itself to external factors? Shouldnt a class rather be fairly self defining within it's own responsibilities/concerns? – ToBe Mar 25 '15 at 10:49
  • Hey, thanks for following up! I'm not sure what you mean? `wouldnt you now define the functionality of your class from outside the class` I dont see that I'm doing this? the first code centralizes functionalities, even those that could be taken in charge by a UserFactory, the second one is a dependency injection definition through the use of closures and is used by dependency injection containers such as [pimple](http://www.sitepoint.com/dependency-injection-with-pimple/), or when defining [services in angularjs](https://docs.angularjs.org/guide/services). Or is it not what you mean? – Félix Adriyel Gagnon-Grenier Mar 25 '15 at 15:34