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.
Related
I have three classes:
Database
User
Page
Each does as it implies.
So, both User and Page need to connect to the database to get their data (User info, page content) - They both get access to the database via dependency injection from Database, and that's perfectly fine. However, Page also needs to display data gotten by User.
This becomes problematic because if I were inject User into Page, wouldn't that technically prompt Page to have TWO instances of the database, both it's own and the one exists in User? That's not good practice, correct?
From I can gather, 'extending' is not the proper solution in this case either. Page is not directly related to User, so they needn't become one.
So, I have two questions:
Would it be 'proper' to channel the database object injected into User to Page by injecting User into Page?
And, if not, what's the proper way to do this?
I think you have some misapprehension about how references work. Let us say I create a database object:
class Database {
function talkToDb(){
//does some database stuff
}
}
Now, lets say I want to have a User class that accesses a Database object - presumably for reading and writing to a persistence layer:
class User {
persistence = null; //This 'points' to a database object that is injected on object instantiation.
function __construct(Database $db) {
$this->persistence = db;
}
}
To create a User you would do the following, somewhere in code:
$db = new Database();
$aUser = new User($db);
$bUser = new User($db);
At this juncture $aUser and $bUser are using the same Database object. Lets talk about Page: it's instantiation is eerily similar to User:
class Page {
$persistence = null;
$someUser = null;
function __construct(Database $db, User $user) {
$this->persistence = $db;
$this->someUser = $user;
}
}
You'd then create - again, somewhere else in code - the Page class. Like so:
$db = new Database();
$aUser = new User($db);
$aPage = new Page($db, $aUser);
At this juncture, the object fields in both $aUser and $aPage are pointing to the same Database object: the one named $db (at least, it is named that in the scope in which we create it above). $aPage also holds a reference to $aUser. Because it has two references - one to the user and one to the database - it could in theory access that same database objects two ways:
class Page {
... //construct everything as above
function accessExample() {
//The following two lines call the exact same function on the exact same object
$this->persistance->talkToDb();
$this->someUser->persistance->talkToDb();
}
}
Remember: when you 'inject' an object into two other objects, you're not copying that object, you're simply copying the reference to the object. It is appropriate for many objects to hold copies of the same reference.
The fact that you can get to the database either way doesn't mean you should. You can mask the User's database from Page by making it private. That said, not doing so is only a matter of code cleanliness: you still can.
Note that it is entirely inappropriate to 'extend' either Page or User from one another or from the Database. An extended class is really a 'subset' or a 'more specific form' of the previous. A User is not a special form of a Database: a MySqlDatabase is. Rather, we know that a User needs to have a database it can 'persist' it's state to. This is why we provide it with a 'reference' to some Database object. We should provide the same reference to all such objects that require access to that same Database. The fact that many objects may hold a reference to the same database object is immaterial: why would this cause a problem?
(Caveat emptor: I'm not actually running the above code, so there may be minor bugs.)
Use a constructor in the database class and set it to a class property.
Assume its available in both user and page classes as you extend our from it.
<?php
class Database
{
}
class MySql extends Database
{
private $sDbName = '';
private $sUsername = '';
private $sPassword = '';
private $sHost = '';
private $oConnection = null;
public function __construct()
{
$this->oConnection = new PDO(
'mysql:host='
. $this->sHost
. ';dbname='
. $this->sDbName,
$this->sUsername,
$this->sPassword
);
}
public function getDb()
{
return $this->oConnection;
}
}
class User extends MySql
{
}
class Page extends User
{
}
$oPage = new Page;
var_dump( $oPage->getDb() );
?>
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);
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();
I'm in the process of implementing a user authentication system for my website. I'm using an open source library that maintains user information by creating a User object and storing that object inside my php SESSION variable. Is this the best way to store and access that information?
I find it a bit of a hassle to access the user variables because I have to create an object to access them first:
$userObj = $_SESSION['userObject'];
$userObj->userId;
instead of just accessing the user id like this how I would usually store the user ID:
$_SESSION['userId'];
Is there an advantage to storing a bunch of user data as an object instead of just storing them as individual SESSION variables?
ps - The library also seems to store a handful of variables inside the user object (id, username, date joined, email, last user db query) but I really don't care to have all that information stored in my session. I only really want to keep the user id and username.
I think that logically grouping related data together under one key is best, it couples the relevant data with a common parent, it also gives you more wiggle room with regards to key names, so you could have $_SESSION['user']->id opposed to $_SESSION['user_id'], allows you to have property name context, so you don't have to provide the context in the key name as you would with a user_* key
I also think that there is a bigger concept that comes into play here, when you use user_* you are pretty much saying that anything with the key name user_* will be associated with a user. This is not a good way to organize objects IMO. However, when you use a user key and stick all of the associated data underneath it so to speak, then you have a much cleaner top level and a real nested data hierarchy as opposed to a linear one.
Why don't you create a session wrapper class that handles the accessing and storing of data? This will produce much cleaner code and abstract so changes to storing methods down the line with be pretty easy.
Here's an example of a wrapper:
abstract class Session
{
private static $_started = false;
private static $_driver;
public static function start($driver = "native", $site = "default")
{
if(self::$_started === false)
{
require_once "drivers/" . $driver . ".php";
self::$_driver = new $driver($_site);
}
}
public static function set($key,$value)
{
self::$_driver->set($key,$value);
}
public static function get($key)
{
self::$_driver->get($key);
}
public static function remove($key)
{
self::$_driver->remove($key);
}
}
The above is only simple but you should get the idea, you would also have to create the native driver file that has the set of methods required, and they should store the session data accordingly.
Example of using the Session class like so:
Session::start("native");
/*
* Generic Code
*/
Session::set("key","value (:");
When fetching your "User" Object you can just do simple like so:
Session::get("userObj")->id;
Produces much cleaner code and larger scope issues.
After time goes by you can just create a new driver for storing in the database and then just change your driver from native to database.
Note: Storing objects in the database can be a little buggy especially if your code is not organized as loading the session before the user class is on scope then you can get partial objects in the session which will lead to lack of functionality.
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.