Maybe I'm just not seeing it, but is there any other way to access a previously created session namespace, other than calling $_SESSION directly? Besides the fact that I really don't want to do this, the Zend documentation also advises against this:
while $_SESSION is still available in
PHP's global namespace, developers
should refrain from directly accessing
it, so that Zend_Session and
Zend_Session_Namespace can most
effectively and securely provide its
suite of session related
functionality.
The Zend_Session_Namespace class doesn't have a static method for getting a namespace, and although the now deprecated namespaceGet method in Zend_Session instructs me to use Zend_Session_Namespace#getIterator, that method is not static.
So that means I need to initialize a new namespace, using the new keyword. The problem is, this doesn't include previously set variables:
$ns = new Zend_Session_Namespace('foo');
$ns->foo = 'bar';
On a subsequent request, this:
print_R(new Zend_Session_Namespace('Foo'));
...prints this:
Zend_Session_Namespace Object
(
[_namespace:protected] => Foo
)
which seems obvious.
So how am I supposed to fetch the previously created namespace, without using $_SESSION['Foo']?
The case of your two code examples doesn't match (foo vs. Foo), I'm not sure if that was just a typo or not. Zend_Session_Namespace is just a wrapper for $_SESSION, so all you need to do is create a namespace object with the same key and then all your data should be available.
$ns = new Zend_Session_Namespace('foo');
$ns->foo = 'bar';
and then on another page:
$ns = new Zend_Session_Namespace('foo');
echo $ns->foo; // should output bar
if this doesn't work then there is a problem with your session configuration.
Related
I have a global object, a "registry", it is a container with other important objects:
Input object.
Output object.
DB object with connection.
Logging object.
Session object.
I need to have this global object in every place (object), where I process my request.
Like in my JBoss environment, where I have one Stateful Session Bean as a front controller, which directs the processing to a special Stateless Session Bean, I have one entry point, "facade.php".
In this facade.php, I create the global object and place the other objects (input object, ...) into it.
Then there is a large switch statement, where I redirect the request to special processing objects.
Is there a method, mechanism, to have access to this general object from the processing objects without handing it over as a parameter?
To have a variable available everywhere you can use a $_SESSION variable like so:
session_start();
$_SESSION['registry'] = // Your data
Make sure to use the session_start() function whenever you want access to the session variables.
What you can do:
put the Object into the Session. http://php.net/manual/en/book.session.php (but this will survive "requests")
Make the Object a singleton. http://en.wikipedia.org/wiki/Singleton_pattern (i recommend this, if its suitable for the situation. don't just use singletons to avoid passing parameters to classes/functions.)
Mark the Object with the global keyword. http://php.net/manual/en/language.variables.scope.php
So lets say I have a class that is composed of other classes.
class HttpRequest
{
public $session = new Session();
// .. the rest of the HttpRequest code
}
Now, I want to have access to Session class through HttpRequest class so Im using composition.
But does this breaks laws of OOP Encapsulation or Data hidding that states that all properties should be protected, and accessed through setter and getter methods?
Is this wrong:
$request = new HttpRequest();
$request->session->set('id', 5);
or should I use this:
$request = new HttpRequest();
$session = $request->getSession();
$session->set('id', 5);
Encapsulation states that properties shoud be protected.
How to provide access to inner classes then? Is the first example wrong as far as proper OOP goes?
There are valid reasons to not allow direct access to the object:
Allows for manipulation of the object outside of the object itself. If you make the property public, any part of your code could overwrite $session on the HttpRequest class, and you'd have a tough time tracking it down. Encapsulation from a data protection standpoint is there to ensure that only the object's methods can directly alter the object.
Allows you to gracefully handle the case in which that variable is not set. If, for some reason, $session does not get set on your class - you'll immediately have a fatal when you try to call a method on it. If you wrap it in a getter, you could check for that condition and create a new instance of the class on the fly.
Follows true "OO" paradigms
However, in some cases I would say it is okay to do this. Particularly if you know that the property will always be set (and the only way in which it would not be set is not a supported way to use the object).
It also makes sense depending on how the property is going to be accessed. Symfony2 uses this in their Request classes. It feels natural in that case, as the "query" "post" and "request" vars are all "ParameterBag"s (glorified arrays). However, they do expose a getter for the Session object - likely because of it's use case.
In short: it really depends on how you'll be using the variable. In this particular case, I'd say it doesn't much matter.
I like your first option, (It's the one using composition), and look that has encapsulation (I don't know what makes the function set), but I suppose that it's modifying some attribute through the function of the "component" object "session", that pattern is also known as "delegation".
On the other hand if you use encapsulation you cannot user "public", that is allowing to be modified for everybody. It's because of this that you user setters or getter, or in your code "set"
I know this is old, but I would use neither of these. Does your HttpRequest object really need to hold onto the Session object or can a Session object be passed into some functions of the HttpRequest object that need it? Is there a strong case for having HttpRequest store this object?
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.
Hay guys. I'm kinda new to OOP in PHP. I've learnt how to write and create objects. Is there a way to take an object and pass it to another script? either using GET or POST or SESSION or whatever. If there isn't how would i assign an object some variables on one page, then assign the same object more variables on another page?
Thanks
You can store objects in the session but you need to include the file which contains the class definition before calling session_start() (or use class autoloading and set this up before you start the session). For example:
On every page:
//include class definition
require('class.php');
//start session
session_start();
1st page:
$object = new class();
$object->someProperty = 'hello';
//store in session
$_SESSION['object'] = $object;
Subsequent pages:
$object = $_SESSION['object'];
//add something else, which will be stored in the session
$object->anotherPropery = 'Something';
Here is an example with autoloading in respect to the answer by Tom Haigh:
Before you start the session:
function __autoload($className) {
$file = PATH_TO_FOLDER_WITH_ALL_CLASS_FILES."/".$className.'.php';
if(file_exists($file)) {
require_once $file;
}
}
session_start();
Page passing the object:
$object = new class();
$object->someProperty = 'hello';
//store in session
$_SESSION['object'] = $object;
Page receiving the object:
$object = $_SESSION['object'];
//add something else, which will be stored in the session
$object->anotherPropery = 'Something';
The autoload method will automatically load the objects while you retrieve data from session.
You could store the object in a SESSION.
You can serialize the object and pass through GET or POST.
If you want the object to persist across the site, then SESSION is probably your best bet.
You can use the $_SESSION. but it will only be for that session.
Using an object on multiple 'scripts':
First, you have to decide what kind of structure your OOP application has.
If you use something like MVC pattern, you do not have to this by using SESSION or REQUEST, because you can 'plug' the objects you want to use into 'one'.
What does this mean?
A quick example:
User A enters your site index.php
Now you can load the content from a static index.html,
but if you want to check whether the user is authenticated to see specific contents e.g. the 'Admin Login', you can use include_once('Authentication.php') and initiate a class from this file, e.g. <?php $Auth = new Auth_Handler; ?>
This will make the Auth class also available in the index.php or any other file you want to include this class.
If you want to pass the authentication class' return value to another file e.g. 'register.php' you can use the SESSION or any other Cache.
Passing whole objects is not recommend due to their size.
Including and initiating the wanted classes at the beginning of files is much better.
And passing the returns by SESSION uses less space.
It really depends one which framework or API you want to use, or what project you want to create.
I've got a site setup that, on page load, turns all user submitted strings into SafeString objects. For those unfamiliar with SafeString, it basically forces the user to echo out sanitized data preventing XSS and whatnot..
Anyways, there's a problem. My $_SESSION array is being filled with __PHP_Incomplete_Class Object. From what I've read, this is due to not initializing the class before the session and then storing class objects in the session.
Here's my code:
require_once __WEBROOT__ . '/includes/safestring.class.php';
$temp = array
(
&$_SERVER, &$_GET, &$_POST, &$_COOKIE,
&$_SESSION, &$_ENV, &$_REQUEST, &$_FILES,
&$HTTP_SERVER_VARS, &$HTTP_GET_VARS,
&$HTTP_POST_VARS, &$HTTP_COOKIE_VARS,
&$HTTP_POST_FILES, &$HTTP_ENV_VARS
);
function StringsToSafeString(&$array)
{
foreach ($array as $key => $value)
{
if (is_string($array[$key]))
{
$array[$key] = new SafeString($value);
}
if (is_array($array[$key]))
{
StringsToSafeString($array[$key]);
}
}
}
StringsToSafeString($temp);
unset($temp);
I can't think of a way to rewrite this which would solve the problem :/
Any ideas?
When you're accessing $_SESSION, you're not just changing the current script's copy of the data read from the session, you're writing SafeString objects back into the active session.
But putting custom objects in the session is dodgy and something I would generally try to avoid. To be able to do it you have to have defined the class in question before calling session_start; if you don't, PHP's session handler won't know how to deserialise the instances of that class, and you'll end up with the __PHP_Incomplete_Class Object.
So avoid frobbing the session. If you must take this approach, make a copy of the data from $_SESSION into a local $mysession array. However, I have to say I think the whole idea of a SafeString is dangerous and unworkable; I don't think this approach is ever going to be watertight. Whether a string of raw text is ‘safe’ is nothing to do with where it came from, it is a property of how you encode it for the target context.
If you get another text string from a different source such as the database, or a file, or calculated within the script itself, it needs exactly the same handling as a string that came from the user: it needs to be htmlspecialcharsed. You're going to have to write that escape anyway; the safestring gains you nothing. If you need to send the string to a different destination format, you would need a different escape.
You cannot encapsulate all string processing problems into one handy box and never think about them again; that's just not how strings work.
I know it's been years since this was asked, but I'm posting my answer because none of the answers above actually explain to the OP what is actually wrong.
PHP serializes its sessions using the built-in serialize and unserialize methods. serialize of PHP has the ability to serialize PHP objects (aka class instances) and convert them to string. When you unserialize those strings, It converts them back those same classes with those values. Classes who have some private properties and want to encode/decode that or do something complex in their serialization/deserialization implement the Serializable class and add serialize and unserialize methods to the class.
When PHP's unserialize tries to unserialize a class object, but the class name isn't declared/required, instead of giving a warning or throwing an Exception, it converts it to an object of __PHP_Incomplete_Class.
If you don't want your session objects to convert to __PHP_Incomplete_Class, You can do it by either requiring the class files before you invoke session_start, or by registering an autoload function.
You just have to include the safestring.class.php before you call session_start() when you want to read the SafeString objects from $_SESSION variable:
<?php
require_once __WEBROOT__ . '/includes/safestring.class.php';
session_start();
print_r($_SESSION);
and yeah, if you are using PHP framework that (most probably) calls session_start() internally, make sure you require_once the class file beforehand (use hooks or whatever mechanisms that the framework provides).
I solved the problem using json_encode and json_decode function.
This is where I wanted to assign the value to session.
$user_json = json_encode($user);
$_SESSION['user'] = $user_json;
This is where I show the user after decoding the json
session_start();
$user_json= $_SESSION['user'];
$user = json_decode($user_json);
This solves my problem but I am not sure about performance or security. I haven't checked them.
Lukman's answer is correct. But you already mention that in your question, so apparently you can't instantiate the class before the session starts, for some reason.
You may want to check if sessions start automatically in the php config:
http://www.php.net/manual/en/session.configuration.php#ini.session.auto-start
If they are and yu cant help that, you may want to check if you can have your classes autoloaded prior to that:
http://php.net/manual/en/language.oop5.autoload.php
If all else fails, you can still serialize the objects before you store them in a session, and unserialize them each them you retrieve them:
http://php.net/manual/en/function.serialize.php
I dont see in your code where you store your variables, but it would be something like
$mystuff = unserialize($_SESSION["mystuff"]);
$mystuff->dostuff();
$_SESSION["mystuff"] = serialize($mystuff);
Be sure to load the class definition before you unserialize your variables
$2c,
*-pike
I just dealt with something like this. Took me hours to finally find how my order was screwed.
I had a file being called asynchronously.
myFile.php
that file contained the following..
$result = include ("myOtherFile.php");
return $result;
Myotherfile.php has something like this
require_once "lib/myClassLibs.php";
require_once "webconfig.php";
the webconfig.php had the session_start() call in it.
The lib/myClassLibs has all the class info init. If you check before the webconfig call, you can see that the class is available.
If you check before the webconfig call, you will also see that the session has started already. If you check before the lib/myClassLibs.php, you will see the session is already started.
Checking in myFile.php before you include MyOtherFile.php, you find the session has not started.
This represented legacy code that has worked for the last 8 years without me fiddling with it. I pulled the includes out of the "MyOtherFile.php". Now my sessions are synching properly.
I solved this problem by including the __autoload function at the top of my php file. So it looks like this:
<?php
require_once("path/to/include.inc");
//Needed for serialization/deserialization
function __autoload($class_name) {
include "path/to/". $class_name . '.php';
}
In PHP 5, this function isn't be needed but I was stuck until I used this function. Hope this helps someone else!
I know this is a really old question but I ran into this problem. After more research and experimenting I came up with a what I think is an acceptable alternative to storing classes in the session. It might be a bit hackish, but works for my current project.
NOTE: this work-around works for me because I start a session when a user logs in and don't want to include every possible class the user might, or might not encounter during the session. Including all the classes doesn't seem practical or efficient (but maybe this isn't any better ???).
First, my base class contains the following code that populates the object attributes from a given array, automatically.
class BaseClass {
public function __construct($properties=[]){
if (!empty($properties)) {
array_walk($properties, function ($val, $key) {
$this->fromArray($key, $val);
});
}
}
public function fromArray($property, $value){
return (property_exists($this, $property)) ? $this->$property = $value : null;
}
public function toArray(){
return get_object_vars($this);
}
}
The Work-Around:
I use the toArray() method to convert a class instance to an array before it goes into the session, then create a new instance of the class when fetching it from the session.
$_SESSION['user'] = $userInstance->toArray();
// ... do stuff ...
$userInstance = new User($_SESSION['user']);
This is also really handy for writing classes to a database and converting to JSON. Both of which are made easier when working with a PHP array.
Like I said above, this may or may not be the most efficient way to handle this problem. It also raises the question, "should I be using PHP classes if I'm just going to convert to arrays?"
I run into the same problem and the solution was inspired by #bobince answer
To be able to do it you have to have defined the class in question before calling
session_start
First, my session was set like this:
$_SESSION["customer"] = $customerObj;
Then before calling the session_start(), I have to load or defined the class first by importing it and then call session_start() right after
require 'entity/Customer.php';
ob_start();
session_start();
$customer = new Customer();
if (isset($_SESSION["customer"]))
{
$customer = $_SESSION["customer"];
echo $customer->getCustomerName();
}
My mistake was sending the user to a PHP page without including the class in that page, only in the original page.
Looked something like this:
index.php
include __DIR__.'AirInfo.php';
session_start();
$plan = new Plan();
header('Location: session.php');
session.php
// Should have put include __DIR__.'AirInfo.php' here
session_start();
My mistake here was that I had set the session.auto_start setting to on. The session would then be initialized before any line of code (including the autoloader) will be called.
I have the same problem with Google Photo API When Try to Authenticate my app and Access Photo API.
Solve it by just use session_start() after include and all use statements.
Here my complete code:
include "./vendor/autoload.php";
use Google\Auth\Credentials\UserRefreshCredentials;
use Google\Photos\Library\V1\PhotosLibraryClient;
use Google\Photos\Library\V1\PhotosLibraryResourceFactory;
use Google\Auth\OAuth2;
session_start();
//rest of code comes here
Short version of #bobince's excellent answer, if you're using an MVC framework and a classmap or psr-4 autoloading etc...
[front controller]
//Do this before session start because session has an object that will not work
// if the class has not been loaded already
require_once('vendor/autoload.php');
//Start a session after your autoload
session_start();
You might just be calling,
session_start();
session_start();
twice in your code. Call it once. Check required php classes for repeats. This was the fix for me.