Strange behaviour migrating sessions with callbacks from php4 to php5 - php

I have to migrate a php4 app that uses session_set_save_handler() to php5.
In php4 everything was fine, but in php5 the callback functions cannot access the global vars anymore, which were set on the page before session_set_save_handler() was called.
In the example below the global var $g1 cannot be accessed in the session_writer() (which is passed as a callback function)
Is there some explanation for this behavior or can you give a hint on migrating sessions with callbacks from php4 to 5?
This is the pseudo code:
function session_writer($id,$vars) {
global $g1;
echo "g1 not defined here: ".is_object($g1);
}
global $g1;
$g1 = SomeObject(); //which is the DB connection for the session writer
session_set_save_handler($o,$c,$r,"session_writer",$d,$g);
session_start();

This is actually noted in the documentation:
As of PHP 5.0.5 the write and close handlers are called after object destruction and therefore cannot use objects or throw exceptions. The object destructors can however use sessions.
It is possible to call session_write_close() from the destructor to solve this chicken and egg problem.
Essentially, you'll have to call session_write_close() from the destructor of your SomeObject, or alternatively, do the following:
<?php register_shutdown_function("session_write_close"); ?>
Either of those solutions should force the writing and closing of the session before all objects are destroyed, allowing you to keep your original callback function.

Molf's answer identifies the problem. Here's some more info:
session_write_close() takes as input an identifier and the data associated with it. If you're going for minimal change, you'll probably need to know the data is what is returned by session_encode() (which is a string encoding the contents of the $_SESSION array). The identifier is what is returned by session_id().

Related

Instantiated object scope and life time in PHP OOP

I want to know when an object in PHP will be destruct (destroy) by default. For example if we instantiate a class in a function, does it destruct at the end of function or still will be alive ?
It will eventually be destroyed when all variables that point to the object cease to exist. Variables cease to exist when they go out of scope or when they are unset. Variables go out of scope when the scope/function they were declared in exits.
Yes, it will. The object will live for the duration that it's instance is in scope. You may wish to browse the PHP Garbage Collection documentation. It's also worth noting that you can try these things out for yourself by implementing a __destruct magic method.

SessionHandlerInterface write method not being called

So I have created a class implementing SessionhandlerInterface and set it as a session handler:
$sessionHandler = new SessionHandler();
session_set_save_handler($sessionHandler);
session_start();
The problem is, the write function is never called.
If I use the second parameter of session_set_save_handler and set it to false:
session_set_save_handler($sessionHandler, false);
Then it works properly. Can somebody explain this behaviour to me? I am using PHP 5.4.6.
In the documentation there is written:
When using objects as session save handlers, it is important to register the shutdown function with PHP to avoid unexpected side-effects from the way PHP internally destroys objects on shutdown and may prevent the write and close from being called. Typically you should register 'session_write_close' using the register_shutdown_function() function.
As of PHP 5.4.0 you can use session_register_shutdown() or simply use the 'register shutdown' flag when invoking session_set_save_handler() using the OOP method and passing an instance that implements SessionHandlerInterface.
But I don't fully understand that.
The documentation states the following:
The "write" handler is not executed until after the output stream is
closed. Thus, output from debugging statements in the "write" handler
will never be seen in the browser. If debugging output is necessary,
it is suggested that the debug output be written to a file instead.
This is probably why you don't see the function being called if you use a simple echo.

Deconstructor in PHP

After taking a look at some old code:
//Nothing like a destructor!!
function destroy() {
settype(&$this, 'null');
}
And called by
$Cache->destroy();
However in PHP 5.3 I get
Deprecated: Call-time pass-by-reference has been deprecated in /blah/-cache-.php on line 154
How should I do this?
Your immediate problem can be met by removing the & in $this, but the whole construction doesn't make sense to me. If it's not plain invalid to destroy $this from within the object's context, it's definitely not good practice.
To destroy an object, a simple
unset($Cache);
will do.
If one wants to execute stuff when the object is destroyed, one should define a destructor in the class. (The comment in your destroy() code says that this is not the point here, though. :)
Just destroy the object like normal.
unset($Cache);
I don't know why they would do that nasty looking mess above. Keep in mind that if the object has pointers in different places you will need to unset all of them - not just that one line. (singletons are an example)
The error you're getting is not related to having a destructor. The error is simply because you've tried to pass $this by reference in a function call.
PHP used to allow this, but in current versions of PHP, if you want to pass something by reference, you should specify it in the function declaration.
Therefore, your function call should look like this:
settype($this, 'null');
(ie without the &). (btw -- the word 'null' in a string??? is that what you meant?)
And if you want to pass by ref, your function should look like this:
function settype(&$object, $secondparameter) {
...whatever the function does...
}
ie with the & in the function parameter.
This rule applies in all cases in PHP. As I said, it has nothing to do with your destructor method, or with it being an object; that's just how you pass by reference in modern PHP.
Now onto the other part of your question: A destructor method.
PHP does allow for an automatic destructor function, written within your class like this:
function __destruct() {
print "Destroying " . $this->name . "\n";
}
If you write this code, the __destruct() method will be called when the object is destroyed. This may or may not be what you want -- depends on how you want it to work. The object will be destroyed when all references to it are unset or come out of scope. If you're passing the object handle around by reference, this may not happen when you expect it to -- ie the object may persist in memory even when you say unset($myobject);. In this case, it may only actually get destroyed when the PHP program finishes running. If this is the case, and you need it to be called sooner than that, you may be fine continuing with the destroy() method you have already, and calling it explicity.
Hope that answers your question.

PHP __Constructor & __Destructor Questions

I've been trying to learn the object oriented side of PHP, and was wondering:
If I used a _constructor to open a connection to a database, used a function within that class (eg. insert), would the defined __destructor close the connection after the method "insert" is executed?
class data(){
function __constructor {
// connect to db
}
function insert($data){
// mysql_query(...)
}
function __destructor {
// close connection to db
}
}
$obj = new db();
$obj->insert('mumbo jumbo');
Or would the connection to the database still be open?
Cause I read that the destructor is only run if the object is destroyed. But how do you destroy an object?
In PHP, an object is destroyed when it goes out of scope. This is normally when the script stops executing or when the function it was created within ends, but you can destroy an object early in your code using:
unset($my_variable);
So, to answer your question, you should be fine allowing the destructor to handle closing the DB for you in most situations, especially with small scripts.
Yes, that will work fine, as long as you use the correct names, __construct() and __destruct(), for your constructors and destructors, as opposed to what you have there.
The object is destroyed once there is no more reference to it, for example by unset()-ting the last variable holding the object or when the script execution terminates.
By the way, the magic methods are named __construct and __destruct, without the trailing -or.
BTW, the constructors and destructors are called __construct and __destruct.
__destructor would be called when there are no more references to the the db. Typically, this occurs when the object goes out of scope, but if you have saved other references to it, this won't happen. You can remove references to the db using
unset($obj);
and likewise if you have stored $obj anywhere.
Keep in mind that PHP also supports persistent connections to databases, which means that even if your object has been destroyed, the connection to DB is still open "in the background" and will be reused when you call the correspondending pconnect (or PDO analogue) the next time.

PHP __PHP_Incomplete_Class Object with my $_SESSION data

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 htmlspecialchars​ed. 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.

Categories