There are different way to run PHP code. For example user initiate reloads and user initiated ajax requests.
What it the best way to maintain state between these runs?
PHP does consider it separate runs. Two things:
Don't use globals... they're bad :) Consider making your "session" class a collection of static functions with the session_id as a static member var.
Simply create a new session class in your 2nd snippet:
$obj_ses = new session();
$obj_ses->activate('email', $this->_protected['email']);
The session id will be the same across all page views for that particular user, so creating a new session() in the second snippet will still refer to the same session you started in the first snippet.
Here's what a static implementation might look like:
// class names should be camel-cased
class SessionManager
{
protected static $session_id = null;
public static function start()
{
self::$session_id = session_start();
}
// ... and so on
}
// to use
SessionManager::start();
SessionManager::activate('email', $email);
That should really be all you need. There are certainly many ways to do this, but this ought to get you started :)
Related
I'm running into a problem when passing an object across different classes and trying to have only one instance of it instead of multiple clones.
TLDR version:
If I have objects A->B->C, where C gets passed A by way of B as a parameter on creation, will C->A->B access the original B that had created it, or a copy of that B? How many copies of B are there in the system memory now?
Slightly more detailed version:
Let's say I have a (perhaps overly convoluted) nested class structure for handling a server-based request. The first step is to instantiate an object of class Session, and then within it, create an object $handler of class Handler. However, as $handler will need to have access to the internal attributes of $session (and multiple other objects created within it, like $user or $database, whose purposes ought to be self-explanatory), I then pass it on as a parameter:
class Session {
public $handler;
public function __construct() {
$this->handler = new Handler( $this );
//DO STUFF HERE
}
}
The Handler class inherits the session like this:
class Handler {
private $session;
public function __construct( Session $inherited_session ) {
$this->session = $inherited_session;
}
}
Side note: $session is set to private to avoid even the slightest chance of infinite loops down the line, of the $this->session->handler->session->handler variety.
Now, according to my understanding and all the research I've done, PHP passes objects by reference, so the $this->session object within this Handler ought to be accessing the same object in the system memory as the original session? Not a copy of itself?
Yet here's my problem. Suppose now I create a third-level nested object within the Handler class, of class Dashboard, and want to pass the original $session to it (not, mind it, just the Handler object itself). Off we go, so we put this somewhere within the Handler class:
$dashboard = new Dashboard( $this->session );
The Dashboard constructor inherits the session in exactly the same way as Handler did:
class Dashboard {
private $session;
public function __construct( Session $inherited_session ) {
$this->session = $inherited_session;
}
}
However, it doesn't seem able to access the instance of Handler that had called it, and by now it appears that we have multiple copies of $session and $handler floating about - and I'd very much like to understand why, because it contradicts everything I understand about references.
Here's an example of this pathological behaviour - say we have a variable in Handler:
public $temp_var;
that the constructor of Handler assigns a value:
$this->temp_var = '123';
and then we try accessing it from within the Dashboard class as $this->session->handler->temp_var. That returns NULL. Why? Did $dashboard inherit a copy of $session on initialisation that doesn't have an initialised ->handler to call on? How can I make it so there is only one (unique) object of each class, and updating the internal (public) variables of $handler would get correctly passed on to $dashboard->session->handler? Or am I just making some obvious / idiotic mistake somewhere and completely not seeing it?
Note #1: any variable set in $session as $this->var is correctly accessible from $dashboard->session->var, so double-level nesting works as expected; it's triple-level one that doesn't.
Note #2: I've already thought of simply passing $handler as a parameter to all of its nested objects along with $session (and if no solution exists, this is what I'll have to do), but it doesn't solve the original problem of $this->session->handler being somehow and inexplicably different from the original $handler within its nested objects.
Oh, and my thanks to anyone who managed to read through all this!
As far as I understand, you're dealing with composition not with inheritance here.
So, you have a Session which is passed to a Handler, the Handler and Dashboard both "know" the session through composition (keeping a reference on the private variable).
I don't understand why do you need this sort of circular reference, but if you want to access the Handler from Dashboard, why not passing the Handler to it?
Besides that, it looks like you are storing the handler in a local scoped variable (I've been away from PHP the last two years, but...)
class Session {
public function __construct() {
// isnt $this->handler = new Handler( $this ) ??
$handler = new Handler( $this );
}
}
From a conceptual point of view, there's not "nesting", only references, so I dont think that the "three levels" does anything to do with that.
Hope it helps!
Here's an example I've modified the Session and privacy just to verify that the $session is still the same
I have a singleton class that I am using as part of a CAPTCHA system to generate the code and image etc.
I have one script included on the html form that loads the class singleton, generates the code and image, and outputs it to the browser. I then have another script to validate and process the form. This loads the class singleton to retrieve the instance that was created previously, and calls the function to validate the code.
The problem I'm having is that when I'm validating the form the code that was generated on the form has changed or is completely absent when I come to validate it!
I haven't started or stored anything in the php session, but a session is created on the page the form is loaded in. Is the instance of the singleton somehow linked to that session? If it's a named session or something?
OR...have I completely misunderstood how singleton classes work? In which case can anyone tell me how I can retrieve the instance of the class that is created on the html form page to use again to validate the code in the form processing script? - And maybe tell me how I should be using singletons!
Many thanks.
Singletons exist for the duration of the request, not the duration of the session.
The idea of a singleton is to provide access to the same object across all included scripts, without having to use any explicit initialisation logic.
So, the first call to $foo = MyObject::singleton() creates a new MyObject, but the second call will simply return that object instead of creating a new one. This is incredibly useful for classes that access external resources such as databases and files.
OR...have I completely misunderstood how singleton classes work?
Partially. Since PHP has no ASP.NET alike application variables, objects in PHP live as long as the request does, unless serialized (for example in a session).
As a solution to your problem: save the captcha code (or the captcha class, which is a bit of overkill imho) in a session variable, like $_SESSION['captcha'].
No, it's not.
You'd have to serialize your singleton object and store it to the session when your code execution ends. When the next page is displayed, you can unserialize the object from your session.
PHP serializes/unserializes objects automatically when you assign them to a session.
This only works correctly under the precondition that your singleton does not use link identifiers to external resources.
Here is an example implementation taken from the comments in PHP docs
class SessionSingleton {
/**
* Returns an instance of the singleton class.
* #return object The singleton instance
*/
public static function _instance()
{
// Start a session if not already started
Session::start();
if ( false == isset( $_SESSION[ self::$_singleton_class ] ) )
{
$class = self::$_singleton_class;
$_SESSION[ self::$_singleton_class ] = new $class;
}
return $_SESSION[ self::$_singleton_class ];
}
/**
* Destroy the singleton object. Deleting the session variable in the
* destructor does not make sense since the destructor is called every
* time the script ends.
*/
public static function _destroy()
{
$_SESSION[ self::$_singleton_class ] = null;
}
/**
* Initialize the singleton object. Use instead of constructor.
*/
public function _initialize( $name )
{
// Something...
}
/**
* Prevent cloning of singleton.
*/
private function __clone()
{
trigger_error( "Cloning a singleton object is not allowed.", E_USER_ERROR );
}
private static $_singleton_class = __CLASS__;
}
I'm going through a Joomla book, and I came across the following piece of code in the chapter of MVC pattern:
class QuizController extends JController
{
static function &getInstance(/* some PHP code... */)
{
// use a static array to store controller instances
static $instances;
if (!$instances)
{
$instances = array();
}
/* some PHP code... */
// return a reference to the controller
return $instances[$class];
}
}
What is the lifetime of $instances? When is it destroyed?
If it is alive during the lifetime of the request, then declaring $instances static doesn't make sense, because this code will be run once.
If it is alive during user session, how does PHP engine knows this?
If it is alive during the lifetime of the request, then declaring
$instances static doesn't make sense, because this code will be run
once.
Yes, the static variable only exists for the duration of the request. It's a common design pattern to store an object in a static variable if it's expensive to create, or if having multiple copies will cause problems.
It's not necessarily the case that this function only be called once - it will likely be called multiple times, at least on certain pages / for certain modules.
By the looks of the code, the variable lasts until the script is finished being executed.
Because you can't access the variable from outside that function, and there is no unset() call to that variable, it doesn't get destroyed until the end of script execution.
I wonder if anyone can help out here, I'm trying to understand how use an objects properties across multiple non class pages,but I can't seem to be able to get my head around everything i have tried so far.
For example a class called person;
class person {
static $name;
}
but i have a number of different regular pages that want to utilize $name across the board.
I have trying things like this;
pageone.php
include "person.php";
$names = new Person();
echo person::$name;
names::$name='bob';
pagetwo.php
include "person.php";
echo person::$name;
I can work with classes to the extent I'm OK as long as I am creating new instances every page, but how can make the properties of one object available to all, like a shared variable ?
Thanks
Every new instance of a PHP script "forgets" everything done in previous scripts. The usual way of establishing a "storage room" for data across page loads is sessions. A session is essentially a specific ID a user gets when visiting a page. That ID is stored in a cookie, or a GET variable that is appended to every URL. PHP keeps text files in a special directory that can contain session specific data. Every file is named using the session ID.
The PHP manual has a thorough introduction here.
pageone.php
session_start();
$_SESSION["name"] = "Bob",
pagetwo.php
session_start();
echo $_SESSION["name"]; // Bob
Now if you had an instantiated object, you could serialize it, store it in a session variable, and unserialize it back in the 2nd page. I don't think that can be done with static classes though. But this should be a good start.
You need to initialize the static variable inside the class declaration itself:
class Person {
public static $name = 'bob';
}
Or, you need some bootstrapping mechanism, where you inititalize the static variable:
bootstrap.php:
Person::$name = 'bob';
and then in the pages:
// assuming, you preloaded the bootstrap somewhere first
$person = new Person();
echo $person::$name;
edit
Ugh, what was I thinking... the above won't even work. You can't access a static member like that on an instance. Only through a method, like so:
class Person
{
public static $name;
public function getName()
{
return self::$name;
}
}
// assuming, you preloaded the bootstrap somewhere first
$person = new Person();
echo $person->getName();
/end edit
Or as Pekka pointed out, use sessions to keep state.
But more importanty: what is the goal you are trying to achieve? If you want to maintain state of a Person object between different requests, then Pekka's route is the way to go, or alternatively use another persistance storage mechanism, like a DB, File, etc...
Because I presume you don't mean to have every single Person instance named 'bob' do you? I presume you mean to maintain state of a single Person instance.
So, concluding, you probably don't want to use a static member to begin with.
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.