Is there any way I can persist objects in PHP?
I am creating some objects in a script, but I have to create them everytime the script is run (losing their state unless I save it to DB).
I know variables can be persisted with $_SESSION global, but should I save objects in it?
If object persistance is not possible, what's the use of OOP in PHP?
Serialize the object before you store it in the session:
$s_obj = serialize($myObj);
$_SESSION['myObj'] = $s_obj;
and later, to retrieve and reconstruct it:
$s_obj = $_SESSION['myObj'];
$myObj = unserialize($s_obj);
There is no need to serialize objects:
<?php
class A
{
protected $name;
public function __construct($name) { $this->name = $name; }
public function getName() { return $this->name; }
}
session_start();
if (isset($_SESSION['obj'])) {
die( $_SESSION['obj']->getName() );
}
$_SESSION['obj'] = new A('name');
?>
Object persistence is possible, but it is not automatically provided. You either need to write it yourself, or use an object layer that does it for you. So you'll probably need a database.
PHP is not an environment where your program responds to multiple page requests over time: instead, your program is invoked to response to a page request and terminates when it's done.
The purpose of object oriented code in PHP is to make it possible to do a whole raft of programming algorithms and styles, and to make it easier to do an even bigger range of coding solutions. Yes, they are instantiated and destroyed within a single page call, so you have to work within that paradigm. Many codebases pass object IDs around between pages or in sessions; as soon as they need the corresponding object, it is instantiated and loaded from persistent storage using that ID. A good object layer will make this easy.
Agree with jcinacio, no need to serialize values before inserting into $_SESSION..
php will manage serialize/unserialize for you on each page request/end.
Another way to persist objects/sessions is to save them on file/database, "emulating" the php behaviour. In this case you'll need to serialize values to convert them into strings, and unserialize them once retrieved from database to convert them back to object.
You may also be interested in the __sleep and __wakeup "Magic Methods" [0] of the object you're going to save. These methods are called when serializing/unserializing the object, to perform action such as connecting/disconnecting from a database, etc.
[0] http://php.net/oop5.magic
Note that if your state is truly shared between the various users, you don't want to use $_SESSION. $_SESSION is only available in the same user session - i.e. if you have 50 users on the site at once, every one of them will have to pay the computation penalty at least once.
In those cases, you might want to use a persistent disk-based on in-memory (memcache) cache.
Try a cache like APC http://www.php.net/apc/
Related
In PHP, is it considered best practice to store the complete object as a session variable?
From my experience, sometimes it works and sometime not. Is there any specific reason for it?
Example:
session_start();
$object = new sample_object();
$_SESSION['sample'] = $object;
Use serialize() in PHP before store your object, and call unserialize() when retrieve your object from session.
store object
session_start();
$object = new sample_object();
$_SESSION['sample'] = serialize($object);
retrieve object
session_start();
$object = unserialize($_SESSION['sample']);
In general, the best practice is not to store objects in session at all :D
I would recommend to instead just store data. It will have the added benefit of making the debugging process a bit easier, when you have to inspect the current state of the session.
If you want to be really fancy, you could make a separate data mapper, that stores and retrieves data from the session for instances of that specific class (or group of classes, with the same interface).
The project I'm working on requires me to have some objects, including an event manager, (read-only) configuration manager and plugin manager, that are available everywhere in the system.
I was using global variables for these, until someone (with a C++ background) kindly pointed out that "You're probably doing something wrong if you need global variables".
He suggested using a state object that's passed to all functions that need it.
So I did:
$state = new State();
$state->register('eventManager' , new EventManager());
$state->register('configManager', new ConfigManager());
$state->register('cacheManager' , new CacheManager());
$state->register('pluginManager', new PluginManager());
$state->get('pluginManager')->initialize($state);
While I can see the benefit of this method in more stateful languages, it seems kind of pointless to me in a (mostly?) stateless language like PHP, where the state is lost after the page is done loading.
Is there any benefit to passing a state object around in a (mostly) stateless language like PHP, does it hold any benefits over other approaches (i.e. a globals-based system) and are there better ways to handle this?
The registry that you propose is still a global variable. And if you want to access a global variable (even if it is an object, although a global one), you are doing something wrong.
A proper application only has on phase where global state plays a role: When bootstrapping it. The request starting the script is global, any request data sent with it is global, and any configuration that affects the application and is stored in a file or some other appropriate storage is global.
The first phase should initialize some dependency injection that puts all the parts that make up the application together. That object graph would be created on demand when the processing of the request has decided which part of the code should be called to respond to the request.
Usually this decision is done inside a framework processing the request, and the dependency injection likely will be also done via a framework. Your own code would only accept either the values needed to operate, or the other objects that are needed.
For example, if your code would need a database, then you'd configure the database object to accept the URL and credentials for your database, and then you'd configure your reader object to accept that database object.
It would be the task of the dependency injection to either create only one single database object, or multiple of them. You don't have to use the outdated "singleton antipattern" because it has many drawbacks.
So in this scenario, there are some objects existing in the dependency injection part that are only created once and injected when needed. These objects do not enforce to be only created once, and they are not stored inside a globally accessible variable. However, something has to live in a global variable, but this is only the main framework object and probably the dependency injection container, and they are never shared into the remaining code as a global variable - so this is not harmful at all.
One way to do this is with singletons:
class ConfigManager {
private static $instance = NULL;
public static function getInstance(){
if(self::$instance === NULL) {
self::$instance = new ConfigManager();
}
return self::$instance;
}
private function __construct(){
// Notice that this is private - only getInstance() can call this.
}
}
// When you need it:
$config = ConfigManager::getInstance();
There are many different opinions on how to do what you're asking - and I myself don't think singletons are always the best way to do this, either. It really depends on your use-case.
That said, singletons are just one common pattern for a class whose instance is supposed to be accessible everywhere.
Given a class with some really expensive code, I want to avoid running that code when re-defining an instance.
Best explained with some pseudo-code:
$foo = new Foo('bar');
print $foo->eat_cpu_and_database_resources(); #=> 3.14159
$foo->store_in_cache(); #Uses an existing Memcached and/or caching to store serialized.
#new thread, such as a new HTTP request. Could be days later.
$bar = new Foo('bar');
print $foo->eat_cpu_and_database_resources(); #=> 3.14159
The second $bar should re-initialize the earlier created instance $foo. Inside my actual class, I do several things on eat_cpu_and_database_resources(), which is named get_weighted_tags(): calculate a weighted tagcloud from values in $foo->tags. $foo->tags() was filled with expensive $foo->add_tag() calls. I would like to retrieve the prepared and filled instance from now on, from cache.
I have tried to simply fetch from (serialized) cache on __construct() and assign the retrieved instance to $this, which is not allowed in PHP:
function __construct ($id) {
if ($cached = $this->cache_get($id)) {
$this = $cached
}
else {
#initialize normally
}
}
Is this a proper way? Or should I treat every instance unique and instead apply caching in the eat_cpu_and_database_resources() method, instead of caching the entire instance?
Is there a built-in way in PHP to revive old instances (in a new thread)?
Depending on the size of Foo, you might want to cache the entire object in the cache store Drupal provides. If it's too big for that, see if it makes sense to just cache the result to the expensive method call(s).
If you want to unserialize an object from the PHP internal format, you have to use the corresponding unserialize method and might want to add the magic __wakeup method to do any post re-initializations:
The intended use of __wakeup is to reestablish any database connections that may have been lost during serialization and perform other reinitialization tasks.
Since you have to have the serialized string for that first, you might want to add some facilitiy to encapsulate this logic, like a Factory or Builder pattern or a dedicated FooCache.
Personally I find caching the method call the best option because there's no point in caching the whole object when it's really just the method call that's expensive. That will also save you any additional work checking whether there is a serialized string to start with or building a factory.
This is probably a noob question, so please be kind.
I'm trying to implement a cache on an expensive "activity" object. In the constructor I first check the cache to see if this Activity instance already exists. If not, I do all the queries to build up the object, serialize it and save it to cache. The next time I come in, I check the cache and my object is there, so I unserialize it. Now is my problem, how do I put that object into $this, the current object? I can't just say "$this = unserialize($row[0]);" That fails with the error message, "Cannot re-assign $this in ActivityClass.php". What am I missing?
Thanks a ton!
Mike
If you don't want your construction to leave the class, you can create a factory method:
class Activity
{
public static function Create(/* your params */)
{
// construct cache and key, whatever
$obj = unserialize($cache->get($key));
if ($obj) return $obj;
return new Activity(/* params */);
}
// rest of your stuff
}
You'll have to serialize only your object's internal state, i.e. its parameters (aka "member variables"). In fact, in this instance, serialize() isn't really what you want to do; rather, you want to store your ActivityClass's data to your cache, not the serialization of the entire object. This gets tricky, though, because as you add new parameters later you need to remember to store these in your cache as well.
Alternatively, you can implement a singleton or factory pattern for your ActivityClass. Since you say you're pulling the class from the cache in the constructor, I take it that only one instance of this class is meant to exist at any given time? In this case, you should make your class a singleton, by doing the following:
Make the __construct() method private or protected.
Create a public static method (I tend to call this getInstance()) that will check your cache for the object, or instantiate a new one and then cache it.
Now instead of directly instantiating a new ActivityClass object, you instead write $foo = ActivityClass::getInstance();, which gives you either a new object or unserializes and returns your cached one.
As you noticed, you cannot just override the current object as a whole.
Instead, a possibility would be to store the data you're serializing/unserializing into a property of your object.
This way, you wouldn't serialize your whole object, but only one of its properties -- and only that single property would be overriden when unserializing.
Typically, you wouldn't serialize the connection to the database, which could be another property of your object.
Another possibility would be to not have your object deal with its own (de-)serialization.
Instead, you should :
Use an external class to instanciate your object
With that external class being responsible of either :
Loading data from cache and pushing it into your object,
Or calling the right method of your class, to load data from the database -- and, then, save that object to cache.
Because I noticed it was a pattern I was constantly re-implementing, I made a DataContainer class that overrides __set,__get,__isset,__unset, and implements ArrayAccess, Countable, IteratorAggregate, and Serializable
For example, my View class, which renders PHP templates in an MVC fashion, inherits from DataContainer so that it has overloaded access to the data that gets supplied to the template.
Now, I am finding myself wanting to create a Session class to abstract away from PHP's low-level session handling. It occured to me that this Session class would do just about everything DataContainer does, and in fact, is-a DataContainer - it holds data.
However, if I inherit from DataContainer, then all the overloaded calls go to its private $_data array. Of course, I can override DataContainer's public get($key), public set($key,$val), etc methods, but the only thing I would be doing is renaming $this->_data to $_SESSION.
Is it possible to set a variable as a reference to a special global like $_SESSION?
class Session extends DataContainer {
//singleton stuff
private function __construct() {
$this->_data =& $_SESSION;
}
}
Is it even a good idea to do this? If not, what do you suggest?
Is it possible to set a variable as a
reference to a special global like
$_SESSION?
Yes, $this->_data =& $_SESSION;
Is it even a good idea to do this?
I don't see why not, one could argue that it may be better to pass the data in to the constructor by reference so that it can be used for any array not just session.
Thus, Yes.
edit: as a side point, remember you don't always have a session, sometimes your running on cli etc, personally I have my own session object (just a DataContainer like yours) which I then persist to $_SESSION where needed, or file or.. - ie I save (stateful) session objects in the $_SESSION rather than use the $_SESSION as the session data, if that makes sense..
I sure hope it's good idea, as I use it constantly. Kind of a Decorator, only for a variable-container. Yes, it has worked for for about 3 years now, and I very much like the validating & tracing capabilities it yields on more complex projects. Keep in mind you cannot force any other code to use the container instead of $_SESSION, but a project wide search for that particular string yields fast results when the majority of the code uses other means.
I also make it a Singleton for those moments a projects is not suited for a proper dependancy-injection path, either for size, time or historical reasons. Referencing a Session::instance() is about as easy as the $_SESSION superglobal.