Jump to content

Class __destruct issue


Guest Luke

Recommended Posts

Posted

After reading Matts blog entry this morning about misbehaving destructors in PHP 5.2.0 and earlier and hearing about his solution, I was motivated in attempting to find a more transparent solution. After all, I will have to do this after I completely migrate to PHP 5.

It took me a little while to figure out, but I managed to write a piece of code that corrects the destructor order no matter what version you're currently using (whether it be before or after PHP 5.2.0).

All you need to do is include the "core" class, extend all other classes with the core class, and change "_ _ construct" and "_ _ destruct" to "_ _ _ construct" and "_ _ _ destruct" (this gives the core class complete control over the construct and destruct routines).

In essence it uses static array to keep track of references to all the classes in the order they were constructed. On every destruct another numerical static value is incremented. Every destruct but the last is ignored. On the last destruct is reverses the array, and calls the destruct functions in the right order. It always works because it doesn't matter which destruct is last, and the classes are always added to the array in the order they were created.

<?php


class class1 extends core {

	function ___construct() {

		print 'C -> ' . get_class() . "<br />";

	}


	function ___destruct() {

		print 'D -> ' . get_class() . "<br />";

	}

}


class class2 extends core {

	function ___construct() {

		print 'C -> ' . get_class() . "<br />";

	}


	function ___destruct() {

		print 'D -> ' . get_class() . "<br />";

	}

}


class class3 extends core {

	function ___construct() {

		print 'C -> ' . get_class() . "<br />";

	}


	function ___destruct() {

		print 'D -> ' . get_class() . "<br />";

	}

}


class core {

	static $__consta = array();

	static $__destructed = 0;


	function __construct() {

		core::$__consta[] = &$this;

		$this->___construct();

	}


	function __destruct() {

		core::$__destructed++;

		if( ($cc = count(core::$__consta)) == core::$__destructed ) {

			core::$__consta = array_reverse(core::$__consta);

			for ($i=0; $i < $cc; $i++) core::$__consta[$i]->___destruct();

		}

	}

}


$c1 = new class1();

$c2 = new class2();

$c3 = new class3();


?>

If you don't have PHP 5.2.0 or earlier, you can confirm this by changing "_ _ destruct" in the core class to "o _ _ destruct" and add this to the bottom:

register_shutdown_function(array(&$c1, 'o__destruct'));

register_shutdown_function(array(&$c2, 'o__destruct'));

register_shutdown_function(array(&$c3, 'o__destruct'));



So, what do ya think :D?

  • Management
Posted

It's a very interesting exercise but is it portable enough to be used in a real application? To make it work for PHP 5.2.0 or less you need to make a code edit? That's not something we can present to our customers. Even if you dynamically check the PHP version you're just making a lot more work for yourself than is needed when register_shutdown_function() is simpler, robust and (hopefully) version proof.

Nice solution, otherwise although I'm not entirely sure it'd work.

Posted

It would be a nice solution, but it would be a bit of a pain in the arse if you want to use extends for its actual purpose. Also, surely a bigger problem will arise as a result? You may be calling the ___destruct() methods in the right order... but you're effectively doing it in the destruct method of a different object. So surely PHP is doing the following:

$Class1->__construct();

// $Class1 = Object Class1

// $Class2 = null

// $Class3 = null

$Class2->__construct();

// $Class1 = Object Class1

// $Class2 = Object Class2

// $Class3 = null

$Class3->__construct();

// $Class1 = Object Class1

// $Class2 = Object Class2

// $Class3 = Object Class3

$Class1->__destruct();

$Class3->___destruct();

// $Class1 = null

// $Class2 = Object Class2 

// $Class3 = Object Class3 (Essentially disabled as it has had its own destructor called)

$Class2->__destruct();

$Class2->___destruct();

// $Class1 = null

// $Class2 = null

// $Class3 = Object Class3 (Essentially disabled as it has had its own destructor called)

$Class3->__destruct();

$Class1->___destruct();

// $Class1 = null

// $Class2 = null 

// $Class3 = null

// Also some kind of null-object fatal error/warning around calling $Class1->___destruct()



Happy to be proved wrong, but since __destruct() is called immediately prior to destroying the object within PHP itself, I can't see how this would work. :s

That being said, I also don't see why you offered up a different solution for below PHP 5.2? From what I can see, there's nothing there that isn't in PHP 5.0.

Posted

Test it for yourself. It does run the destruct method in the proper classes. The only thing you have to do is extend the classes you want corrected to core. It's no different than doing it manually through register_shutdown_function. The difference is you don't have to make sure that you're calling "register_shutdown_function" in the right order. This class does all that work for you.

As long as you use _ _ _ construct and _ _ _ destruct instead of _ _ construct and _ _ destruct in your classes, it works just fine.

Here's another example using another classes to extend the class that's extended by core:

class core {

	static $__consta = array();

	static $__destructed = 0;


	function __construct() {

		core::$__consta[] = &$this;

		$this->___construct();

	}


	function __destruct() {

		core::$__destructed++;

		if( ($cc = count(core::$__consta)) == core::$__destructed ) {

			core::$__consta = array_reverse(core::$__consta);

			for ($i=0; $i < $cc; $i++) core::$__consta[$i]->___destruct();

		}

	}

}


class class1 extends core {

	function ___construct() {

		print 'C -> ' . get_class() . "<br />";

	}


	function ___destruct() {

		print 'D -> ' . get_class() . "<br />";

	}

}


class class2 extends core {

	function ___construct() {

		print 'C -> ' . get_class() . "<br />";

	}


	function ___destruct() {

		print 'D -> ' . get_class() . "<br />";

	}

}


class class3 extends core {

	function ___construct() {

		print 'C -> ' . get_class() . "<br />";

	}


	function ___destruct() {

		print 'D -> ' . get_class() . "<br />";

	}

}


class class1a extends class1 {

	function ___construct() {

		print 'C -> ' . get_class() . "<br />";

	}


	function ___destruct() {

		print 'D -> ' . get_class() . "<br />";

	}

}


$c1 = new class1a();

$c2 = new class2();

$c3 = new class3();



Compare the results to using normal _ _ construct and _ _ destruct without extending core. The results are the same. The difference is PHP 5.2.0 and earlier no longer destruct in the incorrect order. This will always make classes destruct in the proper order (reverse order of how they were initiated).

Test it for yourself. I encourage you to find holes in this method. If there are, I'd certainly like to know about them before I put this to use.

Archived

This topic is now archived and is closed to further replies.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...