The Gamer Corner
Whoring out stats at an average rate of 3 per day.
Lost your password?

Web Login OOP Philosophy

Web Login OOP Philosophy – January 24, 2011 1:32 PM (edited 1/24/11 8:32 AM)
Talraen (2373 posts) Doesn't Play with Others
Rating: Not Rated
Recently I've been coding new bits of my basic web engine, trying out new techniques, making things cleaner, and so on. I've come across one such part that I'm not really sure how to go about implementing in an object-oriented fashion: the login.

For a website, there are several components to a login, and several outputs. Here's a basic outline of what it needs to do, and what information I need from it:

• First, it has to check if a login form was filled out. Typically I'll use unique $_POST fields for this, so it's easy to identify whether there was a login attempt. If there was, it needs to verify the username and password, and determine which user it is.
• Alternately, the user could have clicked the logout button, which is handled in a similar way.
• If the login form wasn't filled out, the code checks to see if there is currently a user logged in (stored in session variables), and verify that the session is legit. If it isn't, it has to log the user out.
• If at this point no user is logged in, the system checks the user's cookies to see if there is automated login information, and verify it. It also removes the cookie that was used (no login cookie is used twice).
• Regardless of the login method, if a user is logged in, it checks whether the user wants to store their information in a cookie, and if so, assigns one. In the future I'll probably add the usual "remember me" checkbox to do this on the fly.
• If at this point the user has logged out, the system clears any failed cookie and reports that there's no one logged in.

There's a bit more to it than that, such as handling pending users, but that's the basic structure of what I need to do.

So originally, this was basically functional: in the Page constructor that sets up general variables, it would try calling the login(), logout(), and cookie_login() functions, figure out what was going on, and do it. In my first attempt at a more OO approach, I was going to create a User object (which I still plan to), then extend that into an ActiveUser object and handle the logins in the constructor. And that may actually be a good idea, depending on how effective object inheritance is in PHP, but I'm still not sure it's really handling logins correctly (since they're still functional).

The new idea I had, which prompted this post, is of a Login object that handles all this stuff. The problem with that is how it should work. Should everything be in the constructor, so you just instantiate it then get the results? Should there be a number of functions? And given that there can only ever be one person logged in for a given instance of a page, at that point should it just be a static class with utility functions? I'm really not sure which way to go with this. Maybe something completely different. So what do you guys think?

--
There is no Mythril Sword in Elfheim
Followup Question – January 24, 2011 2:50 PM (edited 1/24/11 9:56 AM)
Talraen (2373 posts) Doesn't Play with Others
Rating: Not Rated
So in planning this out, I ran into a second issue that I'm not really sure the best way to solve. Well, the "best practices" way at least. I'm trying to set up an OO interface for database tables, at least the major ones, and that means a User object. Specifically, the User object in this case will be used to check logins (since that's where you find the password, cookie settings, and so on to verify everything). And that brings to mind two unrelated questions.

The first question is whether the Login object should give a user id or a User object. My initial feeling was to give a user id and then create the User object outside the Login, but since the Login has to create a User object for verification anyway, it may as well just keep it and pass it along, right? It seems a little weird doing it, since the Login object becomes a strange one-use User factory, but meh. Pretty code is nice, but practically speaking my main concern is to have as few SQL queries as possible... so I guess this is a no-brainer.

The other question is sort of similar. As I've just discovered, having never tried it before, php does not allow you to overload constructors. This presents an issue with the creation of the User object. In normal use, the User constructor would take a user id. However, for logins it has to see if the username/password (or cookie) combination exists. Optimally, this would be done with separate constructors, but as I mentioned, php doesn't allow that. So the options are basically override the parameter list (easy enough since php variables are untyped, but still ugly) or have static functions like "checkPassword(username, password)" that return a user id. Philosophically those seem like a better idea, but as I mentioned, limiting queries is paramount, and I can work the conditions into the "get user info" query and save a step.

The issue is that I'd need to handle at least three constructor parameter lists, and it's hard to differentiate them. In pseudocode-ish terms, I'd want these constructors:
function __construct(int $user_id)
function __construct(string $username, string $password)
function __construct(string $cookie) (alternately, since the cookie is a superglobal, I could use an empty parameter list)

Now I guess that works, since I can check the type and number of parameters, but ugh. Or ugly, more like. But I think that's the way to go, right?

EDIT: I should also mention that the cookie constructor could take two parameters, both strings. The actual cookie text is basically two strings with a delimeter (an identifying string and a verification string). But then it's really a pain to differentiate with the username/password version).

EDIT 2: Alternately I could add another parameter to act as a "what type of constructor is this" thing. Though in 99% of cases I'd want just the user_id one, so it's probably not worth the effort.

--
There is no Mythril Sword in Elfheim
Re: Followup Question – January 24, 2011 3:01 PM (edited 1/24/11 10:01 AM)
Debonair (259 posts) Lurker Extraordinaire
Rating: Not Rated
maybe your login object could be a static factory, returning the user object if successful or an exception if failed?

Re: Followup Question – January 24, 2011 3:06 PM (edited 1/24/11 10:06 AM)
chaoscat (452 posts) Ambassador of Good Will
Rating: Not Rated
So it might make sense to have the constructor return a blank object, i.e. one not read in from the DB yet. Then you could have methods to set the user_id, name & password and/or the cookie. Then call a load_user method that runs your one query to pull the user in. I don't really see the need for a separate login object personally.

_________________________________________________
Syllabic (4:14 PM): tozzi are you like dowd's jiminy cricket
Re: Followup Question – January 24, 2011 3:09 PM (edited 1/24/11 10:09 AM)
Talraen (2373 posts) Doesn't Play with Others
Rating: Not Rated
Bill, that's currently more or less what I'm designing. I haven't yet decided if it should be static or not, though. I think code-wise it doesn't really matter, but in a more real sense a Login is a distinct object (it even has a one-to-one ratio with entries in the "login" table, and as I mentioned I'm going for an OO database design here).

But your second point is much more interesting - what should Login do if it fails? An Exception would make sense, but here's the issue. The code is going to have a User object that represents the person currently logged in. Having every part of the code work differently based on whether the User object exists would be a nightmare - my plan instead is to have a generic User object that represents an anonymous user. They wouldn't have any posting rights or any of that, just minimal viewing stuff (based on the default site parameters). So if the Login object can give me a User object, which as I mentioned in the second post it probably ought to, then on failure it should return that same generic User object. I'd also have methods to determine if the login succeeded or failed, what type it is, etc., but the "Login" object could exist (philosophically) even on a failed login, if that makes sense.

And that brings up another point... I mentioned initially that I was planning an ActiveUser object that extends User, and putting the Login stuff there. Even if I don't (and currently I'm leaning towards not doing so), the ActiveUser object is probably a good idea because I don't need to know nearly as much about normal Users as the active one (such as posting/viewing rights). Of course, if I have the User object forego queries until the information is actually requested, then there's no real performance hit from only having User objects. I could really go either way on that one, though I imagine for general code security and clarity ActiveUser is still the way to go.

--
There is no Mythril Sword in Elfheim
Active Users: (guests only)
1 user viewing | Refresh