PHP Destructor is not called - php

I'm Using PHP 5.4.12
I have two class
<?php
class MySessionHandler implements SessionHandlerInterface {
//..
}
$handler = new MySessionHandler();
session_set_save_handler($handler,true);
?>
And
<?php
class MySession {
//..
function __destruct() {
session_write_close();
echo 'called';
}
}
And with this code, MySession's destructor is never called.
<?php
require_once 'MySessionHandler.php';
include_once 'MySession.php';
$test = new MySession();
But with this code, "MySession" destructor is called ok
<?php
require_once 'MySessionHandler.php';
include_once 'MySession.php';
class Test {
function __construct() {
$test = new MySession();
}
}
$obj = new Test();
I have tested session_set_save_handler($handler,true/false).
I need MySession's destructor to be called 'cause I have issues with session_write_close(); I have to call it explicitly or the session never will be written to the server.
any workaround about this? I have simplified the code to their roots, but the __destruct method is not called when should be.

Dont depend on __destruct... From the manual:
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.

In your case above, you need to instantiate the object:
$test = new MySession();

by calling the __destruct() function itself
in my case __destruct() is not called when working in child file came from different file location.
I think __destruct() can only work on a single file page.

Related

Class instantiation causing functions within said class to be executed PHP

I have a registration class. The problem I'm facing is that the instantiation itself is somehow causing the functions within that class to be called.
I've tested this by adding an error_log() directly before and after the instantiation: $register = new Register(); every time I receive another error_log() which I placed inside the functions of the class that I'm instantiating.
How can I solve this?
EDIT this is what an example may look like:
testclass.php
class Test {
public function test() {
error_log("Function test() was run.'");
}
}
test.php
require_once("testclass.php");
$test = new Test();
It's because your function, test() has the same name of the class Test so PHP is using it as the constructor and calling it when you instantiate it.
class Bar {
public function Bar() {
// treated as constructor in PHP 5.3.0-5.3.2
// treated as regular method as of PHP 5.3.3
}
}
As the docs say:
Warning Old style constructors are DEPRECATED in PHP 7.0, and will be
removed in a future version. You should always use __construct() in
new code.

Using require and include from PHP class to load outside the class

I've been attempting to create a PHP loader class that'll take care of all of my directory issues. I've mostly gotten it to work, but it breaks when including global functions.
Here's what I'm using:
<?php
class Loader {
public function __construct() { ... }
private function check_if_file_exists($file) { ... }
public function load($file) {
$this->check_if_file_exists($file); //Throws fatal Exception if not a file
//The "important" stuff: works with global vars, not with global functions:
extract($GLOBALS, EXTR_REFS);
ob_start();
require_once "{$this->path}/$file";
return ob_get_clean();
}
}
This lets me do the following:
<?php
$loader = new Loader();
$loader->load('file.php'); //Class takes care of path stuff--don't worry about it
//This works:
print $variable_in_file_dot_php;
//This does NOT work:
function_in_file_dot_php();
How can I make it so that function_in_file_dot_php(); works?
Better you use AutoLoader class already available in php.Refer this url http://php.net/manual/en/language.oop5.autoload.php
i'm going to try to answer your question as a technical curiousity, but i strongly advise you not to do this.
Referring to the include/require documentation I see that variables defined inside included files will inherit the variable scope of the line that called require. In your case this will be the variable scope of Loader::load() method inside some instance of Loader class
Therefore $variable_in_file will not be available globally. unless you
define the $var before calling the include statement, thus giving it global scope.
declare it global inside your calling method (Loader::load())
You acomplish #2 with extract($GLOBALS...) however in order to do #1, you must have a priori knowledge of what is being included before you include it... invalidating your attempt at generalization with the Loader class.
function_in_file() however should be available in the global scope, I'd like to see your file.php and error message. Here's mine.
$cat foo.php
public function load($file) {
extract($GLOBALS,EXTR_REFS);
require_once $file;
}
}
$variable = 1;
$loader = new Loader();
$loader->load('file.php');
echo "\n" . $variable;
echo "\n" . method();
$cat file.php
<?php
function method() {
echo "hi i am a method";
}
outputs
$php foo.php
hello i am a variablehi i am a method
but seriously, don't do this. You seem to be trying to use includes() as a vector of code reuse, when it is mostly envisioned as a method for code separation. You are messing with phps' natural scoping in a hard to debug and unpredictable way. This is an anti-pattern.

Cannot call class's function in PHP

I have 4 classes. When someone goes to printHi.php, it prints "hi" twice--from different classes. However:
printHi.php
include('main.php');
$main = new Main;
main.php:
class Main {
function __construct() {
include('class2.php');
include('class3.php');
$this->class2 = new class2;
$this->class3 = new class3;
$this->class2->sanity();
}
}
class2.php
class class2 {
public function sanity() {
echo "Hi.";
}
}
class3.php
class class3 {
function __construct() {
$this->class2 = new class2;
$this->class2->sanity();
}
}
No ouput shows (or errors)? What am I doing wrong?
Also, if I wanted to use sanity() in all of my classes, how would I do that without doing
$this->class2 = new class2;
$this->class2->sanity();
in every class?
http://pastebin.com/HHyQfvhW
Errors are being thrown. You might have error_reporting turned off and be seeing a blank screen, but they are being raised. Here's a list of errors from what I can see:
Class3's constructor is missing the function declaration. This should be a fatal parse error
function __construct() {
Class1's constructor tries to call the method sanity() on the non-object $this->class. This should be a fatal error.
So, obviously this isn't your actual code. Assuming that you're just talking about making Class2 available to all your classes, I'd suggest Dependency Injection. So:
class Main {
public function __construct(class2 $class2, Class3 $class3) {
$this->class2 = $class2;
$this->class3 = $class3;
$this->class2->sanity();
}
}
class Class2 {
public function sanity() {...}
}
class Class3 {
public function __construct(Class2 $class2) {
$this->class2 = $class2;
}
}
That way, everything is passed in. It's far more flexible, easier to understand and debug, and far easier to test.
Edit: Based upon the linked code:
There are a few issues.
Inject your dependencies. Don't just create new instances of classes everywhere (hardcoding relationships)
Indent your code properly. Readability is king. always indent.
require() or die() is pointless. require will end execution for you if it fails. the or die bit is redundent.
The sanity() method on Config is declared as static, yet you're trying to call it on an instance. Figure out if it's tied to an instance (needs to use $this) or not, and make it appropriately. Then only call it appropriately. Don't call Foo::bar() if bar is an instance method and vise versa.
Your todo is wrong, since require 'foo' or die() is working how it should. OR has the higher precidence, so that's why you get require 1 since it's interpreted as require ('foo' or die())...
Finally, don't use require blindly like this. Instead, either autoload you classes, or use require_once in case a file was already required (to prevent errors).
You are not seeing errors likely because
class class3 {
__construct() {
$this->class2 = new class2;
$this->class2->sanity();
}
}
contains a parse error. Namely, you need to write function __construct(). Because of this, methods to turn on errors such as error_reporting and ini_set will not work because the script never runs due to the parse error. Therefore, look to your php.ini file and set the error_reporting and display_errors directives there. After having done that, you should see your error messages.

php destructor behaviour

im trying to understand php constructor and destructor behaviour. Everything goes as expected with the constructor but i am having trouble getting the destructor to fire implicitly. Ive done all the reading on php.net and related sites, but i cant find an answer to this question.
If i have a simple class, something like:
class test{
public function __construct(){
print "contructing<br>";
}
public function __destruct(){
print "destroying<br>";
}
}
and i call it with something like:
$t = new test;
it prints the constructor message. However, i'd expect that when the scripts ends and the page is rendered that the destructor should fire. Of course it doesnt.
If i call unset($t); when the scripts ends, of course the destructor fires, but is there a way to get it to fire implicitly?
This is pretty easy to test.
<?php
class DestructTestDummy {
protected $name;
function __construct($name) {
echo "Constructing $name\n";
$this->name = $name;
}
function __destruct() {
echo "Destructing $this->name\n";
//exit;
}
}
echo "Start script\n";
register_shutdown_function(function() {
echo "Shutdown function\n";
//exit
});
$a = new DestructTestDummy("Mr. Unset");
$b = new DestructTestDummy("Terminator 1");
$c = new DestructTestDummy("Terminator 2");
echo "Before unset\n";
unset($a);
echo "After unset\n";
echo "Before func\n";
call_user_func(function() {
$c = new DestructTestDummy("Mrs. Scopee");
});
echo "After func\n";
$b->__destruct();
exit("Exiting\n");
In PHP 5.5.12 this prints:
Start script
Constructing Mr. Unset
Constructing Terminator 1
Constructing Terminator 2
Before unset
Destructing Mr. Unset
After unset
Before func
Constructing Mrs. Scopee
Destructing Mrs. Scopee
After func
Destructing Terminator 1
Exiting
Shutdown function
Destructing Terminator 2
Destructing Terminator 1
So we can see that the destructor is called when we explicitly unset the object, when it goes out of scope, and when the script ends.
The __destruct() magic function is executed when the object is deleted/destroyed (using unset). It is not called during shutdown of a script. When a PHP script finishes executing, it cleans up the memory, but it doesn't 'delete' objects as such, thus the __destruct() methods aren't called.
You may be thinking of the register_shutdown_function(), which is fired when your PHP script finishes executing.
function shutdown()
{
// code here
echo 'this will be called last';
}
register_shutdown_function('shutdown');
My understanding is that destructors are automatically called for any remaining objects when the script ends.
Looking though the manual page on constructors and destructors, it seems the only way to bypass destructors entirely is if you call exit() from the destructor of an object that is destroyed prior to the object in question.
Are you using exit() in any of your destructors? Are there even multiple objects in your script?
If it's not too much trouble, perhaps you could post the actual code in question rather than the sample code you have in your question now. Aside from the typo in your sample constructor, that code should call both the constuctor and destructor for your test object.
The __destruct method of a class is called once all references to the object are unset.
For example
$dummy = (object) new Class();
The destructor is automatically called if the dummy object is set to null or the script is exited.
unset($dummy); // or $dummy = null;
//exit(); //also possible
However, there are three crucial memory notes to calling the destructor method:
Firstly, the desctructor method should be a public method, not protected or private.
Secondly, refrain from using internal and circular references. For example:
class NewDemo
{
function __construct()
{
$this->foo = $this;
}
function __destruct()
{
// this method will never be called
// and cause memory leaks
// unset will not clear the internal reference
}
}
The following will not work either:
$a = new Class();
$b = new Class();
$a->pointer = $b;
$b->pointer = $a;
unset($a); // will not call destructor
unset($b); // will not call destructor
Thirdly, deciding whether destructors are called after output is sent. Using
gc_collect_cycles()
one can determine whether all destructors are called before sending data to user.
See http://php.net/manual/en/language.oop5.decon.php for the sources and elaborate explanations of magic destruct methods with examples.

How can I initiate a PHP class and use it in several files?

I am stumped right now. In my last post about this question the answer was to use a singleton to make sure an object is only initiated 1 time but I am having the opposite problem.
If I have a file called index.php and then I include these files into it, class1.php, class2.php, class3.php, class4.php.
In index.php I will have,
<?PHP
$session = new Session();
require_once '/includes/class1php';
require_once '/includes/class2.php';
require_once '/includes/class3.php';
require_once '/includes/class4.php';
?>
then in all 4 of the test files I will try to access a method called get() from the session class, assume the session class file is already included into the index.php page as well.
Now if I try to use...
$testvar = $session->get($var1);
in any of the test class files I will get this error
Fatal error: Call to a member function get() on a non-object
the only way the code works without an error is if I use
$session = new Session();
in every file.
How can I fix/avoid having to initaite the class in every file when it is already initated in the index.php file?
the goal is to let me initiate a class in 1 file like index.php and then include the class files into that page, the catch is most of the classes use methods from other classes so would be nice if I didn't have to initiate every class in every file
Without seeing the code it's hard to tell, but I think I can make some assumptions. correct me if I'm wrong:
EDIT: So post your source so we can stop speculating
1) The files you are including are class files. in other words, they contain something like:
class a
{
function a(){}
function b()
{
}
}
2) You aren't trying to execute code in the class files, at load time, but at some later time by instantiating them
i.e.
require("class.a.php");
$myA = new a();
$a->b();
If you are trying to reference your session variable inside those classes, then you have a scope issue. A variable declared outside a class definition can't be used inside the class, unless it is declared as a global var inside the class.
class a
{
function a(){}
function willFail()
{
$session->doSomething(); //fails
}
function b()
{
global $session;
$session->doSomething(); //succeeds
}
}
Even then, you probably don't want to do that, but instead you should pass in your session as a variable if the class needs access to it:
class a
{
function a(){}
function b($session)
{
$session->doSomething(); // yay!
}
}
You could have a base class they all all extend from
Example
class test1 extends Base {
public function doSomething() {
$this->session->get('something');
}
}
class Base {
protected session;
public function __construct() {
$this->session = new Session();
}
}
You're kind of thinking about it backwards. Any file that will use the session object will need to include the file containing that class definition. The alternative is to use __autoload to pull the class in:
function __autoload($classname)
{
if ($classname == 'Session')
{
include_once 'Session.php';
}
}
EDIT : you'll need to put the file containing that autoload into every file that will use it.

Categories