I've recently learned about the advantages of using Dependency Injection (DI) in my PHP application. However, I'm still unsure how to create my container for the dependencies, or whether I should be using DI at all for the online forum that I'm building.
The following code is my version of the DI container I have made based on the example I learned from here .
class ioc {
var $db;
var $session;
var $user_id;
static function newUser(static::$db, static::$user_id) {
$user = new User($db, $user_id);
return $user;
}
static function newLogin(static::$db, static::$session) {
$login = new Login($db, $session);
return $login;
}
}
$user = ioc::newUser();
$login = ioc::newLogin();
I have a few questions:
1) Where should I instantiate my injected dependencies, such as $database, $session, etc? Would it be outside the container class, or inside the container's constructor.
2) What if I need to create a multiple instances of the User class inside other classes? I can't inject the previously instantiated $user object because that instance is already being used. However, creating the multiple User instances inside of another class would violate the rules of DI. For example:
class Users {
function __construct($db, $user_id) {
$this->db = $db;
$this->user_id = $user_id;
}
function create_friends_list() {
$st = $this->$db->prepare("SELECT user_id FROM friends WHERE user_id = $this->user_id");
$st->execute();
while($row = $st->fetch()) {
$friend = ioc::newUser($row['user_id']);
$friend->get_user_name();
$friend->get_profile_picture();
}
}
}
3) I'm wondering if I should even adopt DI, knowing that I have to rewrite all of my previous code. I've previously been relying on global variables that I instantiate in my initialize.php, which is included in all my files.
It seems to me that DI creates a lot of overhead and there are situations where it is unusable (as in my #2 example). The following site is from a developer who cites many good reasons not to use DI. Does his arguments have any merit? Or am I just using DI wrong?
check this link.
Where should I instantiate my injected dependencies, such as $database, $session, etc? Would it be outside the container class, or inside the container's constructor.
Ideally your database connection and session would be bootstrapped in. Proper DI requires an instance of a base object for which everything is registered into. So taking your IOC class as an example you need to make an instance of it ($ioc = new IOC();) then you need some kind of service provider class say
$ioc->register('database', new DatabaseServiceProvider($host, $user, $pass))
Now every time you want a connection to the database you just need to pass in $ioc->get('database'); a very rough example but I think you can see the idea is basically to store everything inside a registry and nothing is statically binded meaning you can create another instance of $ioc with totally different settings making it easy to create connections to say a different database for testing purposes.
What if I need to create a multiple instances of the User class inside other classes? I can't inject the previously instantiated $user object because that instance is already being used. However, creating the multiple User instances inside of another class would violate the rules of DI.
This is a common issue and there are multiple different solutions. Firstly your DI should show the difference between logged in user and just a user. You would probably want to register your logged in user but not just any user. make your user class just normal and use
$ioc->register('login-user', User::fetch($ioc->get('database'), $user_id));
so now $ioc->get('login-user') returns your logged in user. You can then use User->fetchAll($ioc->get('database')); to get all your users.
I'm wondering if I should even adopt DI, knowing that I have to rewrite all of my previous code. I've previously been relying on global variables that I instantiate in my initialize.php, which is included in all my files.
If you need to rewrite all your code to use DI you shouldn't probably do it. Maybe look into making a new project and work in some of your old code if you have the time. If your codebase is large I would recommend looking into breaking it down into smaller projects and using RESTFUL apis for getting and saving data. Good examples of writing APIs would be for putting your forum search into its own application /search/name?partial-name=bob would return all users with the word bob in it. you could build it up and make it better over time and use it in your main forum
I hope you understand my answers but if you need any more info let me know.
I was going to write this a comment, but it grew too long. I am not an expert so I will just give my point of view from what I've learned through few years practicing and here in SO. Feel free to use or question any part of my answer (or none).
1.- Outside. What does the container do? The answer should be a single thing. It shouldn't have to be responsible to initialize the classes, connect to the database, handle the session and other things. Each class does one thing only.
class ioc
{
public $db;
// Only pass here the things that the class REALLY needs
static public function set($var, $val)
{
return $this->$var = $val;
}
static function newDB($user, $pass)
{
return new PDO('mysql:host=localhost;dbname=test', $user, $pass);
}
static function newUser($user_id)
{
return new User($db, $user_id);
}
static function newLogin($session)
{
return new Login($this->db, $session);
}
}
if (ioc::set('db',ioc::newDB($user, $pass)))
{
$user = ioc::newUser($user_id);
$login = ioc::newLogin($session);
}
2.- You shouldn't do $friend = ioc::newUser($row['user_id']); inside your class. There you are assuming that there's a class called ioc with a method called newUser(), while each class should be able to act on it's own, not based on [possibly] other existing classes. This is called tight coupling. Basically, that's why you shouldn't use global variables either. Anything used within a class should be passed to it, not assumed in the global scope. Even if you know it's there and your code works, it makes the class not reusable for other projects and much harder to test. I will not extend myself (PUN?) but put a great video I discovered here in SO so you can dig more: The Clean Code Talks - Don't Look For Things.
I'm not sure about how the class User behaves, but this is how I'd do it (not necessary right):
// Allow me to change the name to Friends to avoid confusions
class Friends
{
function __construct($db)
{
$this->db = $db;
}
function create_friends_list($user_id)
{
if (!empty(id))
{
// Protect it from injection if your $user_id MIGHT come from a $_POST or whatever
$st = $this->$db->prepare("SELECT user_id FROM friends WHERE user_id = ?");
$st->execute(array($user_id));
$AllData = $st->fetchAll()
return $AllData;
}
else return null;
}
// Pass the $friend object
function get_friend_data($friend)
{
$FriendData = array ('Name' => $friend->get_user_name(), 'Picture' => $friend->get_profile_picture());
return $FriendData;
}
}
$User = ioc::newUser($user_id);
$Friends = new Friends($db);
$AllFriendsIDs = array();
if ($AllFriendsIDs = $Friends->create_friends_list($User->get('user_id')))
foreach ($AllFriendsIDs as $Friend)
{
// OPTION 1. Return the name, id and whatever in an array for the user object passed.
$FriendData = $Friends->get_friend_data(ioc::newUser($Friend['user_id']));
// Do anything you want with $FriendData
// OPTION 2. Ditch the get_friend_data and work with it here directly. You're already in a loop.
// Create the object (User) Friend.
$Friend = ioc::newUser($Friend['user_id']);
$Friend->get_user_name();
$Friend->get_profile_picture();
}
I didn't test it, so it has probably some small bugs.
3.- If you are learning while coding, you will have to rewrite MANY things. Try to do somethings right from the beginning so you don't need to rewrite everything, but only the classes/methods and adopting some conventions for all your code. For example, never echo from within the function/method, always return and echo from outside. I'd say that yes, it's worth it. It's bad that you have to loose 1 or 2 hours just rewriting something, but if it has to be done, do it.
PS, sorry, I changed your bracket style everywhere.
EDIT
Reading other answers, while you shouldn't connect to the database with your ioc object, it should be perfeclty fine create a new object with it. Edited above to see what I mean.
Instead of globals in your init.php define your objects like:
ioc::register('user', function() {
return new User();
});
And inisde your create_friends_list method use:
ioc::get('user')->newUser($user_id);
This is a really plain implementation. Check out:
http://net.tutsplus.com/tutorials/php/dependency-injection-huh/?search_index=2
or
http://net.tutsplus.com/tutorials/php/dependency-injection-in-php/?search_index=1
for more information.
The question you asked has one very important catch, which I asked about in a comment. Any time you examine the possibility of pausing forward progress in order to go back and accomplish a less than trivial re-factoring of existing classes, you have to really think about the payoff.
You replied:
It seems a bit complex (and very different) and more work than it's worth to implement at this point. I'm just trying to hack out something real quick and see if it gains traction.
Remember that you have the following things to do if you want to implement an DI with an IoC container properly:
Implement the IoC container and registry at bootstrap
Modify all existing classes that have dependencies to allow setter injection, while not depending on the IoC container itself
Re-writing / Re-structuring of your tests as needed
A note on the second bullet which reflects my own personal view and preference, developing with DI in mind can be hard and you want the full reward it can give. One of the biggest rewards is completely decoupled objects, you don't get the complete part of that if everything still depends on an IoC container.
That's a lot of work, especially if you haven't been considering the pattern until now. Sure, you'll have clear benefits, such as lots of re-usable and easily testable objects when you're done. But, as with all re-factoring, nothing new would be accomplished in the context of adding or finishing new functionality and features. Is difficulty testing or tight coupling getting in the way of that? That's something you'd have to weigh.
What I suggest you do instead is keep the pattern in mind as you write new code. Provide setter methods to inject dependencies, which can be utilized manually or through an IoC container. However, have your classes continue to create them just in time if none have been injected and avoid composition in a constructor.
At that point you'll have a collection of classes that lend much better to inversion of control, if that's a pattern you want to pursue in the future. If it works well for you and it's something you really want to incorporate into your design decisions, you can easily re-factor to remove the JIT logic later.
In conclusion, I'm afraid that the only thing you'll end up with if you try implementing it completely, and properly right now is a mess. You can change how you write classes now going forward, but I wouldn't go back and try to implement it across the board.
Related
I'm not new to PHP or programming at all. But recently I was thinking about website programming in PHP and how easier it was before the OOP. Anyway, I prefer OOP than the old procedural style.
I want to implement a website but it seems I always have to use a global or a static variables. And I'm starting to wonder, how can I do it without those?
Anyway, what I'm talking about is having a class for each "component" of the website.
For example if it was an url shortener website it would be: links, members, database.
What I'm talking about is way more complicated, at least 8 classes.
Anyway, my current approach is the following:
$database = new Database(...);
$links = new Links($db);
$users = new Users($db);
Anyway, for example I want to get all the links a user posted by its ID, I need to use both links and both users components.
Is there any other way I could do this? Any other approach? except passing them as constructor parameters.
You should have the following components:
Business objects, which model and express one particular "thing" in your app:
class Link { ... }
class User { ... }
These don't "do" anything, they're just there to formalise your data structures. These objects have getter and setter methods to get and set individual attributes, which are also validated there:
public function setUrl($url) {
if (!/* validate the URL here*/) {
throw new InvalidArgumentException("$url is not a valid URL");
}
$this->url = $url;
}
Minimum required data is part of the constructor. This ensures your data integrity application-wide. It allows you to assert that when you have an instance of Link, the data expressed by it is minimum valid data for a link.
A database link. Only the bare necessary thing to connect to a database, nothing more, nothing less. A raw PDO or mysqli object will do just fine.
A data-object mapper, which takes a database link and knows how to store business objects in the database and how to retrieve them:
class LinkStorage {
protected $db;
public function __construct(PDO $db) {
$this->db = $db;
}
}
This class has all the various methods of how to retrieve things from your database:
public function getById($id) {
$stmt = $this->db->prepare('SELECT ... FROM ... WHERE id = :id');
$stmt->execute(compact('id'));
if (!$data = $stmt->fetch()) {
throw new RuntimeException("Record with id $id does not exist");
}
return new Link($data['url']);
}
You can have all sorts of different queries encapsulated this way, e.g.:
/**
* Returns all links by a particular user.
* #param User $user
* #return Link[]
*/
public function getAllFromUser(User $user) {
...
}
The usage is then simple:
$db = new PDO(...);
$linkStorage = new LinkStorage($db);
$userStorage = new UserStorage($db);
$user = $userStorage->getById($id);
$links = $linkStorage->getAllFromUser($user);
This kind of code would then be encapsulated in a service class, which holds all the possible "actions" you can do in your app. registerUser(array $data), getLinksOfUser($id), newLinkFromPostData(array $data) etc.
What I've just described is basically the model portion of an MVC-style application. The other two parts would be controllers which call the service methods, and views which output data retrieved from service methods. This approach keeps responsibilities separate and isolated and allows you to put higher-level logic and functionality together like building blocks. Business objects are the lowest building block, their structure needs to be solid and well defined for the rest to work. Data-object mappers just concern themselves with putting those objects into the database and getting them back out again. Services then put all this together in various complex ways and make things happen.
You shouldn't have any cyclic dependencies with this, as responsibilities are well separated. Your individual dependencies may still be somewhat complex. If it becomes too cumbersome to instantiate classes, you'll want to look into Factories:
$factory = new Factory;
$userStorage = $factory->newUserStorage();
All the complexity of instantiation is encapsulated in this factory. One step further are dependency injection containers, who you can configure in, for example, an XML file to specify which class depends on what, and then the DI container will take care of it for you.
I was thinking about website programming in PHP and how easier it was
before the OOP
Well, stick to procedural then. If it is easier to write a well written website in a procedural or functional way then opposed to the ojbect-oriented way. Stick ti what you are used to. OO isn't better then functional. It's just a different approach.
the public void main() in php
In the lanuage Java every piece of software we write has a single entry point. The public void main() method. This method fires up the entire application and passes in the arguments provided on startup. It is also the only exit point in the application. the application ends in this method (unless it crashes).
In php, there is no single entry point. We have a bunch of files that run some scripts that do some more stuff and then somewhere along the line another script decides to return stuff and die();
Dependency injection and how IoC libraries can help
When using dependency injection, it becomes a real pain in the a$$ when creating objects and passing arround the correct instance of a class. We start solving this problem with ugly solutions: Singleton, globals, statics, ... Making our software more and more tightly coupled and harder to maintain.
Inversion of Control can help here. there are some really greate articles on the webz.
You can use autoloading in PHP for a better solution:
http://php.net/manual/en/language.oop5.autoload.php
I'm not new to PHP or programming at all. But recently I was thinking about website programming in PHP and how easier it was before the OOP. Anyway, I prefer OOP than the old procedural style.
I want to implement a website but it seems I always have to use a global or a static variables. And I'm starting to wonder, how can I do it without those?
Anyway, what I'm talking about is having a class for each "component" of the website.
For example if it was an url shortener website it would be: links, members, database.
What I'm talking about is way more complicated, at least 8 classes.
Anyway, my current approach is the following:
$database = new Database(...);
$links = new Links($db);
$users = new Users($db);
Anyway, for example I want to get all the links a user posted by its ID, I need to use both links and both users components.
Is there any other way I could do this? Any other approach? except passing them as constructor parameters.
You should have the following components:
Business objects, which model and express one particular "thing" in your app:
class Link { ... }
class User { ... }
These don't "do" anything, they're just there to formalise your data structures. These objects have getter and setter methods to get and set individual attributes, which are also validated there:
public function setUrl($url) {
if (!/* validate the URL here*/) {
throw new InvalidArgumentException("$url is not a valid URL");
}
$this->url = $url;
}
Minimum required data is part of the constructor. This ensures your data integrity application-wide. It allows you to assert that when you have an instance of Link, the data expressed by it is minimum valid data for a link.
A database link. Only the bare necessary thing to connect to a database, nothing more, nothing less. A raw PDO or mysqli object will do just fine.
A data-object mapper, which takes a database link and knows how to store business objects in the database and how to retrieve them:
class LinkStorage {
protected $db;
public function __construct(PDO $db) {
$this->db = $db;
}
}
This class has all the various methods of how to retrieve things from your database:
public function getById($id) {
$stmt = $this->db->prepare('SELECT ... FROM ... WHERE id = :id');
$stmt->execute(compact('id'));
if (!$data = $stmt->fetch()) {
throw new RuntimeException("Record with id $id does not exist");
}
return new Link($data['url']);
}
You can have all sorts of different queries encapsulated this way, e.g.:
/**
* Returns all links by a particular user.
* #param User $user
* #return Link[]
*/
public function getAllFromUser(User $user) {
...
}
The usage is then simple:
$db = new PDO(...);
$linkStorage = new LinkStorage($db);
$userStorage = new UserStorage($db);
$user = $userStorage->getById($id);
$links = $linkStorage->getAllFromUser($user);
This kind of code would then be encapsulated in a service class, which holds all the possible "actions" you can do in your app. registerUser(array $data), getLinksOfUser($id), newLinkFromPostData(array $data) etc.
What I've just described is basically the model portion of an MVC-style application. The other two parts would be controllers which call the service methods, and views which output data retrieved from service methods. This approach keeps responsibilities separate and isolated and allows you to put higher-level logic and functionality together like building blocks. Business objects are the lowest building block, their structure needs to be solid and well defined for the rest to work. Data-object mappers just concern themselves with putting those objects into the database and getting them back out again. Services then put all this together in various complex ways and make things happen.
You shouldn't have any cyclic dependencies with this, as responsibilities are well separated. Your individual dependencies may still be somewhat complex. If it becomes too cumbersome to instantiate classes, you'll want to look into Factories:
$factory = new Factory;
$userStorage = $factory->newUserStorage();
All the complexity of instantiation is encapsulated in this factory. One step further are dependency injection containers, who you can configure in, for example, an XML file to specify which class depends on what, and then the DI container will take care of it for you.
I was thinking about website programming in PHP and how easier it was
before the OOP
Well, stick to procedural then. If it is easier to write a well written website in a procedural or functional way then opposed to the ojbect-oriented way. Stick ti what you are used to. OO isn't better then functional. It's just a different approach.
the public void main() in php
In the lanuage Java every piece of software we write has a single entry point. The public void main() method. This method fires up the entire application and passes in the arguments provided on startup. It is also the only exit point in the application. the application ends in this method (unless it crashes).
In php, there is no single entry point. We have a bunch of files that run some scripts that do some more stuff and then somewhere along the line another script decides to return stuff and die();
Dependency injection and how IoC libraries can help
When using dependency injection, it becomes a real pain in the a$$ when creating objects and passing arround the correct instance of a class. We start solving this problem with ugly solutions: Singleton, globals, statics, ... Making our software more and more tightly coupled and harder to maintain.
Inversion of Control can help here. there are some really greate articles on the webz.
You can use autoloading in PHP for a better solution:
http://php.net/manual/en/language.oop5.autoload.php
I'm moving from Globals and Singletons (=bad?) to Dependency Injections (=good?) in PHP and I'm very new to it.
I have read many related topics on Stack Overflow already but I still can't understand the main principles of DI.
Please tell me if I'm doing it right (it's just shortened pseudo-code):
// first connect to DB
$sql = new Sql();
// create logger (I'm writing logs to Database so I need to pass $sql to it)
$log = new Log($sql);
// restore logged in user, get info about user, unread messages...
// class "User" needs access to Database and Logs:
$user = new User($sql, $log);
// now we need to collect all the data of current section of my Website
// I'm using Model and Controller:
$model = new FrontPageModel($sql, $user, $log);
$pageController = new FrontPageController($model);
Though it may look OK at this step but what if I need to get access to more classes like Config, Session, etc.?
Should my code transform to this?
$model = new FrontPageModel($sql, $user, $log, $config, $session);
Isn't it too much already?
I know someone could advice to use a kind of big "Application" class and put Config, Session, Log, Db objects inside of this class but I feel that it isn't very good idea.
Next question - what if I need to get logged User's ID inside of my FrontPageController? I did not pass an instance of "User" to FrontPageController, but it was passed before (in chain) to FronPageModel.
class FrontPageController{
private $model;
function __construct($model){
$this->model = $model;
}
function getData(){
echo $this->model->user->id; // is it right way?
}
}
That "$this->model->user->id" seems like overkill to me.
It might not be the prettiest, but you're certainly not doing it "wrong". You're demonstrating classing constructor injection, but you could perhaps refactor to keep some of the disparate objects separated a bit more.
My suggestion would be to look at established PHP DI containers. See how they work, use them in a few apps, and (1) you'll have much more testable apps and (2) you'll be much more comfortable with DI in general.
Take a look at one or more of the following:
Pimple
Aura DI
Symfony DI
Like all the joyful stuff, it' depends.
I would put everything inside a factory class, or several factory classes for different sub systems.
class AppFactory {
protected $_sql;
public function getSql()
{
if (!empty($this->_sql))
return $this->_sql;
$this->_sql = new Sql();
}
}
Then initial it on application run. Configure if needed.
$factory = new AppFactory();
// set config
// $factory->internallyGenerateSingletons = true;
And now every other class can use it. And of course you have injection to put factory inside.
$model = new FrontPageModel($factory);
// or
// $model->useFactory($factory);
Regarding other question. Is this the right way.
...
$this->model->user->id
...
I would say it's ok but batter is to use methods like this:
...
$this->model->getUser()->id
..
So you can mock user class when doing unit testing. And it makes application more flexible.
It seems like you have got the idea of dependency injection right. You even question Symfonies idea of DI (in a comment), which shows more mature understanding.
A next step to solving the questions you have is to take a step away from leaking technical information from one layer into another. Eg your controllers don't care that they talk to a model. Your model doesn't care it talks to an sql or nosql database.
Another is about responsabilities. Does the user really need to do all of that? Is the user responsible for logging? It may be a correct choice, but it also seems that you ask it to do a lot of things. Does the user construct itself, update itself? etc
$this->model->user->id; // is it right way? Is not the right way. 1) This is very strong coupling. 2) do you need the id of the user? is the user only put in as dependency to get something from it? perhaps something like $this->user->showUserInfo($userInfoDisplay) can be used?
My code is located here: https://github.com/maniator/SmallFry
Should I make it so that that the App class does not have to use static functions but at the same time be able to set and set variables for the app from anywhere?
Or should I keep it how it is now with App::get and App::set methods?
What are the advantages and disadvantages of both?
How would I accomplish that 1st task if I was to undertake it?
Related Question
Sample code:
//DEFAULT TEMPLATE
App::set('APP_NAME', 'SmallVC');
//END DEFAULT TEMPLAT
//
//DEFAULT TEMPLATE
App::set('DEFAULT_TEMPLATE', 'default');
//END DEFAULT TEMPLATE
//DEFAULT TITLE
App::set('DEFAULT_TITLE', 'Small-VC');
//END DEFAULT TITLE
//LOGIN SEED
App::set('LOGIN_SEED', "lijfg98u5;jfd7hyf");
//END LOGIN SEED
App::set('DEFAULT_CONTROLLER', 'AppController');
if(App::get('view')){
$template_file = $cwd.'/../view/'.App::get('view').'/'.App::get('method').'.stp';
if(is_file($template_file)){
include $template_file;
}
else {
include $cwd.'/../view/missingview.stp'; //no such view error
}
}
else {
App::set('template', 'blank');
include $cwd.'/../view/missingfunction.stp'; //no such function error
}
I think you have a feeling that static is bad. What I am posting may seem fairly crazy as it is a massive change. At the very least hopefully it presents a different idea of the world.
Miško Hevery wrote static methods are a death to testability.
I like testing, so for that reason I don't use them. So, how else can we solve the problem? I like to solve it using what I think is a type of dependency injection. Martin Fowler has a good but complicated article on it here.
For each object at construction I pass the objects that are required for them to operate. From your code I would make AppController become:
class AppController
{
protected $setup;
public function __construct(array $setup = array())
{
$setup += array('App' => NULL, 'Database' => NULL);
if (!$setup['App'] instanceof App)
{
if (NULL !== $setup['App'])
{
throw new InvalidArgumentException('Not an App.');
}
$setup['App'] = new App();
}
// Same for Database.
// Avoid doing any more in the constructor if possible.
$this->setup = $setup;
}
public function otherFunction()
{
echo $this->setup['App']->get('view');
}
}
The dependancies default to values that are most likely (your default constructions in the if statements). So, normally you don't need to pass a setup. However, when you are testing or want different functionality you can pass in mocks or different classes (that derive from the right base class). You can use interfaces as an option too.
Edit The more pure form of dependency injection involves further change. It requires that you pass always pass required objects rather than letting the class default one when the object isn't passed. I have been through a similar change in my codebase of +20K LOC. Having implemented it, I see many benefits to going the whole way. Objects encapsulation is greatly improved. It makes you feel like you have real objects rather than every bit of code relying on something else.
Throwing exceptions when you don't inject all of the dependencies causes you to fix things quickly. With a good system wide exception handler set with set_exception_handler in some bootstrap code you will easily see your exceptions and can fix each one quickly. The code then becomes simpler in the AppController with the check in the constructor becoming:
if (!$setup['App'] instanceof App)
{
throw new InvalidArgumentException('Not an App.');
}
With every class you then write all objects would be constructed upon initialisation. Also, with each construction of an object you would pass down the dependencies that are required (or let the default ones you provide) be instantiated. (You will notice when you forget to do this because you will have to rewrite your code to take out dependencies before you can test it.)
It seems like a lot of work, but the classes reflect the real world closer and testing becomes a breeze. You can also see the dependencies you have in your code easily in the constructor.
Well, if it was me, I would have the end goal of injecting the App dependency into any class (or class tree) that needs it. That way in testing or reusing the code you can inject whatever you want.
Note I said reuse there. That's because it's hard to re-use code that has static calls in it. That's because it's tied to the global state so you can't really "change" the state for a subrequest (or whatever you want to do).
Now, on to the question at hand. It appears that you have a legacy codebase, which will complicate things. The way I would approach it is as follows:
Create a non-static version of the app class (name it something different for now) that does nothing but proxy its get/set calls to the real app class. So, for example:
class AppProxy {
public function set($value) {
return App::set($value);
}
}
For now, all it has to do is proxy. Once we finish getting all the code talking to the proxy instead of the static app, we'll make it actually function. But until then, this will keep the application running. That way you can take your time implementing these steps and don't need to do it all in one big sweep.
Pick a main class (one that does a lot for the application, or is important) that you easily control the instantiation of. Preferably one that you instantiate in only one place (in the bootstrap is the easiest). Change that class to use Dependency Injection via the constructor to get the "appproxy".
a. Test this!
Pick another class tree to work on, based on what you think will be most important and easiest.
a. Test!!!
If you have more calls to App::, Go to #3
Change the existing App class to be non-static.
a. Test!!!!!!!!!!
Remove the AppProxy and replace with App in the dependency injectors. If you did it right, you should only have one place to change to make this switch.
Pat yourself on the back and go get a drink, cause you're done.
The reason that I segmented it out like this is that once a step is completed (any step), you can still ship working software. So this conversion could take literally months (depending on the size of your codebase) without interrupting business as usual...
Now, once you're done, you do get some significant benefits:
Easy to test since you can just create a new App object to inject (or mock it as needed).
Side effects are easier to see since the App object is required wherever it could be changed.
It's easier to componentize libraries this way since their side effects are localized/
It's easier to override (polymorphism) the core app class if it's injected than if it's static.
I could go on, but I think it's pretty easy to find resources on why statics are generally bad. So that's the approach I would use to migrate away from a static class to an instance...
If you don't want to have static functions but global access from everywhere WITHOUT passing the object to the places where it is actually needed then you pretty much can only use one thing:
A global variable
So you are not really better of doing that. But that is the only thing i can think of that would fulfill your requirements.
If you App object is something like an application config a first possible step would be to pass it to the objects that need it:
class Login {
public function __construct() {
$this->_login_seed = App::get('LOGIN_SEED');
self::$_ms = Database::getConnection();
}
changes into:
class Login {
public function __construct(App $app) {
$this->_login_seed = $app->get('LOGIN_SEED');
self::$_ms = Database::getConnection();
}
I recently took my Db initiating code out of the __construct of my Page class and placed it just after I initiate the Page class. I removed it from within the Page class because I want to be able to access it from anywhere (other classes for example). It also takes server, username, password and database arguments to it when initiated, and I don't wish to enter these every time.
Is there a way I can access it from under the Page class now? I've tried a few methods, even global (which I have been told is an awful way to do things) and so far no avail. I am still new to OO, but I am teaching myself as best as I can.
Should I make it a static class? Will this affect the lazy connector to the Db I have setup?
Any help would be much appreciated.
Thank you
[EDIT]
Similar Question: Global or Singleton for database connection?
A global of some sort (Be that global variables, singleton or some other variant) is an improvement over your previous approach, and as such you're on the right track. Generally speaking though, you should try to minimise the scope of program state (For a number of reasons, which I won't get into here). Having a global variable is in conflict with this principle. There are different solutions to this problem, but the most powerful and often overlooked approach, is to use inversion of control; Instead of obtaining a dependency, your class should receive it. For example, let's say you currently have this
class EditUserController {
function saveUser() {
$db = Database::GetInstance();
$db->execute("update users set ...", ...);
}
}
You could change this into:
class EditUserController {
function saveUser($db) {
$db->execute("update users set ...", ...);
}
}
Passing dependencies on the function-parameter level can be a bit unwieldy though, so a compromise could be to pass it on a per-object level:
class EditUserController {
protected $db;
function __construct($db) {
$this->db = $db;
}
function saveUser() {
$this->db->execute("update users set ...", ...);
}
}
This is a fairly common pattern in OO programming. In addition to being more practical than passing in function parameters, it has the additional benefit of separating construction (Where shared dependencies are wired up to each other), from runtime (Where they are used). This makes a lot of things simpler.
Global variables do have a use, and this would be one of them. Unless it's likely that you're going to be needing multiple database connections, (or even still), then I don't see a problem with setting up a global $db object.
An alternative way is to have a static "Factory" class which you can use to get the object. In Joomla 1.5, the way you access the DB object is like this:
$db =& JFactory::getDBO();
the getDBO function checks if the DB object has been created: if it has, return a reference to it, otherwise connect and initialise, and then return it.
This could equally apply to other "could-be-made-global" objects, like the current User object.
The singleton method was created to make sure there was only one instance of any class. But, because people use it as a way to shortcut globalizing, it becomes known as lazy and/or bad programming.