Feedback on a session storage class design - php

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.

Related

Why can't I access my static variables from another php page

I'm trying to create a simple class that holds all my session information so I can easily obtain it later on. I don't like having to keep using $_SESSION['foo'] to access it through all my PHP pages because I want to be better organized and have all of the session data stored in one area so I can just pull up the page if I need a quick reference to see all the variables I've stored. Also on a side note, if there is a better way I'm suppose to store SESSION data so that its organized and easily read by other programmers that would be very helpful information as well.
So far I created the following class..
class staticVars {
public static $ownerID;
public static $ownerFullName;
public static $ownerEmail;
public static function createFromArray($array) {
$ownerID = $array['ownerID'];
$ownerFullName = $array['ownerFullName'];
$ownerEmail = $array['ownerEmail'];
}
}
in my PHP page I have this...
include("./includes/core/coreGlobals.php");
staticVars::createFromArray($_SESSION);
echo ("owner id " . staticVars::$ownerID);
However, when I echo out the response, the $ownerID just comes back blank.
You're setting variables local to the function and not the class variables. Use self or maybe static:
public static function createFromArray($array) {
self::$ownerID = $array['ownerID'];
self::$ownerFullName = $array['ownerFullName'];
self::$ownerEmail = $array['ownerEmail'];
}
See: Late Static Bindings for when you would use static.

Is fetching data from database a get-method thing?

I have a small class that I call Viewer. This class is supposed to view the proper layout of each page or something like that...
I have a method called getFirstPage, when called the user of this method will get a setting value for which page is currently set as the first page. I have some code here, I think it works but I am not really shure that I have done it the right way:
class Viewer {
private $db;
private $user;
private $firstPage;
function __construct($db, $user) {
$this->db = $db;
if(isset($user)) {
$this->user = $user;
} else {
$this->user = 'default';
}
}
function getFistPage() {
$std = $db->prepare("SELECT firstPage FROM settings WHERE user = ':user'");
$std->execute(array(':user' => $user));
$result = $std->fetch();
$this->firstPage = $result['firstPage'];
return $this->firstPage;
}
}
My get method is fetching the setting from databse (so far so good?). The problem is that then I have to use this get method to set the private variable firstPage. It seems like I should have a set method to do this, but I cannot really have a set method that just fetch some setting from database, right? Because the user of this object should be able to assume that there already is a setting defined in the object...
How should I do that?
I think your approach is not bad. The most important thing is passing $db in the constructor, which you do. The user could be parameter of the constructor or the method itself, it depends on how 'permanent' the user is, for the application.
There are several minor things I would improve:
Use type hinting for PDO object. Therefore, anyone who uses your 'library' knows what kind of object should be injected.
Almost never use private visibility, use protected instead. Therefore, if someone wants to extend your class, he still has access to your properties.
Don't use isset/empty for checking $user, rather introduce a default value. Therefore, anyone who calls your method and sees the parameters knows, what's going on.
Always explicitly use public visibility. It's a good practice and you won't confuse for example Java developers, who have package as default.
If you really want to create a high quality code, check for every possible error state so you won't encounter a fatal error. PDO::fetch can return false and you should check this error state before you access the result as an array.
If you decide to save $firstPage to the object state, you should reuse it the next time the method is called. However, if you write a common web app, I don't think you really want to put it to object state. Instead, just return the result.
Then, your code would look like this:
class Viewer {
/** #var PDO $db */
protected $db;
protected $user;
public function __construct(PDO $db, $user = 'default') {
$this->db = $db;
$this->user = $user;
}
public function getFistPage() {
$std = $this->db->prepare("SELECT firstPage FROM settings WHERE user = ':user'");
$std->execute(array(':user' => $this->user));
$result = $std->fetch();
if ($result !== false) {
return $result['firstPage'];
} else {
throw new YourException('Failed to fetch first page.');
// or return false/null;
}
}
Edit: You should always fully set up the object state in the constructor and you should not do any computation in it. Also, avoid using initialize-like methods. In this case, constructor ensures we have PDO and $user parameter set up (object state). Then, you can do your computation in the method without passing additional parameters (which is good, it supports object encapsulation).
Getters should not change the state of the object. However, sometimes member variables are not part of the actual object state - rather they are used for internal caching. You should ask yourself - is firstPage part of the state? Should users of the class care whether it was set or not? Other than performance, does the object act differently based on it's value? If not, than it's OK to set it in a getter.

PHP - serialize a class with static properties

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.

PHP, passing parameters to object method or using instance variables

This is something I have never been fully sure of or never found a solid answer for.
Lets say I have a User class with a register() method inside it and I'm not sure which way is best to implement this method.
In my register.php page should I have
$user->register($_POST['firstName'], $_POST['lastName'], $_POST['username'], etc..);
and then in the register() method don't bother setting the objects attributes and just use the variables supplied in the signature of the method or should I do
$user->register();
and then in the register function do something like
$this->firstName = $_POST['firstName'];
$this->lastName = $_POST['lastName'];
etc...
Thanks in advance.
If the register method is tied to the object (the instance, not the class), the I'd have it use the internal properties which have to be set in advance. So, you instantiate a user, set the properties and then call $user->register().
$user = new User();
$user->firstName = 'name'; //$user->setFirstName('name') could also work
$user->lastName = 'last name'; // for this to work, the properties have to be public
$user->register();
User A should only be able to register itself, and not anything else.
If you use a method with parameters, you could basically register anything (not only a user).
Also, if registration means writing the parameters to a database, a method that only uses the internals of the user object is more robust. If you decide to change the registration mechanism (if you need some other info from the user object), only the user class has to be modified.
Edit:
Now that I've thought about it a bit more, I think I'd make another class to register users, it would take the entire user object and add a role or whatever and save it to the database. That way, a user object is a bit simpler, and does not need to know how it is registered or unregistered, and if the registration mechanism changes, the users can stay the same.
Edit 2:
Be careful when setting object properties from a method that is not a really a setter (like you would in the register($fname, $lname, ...)). The same approach has brought me headaches when "something" would change my object for no apparent reason, and I couldn't find a setter or a direct call to the property anywhere in code.
The implementation is purely up to you. You can do either way. Here is an example:
class User{
protected $_firstName = null;
protected $_lastName = null;
public function register( array $params = array() ){
if(!empty($params) ){
$this->setParams($params);
}
// Do more processing here...
}
public function setParams($params){
// Set each of the users attributes.
}
public function setFirstName($name = null){
if($name !== null){
$this->_firstName = $name;
return true;
}
return false;
}
public function getFirstName(){
return $this->_firstName;
}
// Same getter and setter methods for other attributes...
}
This way you can pass an array of User attributes to the $_POST or you can do it individually by calling $user->setFirstName(), $user->setLastName(), etc...
Considering $_POST is defined in the global scope, it would make more sense to use your latter approach (not passing in arguments and setting it up from the function). NOTE however, that this will only work in the case that $_POST is declared in the global scope (in this case) and you will lose flexibility in scenarios when you pass in the class from external PHP modules.

Repetitive class (method/property) invoking in PHP

The following is an excerpt from some code I wrote to assign the $user->privilege based on a method from that same class. It seems excessively repetitive, and I am wondering if there is something I can do to make it more readable -- given that I haven't seen this kind of repetition too much in codes I have looked at.
$user -> privileges = $user -> get_privileges ( $user -> username );
It doesn't look particularly repetitious to me, but it is a little unusual to be assigning an object's property based on a method outside the class. Instead, this might be better handled inside the object constructor, eliminating the need for you to remember to set the property when coding:
class User {
public $username;
public $privileges;
public function __construct() {
// setup the user however that's done...
// And assign privileges in the constructor
$this->privileges = $this->get_privileges();
}
// In get_privilegs, rather than passing the username property,
// just access it via $this->username.
// Unless you need to use this method from time to time outside the class, it can be private
private function get_privileges() {
// Get privs for $this->username
}
}
And as an alternative to $this->privileges = $this->get_privileges(); called in the constructor, you might just set $this->privileges inside the get_privileges() method. Then you can just call it as $this->get_privileges() in the constructor, no assignment necessary. Either way works.
I use this pattern a lot when a method is expensive and I can just store the result for the remainder of the request:
class User {
protected $_privileges = null;
public function getPrivileges() {
if ($this->_privileges == null) {
// code to populate privileges array
$this->_privileges = $privileges;
}
return $this->_privileges;
}
}
That way getPrivileges() will only do the hard work once and afterward it uses its own locally cached copy for the remainder of the request for that object instance.

Categories