[Typo3-dev] XCLASS on steroids or how to get rid of XCLASS with PHP5
Christian Jul Jensen
christian at jul.net
Fri Mar 25 11:46:34 CET 2005
Hi
This is maybe more A-team stuff as it is not likely to be realistic
soon, but I post it here.
The discussion about XCLASSes in the thread 'Welcome to the goto-age'
made me think what if you could provide somekind of mechanism to allow
a hook for every method call automatically.
So i made a "proof of concept" (see code below) of how this could be
achieved with PHP5
The idea is to have meta class for every instance, this class manmages
access to the the properties and methods of the class through an API
that identifies which method should actually be called. This way an
extension can register that it provides a specific method for a
class. SInce this is done by aggregation intead of inheritance, it
allows for different extension to overrule different parts of a class
independently (but of course the logic might clash)
Also, since the determination of which method to call is done at call
time, you can possibly define that an extension of some class should
only be done if some condition is met.
Another thing that this approach would solve are the extension of
non-instantiated base-classes like tslib_menu, this cannot be done by
XCLASS because the class is never instantiated only inerited from, so
you have to provide XCLASSes for every descendent of the class. With
this approach you can simply say something like 'if the class that
calls this method is a subclass of tslib_menu call the method in this
extension object'.
I haven't had this much fun with programming in a long time... :)
Here you go:
**** object-play.php ****
<?php
// this is just to have somekind of pool for objects, something more
// clever should be made
$objectPool = array('proofOfConcept' => null);
//this is the baseclass that makes classes able to work as an
//aggregate of a metaClass. All classes in the system should inherit
//from this one. Basically this replaces XCLASS statements in the
//bottom of class files
abstract class metaBaseClass {
private $_metaObject;
function __construct($metaObject) {
$this->_metaObject = $metaObject;
}
function __set($property,$value) {
$this->_metaObject->$property = $value;
}
function __get($property) {
return $this->_metaObject->$property;
}
function __call($method,$args) {
echo $method.' called';
}
}
//just a class to prove the concept, contains some methods.
class proofOfConceptMethodClass extends metaBaseClass {
function __call($method,$args) {
echo $method.' called';
}
function changeProperty($property,$value) {
$this->$property = $value;
}
}
//just a class to prove the concept, contains some properties
class proofOfConceptPropertyClass extends metaBaseClass {
public $someProperty;
}
// we all know this one...
class t3lib_div {
// this is the new makeInstance method, it creates a metaObject that
// calls the framework to invoke the actual logic
function makeInstance($className) {
$basicClassCode = '
class %s {
private $_secretKey;
function __construct() {
$this->_secretKey = t3lib_div::generateSecretKey();
}
function __call($method,$args) {
$methodObj = t3lib_div::getMethodObject(get_class($this),$method,$this);
return call_user_func_array(array($methodObj,$method),$args);
}
function __set($property,$value) {
$propertyObj = t3lib_div::getPropertyObject(get_class($this),$this->_secretKey,$property,$this);
$propertyObj->$property = $value;
}
function __get($property) {
$propertyObj = t3lib_div::getPropertyObject(get_class($this),$this->_secretKey,$property,$this);
return $propertyObj->$property;
}
}';
$classCode = sprintf($basicClassCode,$className);
eval($classCode);
return new $className;
}
//this is the method that returns an object containing the method
//that should be invoked. Note that this could be different objects
//depending on the method, allowing extensions to plugin an object
//that adds or overrides a method. Also since this is done at
//call-time it can be dynamically taking the current state of the
//system into account. This is basically creates a hook for every method.
function getMethodObject($className,$method,$metaObject) {
//do all kinds of logic here, you could even load a specific
//object depending on the class, that decides what to
//return.. Obviously a new object should not be instatiated
//everytime, but get fetched from some kind of object pool.
return new proofOfConceptMethodClass($metaObject);
}
//similar to getMethodObject, but since this returns the objects
//that contains the state of the object you are working on it needs
//to return any already instatiated object. To ensure more than one
//instance of a class, an identifier key is provided. Again note
//that the state can be held in more than one object, allowing
//extensions to add new internal variables to classes idenpendently
//of each other.
function getPropertyObject($className,$key,$method,$metaObject) {
//do all kinds of logic here, you could even load a specific
//object depending on the class, that decides what to return.
if(!is_object($GLOBALS['objectPool'][$key])) {
$GLOBALS['objectPool'][$key] = new proofOfConceptPropertyClass($metaObject);
}
return $GLOBALS['objectPool'][$key];
}
//this function generates a unique key for a metaObject to mark it's state objects.
function generateSecretKey() {
//generate special identifier to ensure unique instances of the class.
return 'proofOfConcept';
}
}
//play with it...
$obj = t3lib_div::makeInstance('tslib_menu');
$obj->writeMenu();
// set a property directly
$obj->someProperty = 'foo';
var_dump($obj->someProperty);
//change the property through a method, which is then located in
//another sub object.
$obj->changeProperty('someProperty','bar');
var_dump($obj->someProperty);
?>
**** object-play.php ****
--
-julle
More information about the TYPO3-dev
mailing list