I'm writing a user account system in PHP, with an emphasis on security, but I'm stuck on refactoring it into something cleaner and more usable.
The problem is trying to group the user account functionality together, but in separate classes. The way I'm doing it now is, there are a bunch of classes with public static methods that all take $username as the first parameter, and they use other static methods, passing the same $username as the first parameter as well. Obviously, OOP is a better way to go, especially since each method must strtolower the username in order to make DB queries, and must handle the case where the username provided doesn't exist at all.
The problem with putting everything in a "User" class is that it would be huge, and there would be a lot of completely unrelated code in the same file. For example, the Change Password code doesn't need to call methods related to validating the user's email or changing the user's (unrelated) settings.
I could have two classes: User and AuthenticatedUser, where AuthenticatedUser inherits User. User would implement all the functionality that is possible without the user being logged in, and AuthenticatedUser would be all the functionality that requires a login -- such as accessing the user's encrypted data. Maintenance code could use objects of User, and GUI code would get an AuthenticatedUser object after the user has logged in. But I don't want to cram all of the functionality into User.
Here's the list of some of operations that are related to a user account, just to show why they won't all fit in one class:
Login
Lockout user if >=X attempts in past Y minutes (includes methods for determining whether a user is locked out, adding a tick to the attempt count, etc).
Bypass the lockout with an email loop
Change password
Administrator change password (force)
Password reset (email loop) (incl. methods for initiating the reset, validating the email token, etc)
Set/get user data stored in plaintext
Set/get encrypted user data
Set/get account settings (password reset is allowed?, lock out the account on password failures?, is bypassing lockout with email loop allowed?, etc) Ideally these settings should be set/got near the code whose behavior depends on them.
Get the user's username in the proper case (as they specified it when creating account)
Email validation
A lot of functionality only used by specific code. E.g. get user "salt" used for key derivation.
A bunch more...
I was thinking I could do something like have a PasswordChanger class that inherits User, and implements the password changing functionality. So it would look like:
$proper = $user->getProperName();
...
$passChanger = $user->getPasswordChanger();
$result = $passChanger->changePassword($oldPass, $newPass);
...
$userLockout = $user->getUserLockout();
$result = $userLockout->isUserLockedOut();
...
$userEV = $user->getEmailValidation();
$result = $userEV->tryValidateEmail($token);
...
That's the best solution I've come up with so far. It lets me split up related functionality into it's own file, and saves from having to pass around the username. But it seems really weird -- I've never seen code like that before. It forces the superclass to know about all of its subclasses, which is bad design. Any ideas?
Edit: An alternative avoiding inheritance would be to have a PasswordChanger that has a a User. Like:
$passChanger = new PasswordChanger($user);
$passChanger->changePassword($old, $new); // uses $user->getUsername()
...
$emailValidator = new EmailValdiator($user);
$emailValidator->tryValidate($token);
"PasswordChanger has a User" vs. "PasswordChanger is a User." The former actually makes sense, so I like this way a little better.
Well, you've made a good start and you're asking the right questions. There is no single answer though. Design is an art rather than a science. It does sound like you are trying to re-design rather than refactor. You might find it easier if you start to refactor your code with only a loose idea of where your design might end up (a really good resource for refactoring is this book by Michael Feathers. As you refactor more and more you should find that a design emerges from the darkness! And often that design is not what you thought you'd end up with when you started out.
Oh, and btw inheritance is one of the most over-used aspects of OO. If you are thinking of inheritance, stop and think again. If you still think you need inheritance, stop and think some more. If you still think you need inheritance, it's possible that you do...
PasswordChanger and User have nothing in common so you should avoid to inherit them.
What I do in this case is something like:
class User {
var $pswChanger; //> -> Object of PasswordChanger
}
Basically put all the objects that you will need with User class within its attribute
You can of course access them with something like $this->pswChanger->method();
You might try using a decorator pattern. You can extend the UserDecorator by adding a validateDecorator, a notifyDecorator etc.
class User {
private $name;
private $password;
public function __construct($name, $pw) {
$this->name = $name ;
$this->password = $pw;
}
public function getUsername() {
return $this->name;
}
public function getPassword() {
return $this->password;
}
}
class UserDecorator {
protected $user;
protected $name;
protected $password;
public function __construct(User $user) {
$this->user= $user;
$this->setValues();
}
public function setValues() {
$this->name = $this->user->getUsername();
$this->password = $this->user->getPassword();
}
public function getUsername() {
return $this->name;
}
public function getPassword() {
return $this->password;
}
}
class PasswordChangeDecorator extends UserDecorator {
private $userdecorator;
public function __construct(UserDecorator $user) {
$this->userdecorator = $user;
}
public function changePassWord($newPw) {
$this->userdecorator->password = $newPw;
}
}
$user = new User("some random user name", "oldpw");
$userdecorator = new UserDecorator($user);
$pwdecorator = new PasswordChangeDecorator($userdecorator);
print $userdecorator->getPassword() . "\n";
// set the new password
$pwdecorator->changePassWord("newpw");
// test the outcome
print $userdecorator->getPassword();
Related
I'd like to know if it is worthwhile to split a large class into different sections, and only use them when required. If so, what would be the best option?
I'm not sure about the resource impact of using a large class in a PHP file that makes very limited use of it when caching is enabled. Maybe this just isn't worth it, no matter how big the class is?
I had two models in mind... this one would need to be able to affect the "parent's" variables. I am fully aware that this code would not work, I just figured I'd type it in PHP+pseudo to explain it rather than pseudo-code alone.
class User {
public $id;
public $password;
public $password_hash;
public $post_count;
public function __construct($id) {
$this->id = $id;
}
}
subclass Password of User {
public function set($password) {
parent::$password = $password; }
public function generateHash() {
parent::$password_hash = hash("sha256", parent::$password, true); }
public function validate($password) {
return (hash("sha256", $password, true) === parent::$password_hash); }
}
subclass Post of User {
public function count() {
$db = ConnectionFactory::getFactory()->getConnection();
$sql = $db->prepare("SELECT 1 FROM `Forum_Posts` WHERE `User_ID`=:user");
$sql->execute(array('user' => parent::$id));
parent::$post_count = $sql->rowCount();
return parent::$post_count;
}
}
$some_bloke = new User(3);
$bob = new User(4);
$bob->Password->set('idonthaveaverygoodpassword');
$bob->Password->generateHash();
$lucy = new User(5);
echo $lucy->Post->count();
I also thought about using multiple traits, but it's not a pretty solution and I'm worried that it would increase memory usage pointlessly:
class User {
use BaseUser;
}
class User_Password {
use BaseUser;
use Password;
}
class User_Post {
use BaseUser;
use Post;
}
trait BaseUser {
public $id;
public $password;
public $password_hash;
public $post_count;
public function __construct($id) {
$this->id = $id;
}
}
trait Password {
//stuff
}
trait Post {
//stuff
}
$some_bloke = new User(3);
$bob = new User_Password(4);
$bob->setPassword('idonthaveaverygoodpassword');
$bob->generatePasswordHash();
$lucy = new User(5);
echo $lucy->countPosts();
I'm also very open to suggestions as to what to rename this post. I just wasn't quite sure.
A general framework, which could help with OOP independent of domain, is called SOLID. It can generically be applied to any domain by trying to comply with each principle it defines. That being said, SOLID is a just a framework, and code could fulfill solid principles while still bein difficult to work with :( I will try to answer with a pretty generic application of it to your domain:
Single Responsibility
Does each class only have a single reason to change. If the hashing algorithm or salt changed would that be isolated to a single spot? Yes. What if requirements changed and organization doesn't want to keep track of the raw password in memory? No. Both the User and the Password need to change.
Open/Closed
How would we create new types of Posts, Users, or passwords? What if the business would like to add an unauthenticated User, who wouldn't need a password. Could that be modeled in the system without modifying the User model? What if unauthenticated users always had 0 posts?
Liskov Substitution
This states, that all objects should be replaceable with instances of their subtypes. This is often only validated once the code that consumes your domain objects is created. In this case:
$some_bloke = new User(3);
$bob = new User(4);
$bob->Password->set('idonthaveaverygoodpassword');
$bob->Password->generateHash();
$lucy = new User(5);
echo $lucy->Post->count();
If $bob were an instance of a Post it would not be able to be instantiated using a Password which is a subclass of User, same goes for $lucy being an instance of a Password instead of a User.
Interface segregation
Dependency Inversion
In general I feel like Memory usage should be a non-issue for higher level (non-embedded) applications. Even for mobile, I find, abstractions and maintainability are more important than minimizing memory usage.
Additionally, inheritance based hierarchies can easily grow unwieldy and brittle. Composition (more like your second suggestion) may help to fulfill SOLID and minimize the brittleness. User could be provided a password strategy, which would define an interface for generating passwords, and allow password generation implementation to change, and leave user unaffected. Users that wanted to generate a password could delegate to their specific strategy.
I've recently learnt OOP in Java and I'm trying to implement what I've learned in my PHP usersystem.
This is my current User class
class User {
public $id;
public $session;
public $email;
public $lastVisit;
public function __construct($id) {
$conn = new Conn();
$array = $conn->ExecuteCmdArray("SELECT * FROM user WHERE id = '".$id."'");
$this->session = $array['session'];
$this->email = $array['email'];
$this->id = $array['id'];
$this->lastVisit = $array['last_visit'];
}
}
^ Oh, and am I supposed to execute a SQL to retrieve the data?
However, after taking a look at some examples online, I looked at their User class, but realised that in it, only functions like login etc. are present. The attributes in the class are also only the username and password.
Shouldn't it contain all the other variables e.g gender, real_name as well? It seems to me that the user class does not store the userdata, but it's only used for logging in and stuff.
Is this how a User class in PHP usually works - which means my User class is done wrongly?
Well there is no right way to design a User class. It depends on the programmers likes, needs, skills and his understanding of OOP and Software-Design.
Seperation of Concerns
Oh, and am I supposed to execute a SQL to retrieve the data?
It's not recommended. Good software design is about creating decoupled self-contained components.
When designing Software it's a good practice to think out of the box and put yourself into another developers shoes. So if you had a User object without knowing what's going behind the scenes. Would you expect it to fire Database-Queries? No it's not a Users responsibility to query the database.
OOP is also about reusability. If we take your example: If I threw your User class into my own project. It would likely break. You are instantiating a Database Connection directly in the class. What if I had other ways of handling DB access? Everything would break.
Seperation of Concerns is the keyword here. And you should adhere to it whenever it's possible.
However, after taking a look at some examples online, I looked at their User class, but realised that in it, only functions like login etc. are present.
Ask yourself these questions:
Should a User know how to log in? How his sessions are managed or is this the task of another service maybe?
Would you expect that a User can log itself in if you didn't know the class?
You see. By just thinking about the concerns of a class you can eliminate problems in design before they even occur.
I would not expect my User that he knows how to log in. I rather would expect a service of my application which is explicitly dedicated to that task, to handle it.
<?php
$authentication = new UserAuthenticationService();
$authentication->attemptLogin('username', 'password');
if( $authentication->check() )
{
$user = $authentication->getUser();
echo $user->username();
}
Build your applications so that your classes are as self-contained as possible and only have the dependencies they really need. It's one of the keys of successful software-design.
It's all about your domain
The attributes in the class are also only the username and password.
Shouldn't it contain all the other variables e.g gender, real_name as well? It seems to me that the user class does not store the userdata, but it's only used for logging in and stuff.
That depends on your use case. If you're creating an application where users can anonymously post stories of their last hangover then why would you need a real name there?
If you however are creating some enterprise-business-app-thingy you probably want to store that data. What data your objects hold is defined by you, and only by you.
Let's take our hangover-site a step further. I want only minimal information from my users so it could look like this.
<?php
class User {
private $username;
private $email;
private $gender;
public function __construct( $username )
{
$this->username = $username;
}
public function username()
{
return $this->username;
}
//[...]
public function setUsername( $username )
{
$this->username = $username;
}
//[...]
}
I don't even store the password on my User class because I don't want to. I decide that I will have some kind of persistance manager and an authentication manager which handle those.
<?php
$authentication = new UserAuthenticationService();
$persistanceMapper = new UserPersistanceMapper();
$authentication->attemptLogin('username', 'password');
if( $authentication->check() )
{
// Let's rename the User just for fun
$user = $authentication->getUser();
$user ->setName('Thomas');
$persistanceMapper->persist( $user );
}
It really depends on your likes. But what I wanted to show in the first place is, that I have objects that do only the thing(s) that they are responsible for, or supposed to do
The User is able to alter it's own state and is able to provide me with it's data. But he does not know anything about the database.
UserPersistanceMapper knows how to persist a User (in a database or wherever it's supposed to)
UserAuthenticationService knows how a users session needs to be handled
I don't have the one object that handles all of it. Instead we have self-contained objects here that are together handling our stuff.
It all comes down to your likes
Is this how a User class in PHP usually works - which means my User class is done wrongly?
Eventually there is no right and no wrong. If you want to roll that way, do it! You may gain an initial time boost if you are not thinking about design. That may be okay for simple projects that are not hard to maintain.
At the time you are building larger applications however, it's always good to reflect on your own code.
Keep in mind:
Try to put yourself into some other developers shoes while designing your application
Visualize how objects are connected to each other before you start coding
Try to keep your classes as self-contained as possible (pass eventual dependencies from the outside)
Get stuff done! If you can't come up with a clean way to do something: Just make it work. Refactor things later. But make sure you have a periodic refactoring cycle. Don't put stuff aside and think "Yeah... whatever I'll do this later". You will end up with a lot of Code Smells this way.
Further reading
Seperation of Concerns
PHP: The Right Way
SOLID Principles
The drawback with the way you made your user class is when you manage lists of users, or process things about lots of users at a time you have to move around all this extra user data, whereas sometimes you would only need it's id, and email.
We can use two or more classes! In your exemple there would be a minimal User class, with very minimal attributes and functions, and a Full_User class, extending the first one.
class User {
public $id;
public $email;
public function __construct($array) {
$this->email = $array['email'];
$this->id = $array['id'];
}
}
class Full_User {
public $session;
public $lastVisit;
public function __construct($array) {
$this->session = $array['session'];
$this->lastVisit = $array['last_visit'];
parent::__construct($array);
}
}
I'd suggest passing the array of properties to the constructor instead of executing the statement. It will allow creating objects from some other data then a database.
$id = 12;
$conn = new Conn();
$minimal_array = $conn->ExecuteCmdArray("SELECT id,mail FROM user WHERE id = '".$id."'");
$full_array = $conn->ExecuteCmdArray("SELECT id,mail,session,lastVisit FROM user WHERE id = '".$id."'");
$user_for_listing = new User($minimal_array);
$full_user = new Full_User($full_array);
When a user logs into my site, I create an instance of my User class, fetch some user-related data and store the object in the SESSION.
Some of the data I fetch from the database should be constant throughout the session AND I want the data to be accessible from other objects. I prefer using User::$static_value_in_class to $_SESSION['static_value_in_session'] when using the value from within another object, but I'm open to persuasion.
The problem is, the values aren't remembered when I serialize my User instance into the SESSION, then load a different page.
Class definitions:
class User {
public $name;
public static $allowed_actions;
public function __construct($username, $password) {
// Validate credentials, etc.
self::$allowed_actions = get_allowed_actions_for_this_user($this);
}
}
class Blog {
public static function write($text) {
if (in_array(USER_MAY_WRITE_BLOG, User::$allowed_actions)) {
// Write blog entry
}
}
}
login.php:
$user = new User($_POST['username'], $_POST['password']);
if (successful_login($user)) {
$_SESSION['user'] = $user;
header('Location: index.php');
}
index.php:
if (!isset($_SESSION['user'])) {
header('Location: login.php');
}
Blog::write("I'm in index.php! Hooray!")
// Won't work, because Blog requires User::$allowed_actions
Should I implement Serializable and write my own version of serialize() and unserialize() to include the static data?
Should I bite my lip and access the $_SESSION variable from within the Blog class?
Should I require a valid User instance sent to the Blog write() method?
Or maybe the internets has a better idea...
EDIT: Writing my real use case (not full code, but enough to get the gist).
My site handles groups of users with shared budget accounts.
Users may spend group money on certain things the group agreed upon, and they report transactions by creating instances of the Transaction class and sending it to the Bank class for database storage.
Bank class:
class Bank {
// Group-agreed reasons to spend money
public static $valid_transaction_reasons;
public function __construct(User $user) {
Bank::$valid_transaction_reasons = load_reasons_for_this_group($user->bank_id);
}
}
User class:
class User {
public $bank_id;
public function __construct($username, $password) {
$query = "SELECT bank_id FROM users WHERE username=$username AND password=$password";
$result = mysql_fetch_array(mysql_query($query));
$this->bank_id = $result['bank_id'];
}
}
Transaction class:
class Transaction {
public function __construct($reason, $amount) {
if (!in_array($reason, Bank::$valid_transaction_reasons)) {
// Error! Users can't spend money on this, the group doesn't cover it
}
else {
// Build a Transaction object
}
}
}
Actual code (login.php, or something):
$user = new User($_GET['uname'], $_GET['pword']);
$_SESSION['bank'] = new Bank($user);
// Some shit happens, user navigates to submit_transaction.php
$trans = new Transaction(REASON_BEER, 5.65);
// Error! Bank::$valid_transaction_reasons is empty!
As I mentioned in the comment, this is more a software design question than a question how to achieve this with PHP.
A static property is not part of the state of an object and will therefore not being serialized with it.
I'll give you a short example how I would solve a related problem. Imagine you have the following message class, that has a static $id property to make sure all instances have a unique id:
class Message {
public static $id;
public $instanceId;
public $text;
/**
*
*/
public function __construct($text) {
// the id will incremented in a static var
if(!self::$id) {
self::$id = 1;
} else {
self::$id++;
}
// make a copy at current state
$this->instanceId = self::$id;
$this->text = $text;
}
}
Serialization / Unserialization code:
$m1 = new Message('foo');
printf('created message id: %s text: %s%s',
$m1->instanceId, $m1->text, PHP_EOL);
$m2 = new Message('bar');
printf('created message id: %s text: %s%s',
$m2->instanceId, $m2->text, PHP_EOL);
$messages = array($m1, $m2);
$ser1 = serialize($m1);
$ser2 = serialize($m2);
$m1 = unserialize($ser1);
printf('unserialized message id: %s text: %s%s',
$m1->instanceId, $m1->text, PHP_EOL);
$m2 = unserialize($ser2);
printf('unserialized message id: %s text: %s%s',
$m2->instanceId, $m2->text, PHP_EOL);
To make sure that the id is unique across multiple script runs further work is nessary. You'll have to make sure that Message::$id is initialized before any object creation, using the value from last script run. This will get additionally wired when it comes to parallel PHP request on a webserver.
Its just an example with the simplest static property I know: an instance counter. In this case I would do so. But I hope you see that there is further work required to serialize / unserialize static properties without have side effects. And this depends on your application needs.
This question cannot be answered general I tend to say it makes no sense in any case to serialize static members. But I would appreciate comments on this.
Some of the data I fetch from the database should be constant throughout the session AND I want the data to be accessible from other objects.
If the data is really constant, then make them a constant.
If the data is not constant, consider whether they belong to the individual users (the object instances) or the User as the general concept (which is what a class is).
Should I implement Serializable and write my own version of serialize() and unserialize() to include the static data?
It does not make sense to store static members in the serialized object's string because they are independent from each other. Storing them would be a snapshot of the class state at the time the object was serialized.
Consider the following code snippet:
$user = new User;
$user::$allowed_actions = 'foo';
$string = serialize($user);
unset($user);
Now imagine some other part of your code does this:
echo User::$allowed_actions;
It still gives "foo" despite no object being in memory at the moment. That is because it's a static member. It's class state.
Now imagine you do this:
User::$allowed_actions = 'bar';
If you do unserialize the object now what should $allowed_actions be? Foo or Bar?
$user = unserialize($string);
echo $user::$allowed_actions;
The output should and would be "bar", because static members are about the class. The fact that we created, destroyed and brought back an object from it is irrelevant. It's all state of the class we changed here.
Also, take into account that statics are death to testability and you want to avoid them when possible. After all, it's called OOP not Class-Oriented-Progamming.
Should I bite my lip and access the $_SESSION variable from within the Blog class?
No, you should not access any of the superglobals anywhere but write abstractions for each of them or rather for the data inside them. They are merely input sources. In case of $_SESSION what you want to do is get all the data you need for that particular request right in your bootstrap and then pass the data around instead, e.g. recreate the user and pass that around.
Should I require a valid User instance sent to the Blog write() method?
In general, methods should be on the objects with the most information to fulfill an action. Whether that applies to your Blog::write I do not know. If the allowed_actions are part of the User instance, then probably yes, you should likely require a valid User instance.
Or maybe the internets has a better idea...
Another option would be to put the permissions into a dedicated Permissions object, holding the user role and it's permission. You could then lookup the permission from that list by passing in a User object. Search for Access Control Lists (ACL) for more info on possible implementations.
EDIT: Writing my real use case (not full code, but enough to get the gist).
If your concern is simply that Bank::$valid_transaction_reasons could be empty, then don't store Bank in the Session at all but only load it from the user when you run the transaction, e.g. create the Bank instance in submit_transaction.php (create it when you need it). That way you will never run into an error.
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)
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.
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.
class AuthenticationService {
/**
* #var User
*/
private $currentUser;
public function __construct(Request $request) {
// check if the request has an user identity
// create a user object or do nothing otherwise
}
public function getCurrentUser() {
return $this->currentUser;
}
}
class User {
public function editPerms(){}
}
// the client code
class Controller {
private $auth;
public function __construct(AuthenticationService $auth) {
$this->auth = $auth;
}
public function handleRequest() {
$currentUser = $this->auth->getCurrentUser();
if ($currentUser === null) { // of course you could use Null Object Pattern
// no user is logged in
}
// do something with the user object
}
}
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.
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:
class User
{
private $logged = false;
private $id;
private static $_currentUser;
public static function currentUser()
{
if (empty(self::$_currentUser))
{
#session_start();
if (array_key_exists('current_user', $_SESSION))
{
self::$_currentUser = $_SESSION['current_user'];
}
else
{
// force login in or whatever else.
// if you log in, make sure to call User::_setCurrentUser();
return null; //or some special 'empty' user.
}
}
return self::$_currentUser;
}
// you may consider making this public, but it is private because it is a bit
// more secure that way.
private static function _setCurrentUser(User $user)
{
self::$_currentUser = $user;
$_SESSION['current_user'] = $user;
}
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
}
// Usage
$pUser = User::currentUser();
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, ...).
since everyone else is weighing in on this, singletons are not evil.
i even read the "liars" article, and he's using a contrived example of non-modular design and poor dependency inheritance.
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
class auth {
private static $auth = null;
private $user = null;
// must use getAuth();
private __construct(){};
public getAuth() {
if (is_null($this->auth) {
$this->auth = new auth();
}
return $this->auth;
}
public function login($user,$pass) {
... // check db for user,
if ($dbrow->user_type == 'admin') {
$this->user = new admin_user($dbrow);
} else {
$this->user = new normal_user($dbrow);
}
$this->user->setSession($db->getsession());
}
public function getUser() {
return $this->user;
}
public function saveSession() {
// store $this->user session in db
}
public function saveUser() {
// store $this->user changes in db
}
...
}
the user class itself become a data structure, simply enforcing security and business rules, and maybe formatting some data for output purposes.
class normal_user extends user {
... getters and setters
public function getName() {}
public function setEmail() {}
public function setprofile() {}
}
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
$me = new normal_user();
$me->setName();
echo $me->getName();
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)
...
public function create(user $user) {
// validate $user
$this->user = $user;
$this->saveUser();
}
...
you just need to make sure you run the save functions at the end of execution...
possibly in a destructor()
simple
I have a session class that basicly just sets and retrieves session variables,
the reason I made it was so I could easily change it to use sessions or something
like memcache to set the items and have them accessible on multiple pages without hitting the database
I then have this user class which uses the session object to get session variables in it.
I am wanting to add to this user class though, to make it more encapsulated I would like to be able to set the variables that I am retrieving in this class
so right now I can display the userid with $user->userid; I would like to first have a method or something that sets its value from the session object I guess
Does this sound lke a good idea or possibly a lot of overhead?
And if what I am trying to do is a good idea maybe you could suggest/show example of how I should do it? I am thinking that if I add that method in that possibly I should move the code in the __construct method into it's own method
Basicly, I have the variables listed in the top part of the class that are used in the construct method, if I have multiple methods in the class though would I need to set them all at the top like that?
<?PHP
//user.class.php file
class User
{
public $userid;
public $name;
public $pic_url;
public $gender;
public $user_role;
public $location_lat;
public $location_long;
public $newuser;
function __construct()
{
global $session;
if($session->get('auto_id') != ''){
//set user vars on every page load
$this->userid = $session->get('auto_id'); //user id number
$this->name = $session->get('disp_name');
$this->pic_url = $session->get('pic_url');
$this->gender = $session->get('gender');
$this->user_role = $session->get('user_role');
$this->location_lat = $session->get('lat');
$this->location_long = $session->get('long');
$this->newuser = $session->get('newregister');
}else{
return false;
}
}
}
//with the class above I can easily show some user variables I have saved into a session like this below
$user = new user();
$user->userid;
?>
In general your idea is a good one
3 things I would do differently:
1) In your implementation doesn't seem to consider having several users. ie Several instances of the same class.
2) I would use factories instead of using IF in the constructor.
So for a user you have saved in the session you would call:
$savedUser = User::fromSession($userId);
for a new user
$user = new User()
3) Use the serialize and unserialze functions to save that data to the session
Then your class could could be implemented as
public static function fromSession($userId) {
return unserialize($session->get('users_'.$userId));
}
public function save() {
return $session->set('users_'.$this->id , serialize($this));
}
I guess this is vaguely an answer to the "is this a good idea" question. In my understanding, locating variables in the session versus refreshing them from the database is a question of the trade off between complex queries and deserializing data. The session data isn't a free magic cache that escapes database calls, it is just a convenient wrapper around a database call that you don't have to deal with. Any variable that you place in the session must be serializable. The whole collection of serialized data is then managed; the server fetches the data using the session key, deserializes it all, and hands it to the php script. Then when it closes the session for that request-response cycle it serializes it all and puts it back in the db.
So the mess in dealing with all that can, in some cases, be worse than the mess of just opening a connection and asking the db for the same stuff (or a subset of stuff) directly.
I would say that putting one or two key values in the session is a good stopping place, and relying on it too heavily for statefulness is a less-optimal plan.
I would set a new session with a name like "ValuesInSession" to true or false depending on whether or not you have session values for the fields in your user class. Then, in the sessions\users class you can check whether this session is true or false and set your values accordingly (IE from the existing sessions or to empty strings\0)
EDIT: You could, alternatively to putting that code in the user or sessions class, write a new class which could work with your users class to set the values properly (perhaps it could extend the sessions class?)
I'm not sure I understand the question, however, if you are using php 5, you can use the __set magic method to help with this.
Modifying your current class:
class User
{
private $id;
private $data = array();
public function __construct()
{
global $session;
$this->id = $session->get('auto_id');
$this->data = array(
'disp_name'=>$session->get('disp_name'),
'pic_url'=>$session->get('pic_url'),
'gender'=>$session->get('gender'),
'user_role'=>$session->get('user_role'),
'lat'=>$session->get('lat'),
'long'=>$session->get('long'),
'newregister'=>$session->get('newregister')
);
}
// return the user id
public function id()
{
return $this->id;
}
// the __get magic method is called when trying to retrieve a value of a
// property that has not been defined.
public function __get($name)
{
if(array_key_exists($name, $this->data))
{
return $this->data[$name];
}
return null;
}
// the __set magic method is called when trying to store a value in a property
// that has not been defined.
public function __set($name, $value)
{
global $session;
// check if the key exists in the 'data' array.
// if so, set the value in the array as well as the session
if(array_key_exists($name, $this->data))
{
$this->data[$name] = $value;
$session->set($name, $value);
}
}
}
This way you can still get and set values the same as you were, but will also store the set the value in your session class.
To test this:
$user = new User;
if($user->id())
{
echo $user->disp_name;
$user->disp_name = 'new name';
echo $session->get('disp_name');
}
I would not suggest you that because:
It is not a good practice to select an architecture "in case of future need" ('the reason I made it was so I could easily change'). Check http://www.startuplessonslearned.com (Eric Ries) or http://highscalability.com articles
Your code is hard/impossible to test (See Misko Hevery's blog (A google evangelist) http://misko.hevery.com for further information).
You are using "global" (never a good idea if you want to keep track of the dependencies).
It is better to seperate "the business logic" (a User class) and the wiring/building (a factory class for example). (See http://en.wikipedia.org/wiki/Single_responsibility_principle and "separation of concerns")
For really good code examples (and to understand which OO laws should not be broken), I can advice you Misko's blog (Also do not miss his technical talks at google that you can find on youtube). I am sure you will love them.
Hope this helps.