I have this User class
class User{
private $logged = false;
private $id;
public function User() {
//> Check if the user is logged in with a cookie-database and set $logged=true;
}
public function isLogged() {}
public function editPerms() {}
//> other methods
}
Well now considering I can't have more than 1 user logged (of course because we are talking for a single http request) in Where should i store the ref of my istance?
This is the case where singleton would be useful but these days everyone say singleton is evil (like static methods).
- http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/
- http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/
I could do a $GLOBALS['currentUser'] = new User();
and having it accesible everywhere but I think this is worse than a singleton.
So what Can I do?
Please note I don't need to save this instance between requests. I just need a way to access this instance in my framework within the same request.
If you want to know what i do now for all of my Helper Objects is a Service Container (that's considered as well bad):
function app($class) { //> Sample
static $refs = array();
if (!isset($refs[$class]))
$refs[$class] = new $class();
return $refs[$class];
}
//> usage app('User')->methods();
(IE what symfony does)
Not sure why all the arguing up top. Seems like a perfectly reasonable question to me.
The key here is to use static members of the User class. Static methods are your friends, regardless of what some may say:
Singletons are not evil. Bad usages of singletons are evil. The reason people have come to dislike this pattern so much (even going to the extent of calling it an anti-pattern, whatever that is), is due to improper use:
Too many inexperienced people make a class a singleton when they find they don't need more than one instance of a class. But the question isn't if you need only a single instance of the class, but whether more than one instance would break your code. So ask yourself this question: would your code break if there were more User instances? If not, then maybe you shouldn't bother. :)
There are legitimate uses of singletons. There are those people who fear this pattern like the plague and consider it always to be bad, without realizing that sometimes it can be very helpful. In the words of a much more experinced programmer than me, "singletons are like morphine: they can give you a real boost, but use them the wrong way and they an become a problem themselves". If you want me to go into some details as to when singletons could be a good choice, leave a comment to this answer. :)
It is always hard to answer architectural questions without the context. In this case it is pretty important how the User objects are persisted (where do they come from?) and how is the client code organized. I will assume a MVC architecture because it's trendy this days. Also I suppose your user objects will have more responsibility as only authentication (you mention some permission control here, but it's still not clear enough).
I would push the authentication responsibility to a service and just pass it around as needed. Here is some sample code.
So the answer to your question is: you need proper dependency injection through out your whole application. The only object you get from the server is a request. The dependency injection container injects it into the AuthenticationService and the latter gets injected into your controller. No singletons, no static methods, no global variables. The dependencies are tracked in the DI container and are injected as needed. Also the DI container makes sure your service is instantiated only once.
The article "Container-Managed Application Design, Prelude: Where does the Container Belong?" may clarify some DI concepts.
i think you should consider a singleton factory pattern, where a singleton factory (Auth) provides a login() method which returns a User class, as well as methods for saving state between HTTP requests on that User.
This will have the benefits of separating the security and session functionality from the User functionality. Additionally using the factory, you can have multiple types of users without the rest of the system needing to understand which object to request before the db is examined
the user class itself become a data structure, simply enforcing security and business rules, and maybe formatting some data for output purposes.
all db, state and security concerns are centralized in the auth. the only way to create a user object (legally) is to run auth->login().
you are still allowed to do
but there is no way for a new coder to save this in the db since it's not referenced in $auth->user;
you can then create a function in auth to consume user objects to create new users (on signup)
you just need to make sure you run the save functions at the end of execution... possibly in a destructor()
simple
Patterns are supposed to be a helpful guide, like a library of previously successful software abstractions. Too often these days people view patterns as being some kind of religion where things are either "right" or "wrong" regardless of the context of the program.
Think about what you want to achieve and map in out in a way that makes sense to you. Fuggering about with minute distinctions between this pattern and that pattern misses the point, and it won't get your program written. Learn by doing!
HTH.
The influence of Misko Hevery is pretty strong on me. So is his newable - injectable distinction. A user is not an injectable but a newable. What are the responsibilities of a user: should he be able to tell of himself whether he is logged in or not? There's a post of him where he talks about a similar problem: a credit card and charging it(self?). It happens to be a post about singletons, what you would like to make it:
http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/
That would leave it to an service to check whether the user is logged in or not, what rights he has on the site.
It also means your architecture would change, your problem will become different (passing around the user?, where is it needed?, how will you have access to the 'checking user is logged in' service, ...).