SessionHandlerInterface write method not being called - php

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.

Related

Intercepting calls to defined PHP methods

I'm attempting to create a Trait to make "freezable value objects". (To avoid positional parameters and telescoping constructors.) This means the setters can be used to initialize the object, and then ->freeze() is called, which makes the object "immutable". The setters will still be there, but when called, they will throw an exception. This can be achieved with a $this->assertIsWritable(); call at the start of each setter. However I'd like to avoid this (as its easy to forget such a call) and do this check automatically. Is there a way to intercept calls to defined methods?
Not acceptable in the solution:
Approaches that break type hinting and/or static code analysis
Dependence on special PHP extensions
I'm using PHP 7.0.
This is not possible without modifying the runtime (using extensions such as Runkit or uopz or performing source code transformation on your PHP code while it being loaded (using stream wrapper magic).

When exactly is the autoloader called?

I have an autoloader that is registered using spl_autoload_register():
class MyAutoLoader{
public function __construct(){
spl_autoload_register(array($this, 'loader'));
}
public function loader($className){
var_dump($className);
}
}
$al = new MyAutoLoader(); //Register the autoloader
From the var_dump(), the autoloader seems to be called on lots of things, things that are data to be inserted into a database, parameterized SQL queries and what not:
string 'name' (length=4)
string 'a:2:{s:5:"label";s:4:"Name";s:8:"required";b:1;}' (length=48)
string 'en_US' (length=5)
string 'object' (length=6)
string 'name = ?' (length=8)
These things will never be classes so should never be loaded using new or class_exists(), etc.
Under what circumstances/function calls are autoloaders called? I would like to put a stop to autoloading "classNames" that are not classes from being called, because each $className is checked using file_exist(), and having these data strings checked is pretty inefficient.
Problem resolved. I first did a back trace as suggested by Brad and dumped the traces to a file (just add a small snippet that opens a file and appends to it).
Obviously, the trace was very big, but I picked the simplest one I could find. Incidentally, that trace happened to be one that called a database (ORM) wrapper I have written to wrap around the awesome RedBean ORM library. The results from me dumping $className also validates that, because those strings are data are going into or coming out of the database.
Having said that, I have a __call() that intercepts methods to my database wrapper, does some processing, pass it to RedBean, process the result, and then sends it back to the caller.
Problem: During the processing, I am making calls to is_subclass_of() and instanceof, which will obviously ask the autoloader to try and load the class (since we don't have any class called name =? loaded, nor does it exist).
The solution was to actually make sure we have an object before calling is_subclass_of() and instanceof: if(is_object($someproperty) && is_subclass_of($someproperty)).
If $someproperty is not an object, the if immediately short-circuits and instanceof and is_subclass_of() is never called, which means the call to the autoloader is never made.
As mentioned by brad, having all sorts of things going to the autoloader to be included using require_once can be a huge security risk and at the same time, hitting the file system so many times using file_exists() is pretty inefficient too.
So, in conclusion, the autoloader is called every time you use instanceof, is_subclass_of, other class-type functions, class-exist functions and reflection methods, as Charles motioned in his answer.
So, the moral of the story is that if you plan to use a class-type function, or any of the functions mentioned above on a variable of mixed type, check it first before passing it to the class-type function.
Under what circumstances/function calls are autoloaders called?
I realize that this has ended up basically being a secondary question given the problems noted in the comments, but it's still worth answering.
Autoload functions, that being a __autoload() function in your code or callbacks registered through spl_autoload_register, are called when -- and only when -- PHP needs to access a class that has not been defined. This is done everywhere with few exceptions, such as class_exists, which have arguments that tell PHP not to call any autoloaders.
The PHP manual has a page dedicated to autoloading. I recommend reviewing it if there's any confusion.
You can use debug_backtrace or debug_print_backtrace to find out where exactly the auto-loader function is being called.
Technically it should only be called by PHP when a script references a class name that does not exist.

Custom session handler in php using db

I am having problem with implementing custom session handler in php.
The code:
http://pastebin.com/9QV9f22Q
I initialize this once in my frameworks bootstrap, after I have connection with db:
require_once 'DbSession.php';
$session = new DbSession();
session_start();
But then I can't view my page. Firefox gets 302 Status with "Server not found" error. Firebug says that content is 5k long, but I can't view the page.
Log after one reload:
http://pastebin.com/JYe14nGR
I wonder why it still "loses" that created DbSession instance. Do yo have any ideas? TIA
Your code is based on a false premise: that you can return a different object from a PHP constructor. Quite the opposite: PHP entirely ignores the return value from a constructor.
Here's what's actually happening.
When your code calls:
$session = new DbSession();
the $firstTime == false check runs, meaning getInstance() gets called.
getInstance finds no existing instance, so it calls setup().
setup() calls new DbSession() again, this time passing in the argument preventing another call to getInstance(). It creates the object, registers it as the session handler, and returns it.
getInstance shoves the object in a static variable, and then returns it to the original constructor call. The original constructor call then drops the existing object on the ground and returns a brand new copy of itself.
You can fix some of this insanity by never instantiating the object outside of setup/getInstance(). Try making the constructor protected and only ever calling getInstance().
However, none of this explains why the code is malfunctioning for you. In fact, we can't explain it either. We're missing all the rest of the code, including what database adapter you're using, what ORM you're using (or even if you are using an ORM, your class and method names suggest it), what framework(s) might be involved, etc.
Try cutting all of the actual database touching from the class. Just write files on disk. Get that working first, then introduce the database layer. Chances are that your error will become obvious at that point.

trying to avoid serialization in php with qcodo

i am having a little problems when storing objects in session. According what i think i understood, if the class is serializable and you include it before calling session_start(), php automatically serializes/unserializes the object.
In the next example (using qcodo 0.4.22 framework) i can not recover the value of the object:
require(dirname(__FILE__) . '/../includes/prepend.inc.php');
QApplication::QcodoInfo();
if (!isset($_SESSION["person"])) {
$person = Person::LoadById(1);
$_SESSION["person"]=$person;
}
else {
echo "Hello ".$_SESSION["person"]->FirstName;
}
So, in order to work i am forced to do:
require(dirname(__FILE__) . '/../includes/prepend.inc.php');
QApplication::QcodoInfo();
if (!isset($_SESSION["person"])) {
$person = Person::LoadById(1);
$_SESSION["person"]=serialize($person);
}
else {
echo "Hello ".unserialize($_SESSION["person"])->FirstName;
}
With no-qcodo classes i dont need to use serialization.
Is it possible to avoid the serialization?
Thanks for providing the link to the forums. You put a very important information in there:
When the object is in the session i get the error: “The script tried to execute a method or access a property of an incomplete object.”
That means, at the time of unserializing the session, the class definitions are not already loaded. The unserialization is done at session_start. So first load all class definitions and then start the session. Helpful in that context are:
Autoloading Classes Docs
Define unserialize_callback_func (a PHP setting, see unserializeDocs). Everytime an undefined class should be instantiated, it'll be called, so you can load the classes via a callback, like with Autloading (Autoloading comes first, you can use this one for debugging however).
Related: (The title of the question is misleading, the interesting part is the answer:) Why is unserialize_callback_func needed when spl_autoload_register is already used?
So try to find out in which like the session gets started. Maybe you only need to start it later, the PHP manual has more information about sessions in general and how to configure when it starts, there is an option to auto-start sessions, maybe you're running into that, but that's just an assumption, there is more to configure and I'm not fluent how Qcodo loads classes.
PHP Session Runtime Configuration Docs
Session Introduction Docs
Thanks!. I am closer to the solution with your comment.
Looking into qcodo core i see the place where session is initialized..... and it is done before including the classes.
The problem is that the class is included in the moment i make a call on it (how can is that possible?). I have written an echo sentence in the class file, and that echo appears in the moment where i use the class. For this, i dont know when to call session_start() in order to fix the problem.
EDIT: i have discovered the "autoload" feature in php. Is for that no?. So my question is if it is possible to keep the autoload and start the session in the right moment
EDIT2: i found finally the solution. In qcodo forums suggested me several options. Finally i had to include the data classes before qcodo calls the session_start function. Thx to everyone

Strange behaviour migrating sessions with callbacks from php4 to php5

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().

Categories