Page 1 of 1

MasterProxyClass - Add speed to your apps. - Please Review

Posted: Fri Jan 05, 2007 8:31 pm
by DaveTheAve
Alright, before I publicly release this code (Well more then this post) I wish to get some feedback on this class and hopefully fix it up a tad.

Basically, I was reading about the PHP Proxy Design Pattern on PHPDeveloper.org and I thought Why are they creating a proxy class for EVERY class? Isn't that wasting time? Thus this is my first attempt at creating the universal proxy class for your classes; this should speed up some of your applications!

NOTE: Please check your version number with the version number below to make sure you have the latest version; I'm constantly updating the script in this post.

Code: Select all

<?php
/**
* Proxy Class for all classes
* 
* The proxy pattern was born out of the need for speed by avoiding unnecessary class 
* initiation, initiating the class only when its functionality is required. To achieve 
* this you needed to create a proxy pattern class for every one of your classes; not 
* any more, MasterProxyClass assures us of this. MasterProxyClass was developed to save 
* time and effort in creating a new proxy pattern class for every class in your project;
* it even comes with ease of use to boot.
* 
* @name		Master Proxy Class
* @author 	David Branco <David@NeoeliteUSA.com>
* @link		http://www.NeoeliteUSA.com/
* @version 	0.7.6
* @since	Jan 04, 2007
* @license	http://creativecommons.org/licenses/by/2.5/
*/
class masterProxyClass {

	/**
	* Holds class file location
	*/
	private $_classFile = null;

	/**
	* Holds class name
	*/
	private $_className = null;

	/**
	* Holds class arguemnts for call
	*/
	private $_classArgs = null;

	/**
	* Holds loaded class
	*/
	private $_class = null;

	/**
	* Sets the class to initiate and the file it can be found in.
	* Any other variables passed will be sent to the class created.
	*
	* Example:
	*    If you wanted: 
	*         $Tank = new Phishtank('MyAppKey','MySharedSecret','MyUsername', 
	*             'MyAPIKey');
	*
	*    You would call it by:
	*         $TankProxy = new masterProxyClas('Phishtank','./phishtank.class.php',
	*             'MyAppKey','MySharedSecret','MyUsername', 'MyAPIKey');
	*
	* @return  void
	*/
	public function __construct($className, $classFile, array $classArgs = array())
	{
		$this->_className = $className;
		$this->_classFile = $classFile;
		$this->_classArgs = $classArgs;
	}
	
	/**
	* Sends varible reading over to the real varibles wanted.
	*
	* @return  mixed
	*/
	public function __get($variable)
	{
		$this->___createClass();
		return $this->_class->$variable;
	}
	
	/**
	* Sends varible writing over to the real varibles wanted.
	*
	* @return  void
	*/
	public function __set($variable, $value)
	{
		$this->___createClass();
		$this->_class->$variable = $value;
	}
	
	/**
	* Sends the isset function over to the real varibles wanted.
	*
	* @return  bolean
	*/
	public function __isset($variable)
	{
		$this->___createClass();
		return isset($this->_class->$variable);
	}
	
	/**
	* Sends the unset function over to the real varibles wanted.
	*
	* @return  void
	*/
	public function __unset($variable)
	{
		$this->___createClass();
		unset($this->_class->$variable);
	}

	/**
	* Redirects all functions called over to the real functions wanted.
	*
	* @return  mixed
	*/
	public function __call($methodName, $args)
	{
		$this->___createClass();
		return call_user_func_array(array(&$this->_class, $methodName), $args);
	}
	
	/**
	* Checks to see if the proxied class has been created. If not
	* the class is created.
	*
	* Thank you, ole for the ReflectionClass idea.
	*
	* @return  void
	*/
	private function ___createClass() {
		if($this->_class == null){
			require_once($this->_classFile);
			$rc = new ReflectionClass($this->_className);
			$this->_class = $rc->newInstanceArgs($this->_classArgs);
		}
	}
}
To test this class I added this block of code to the bottom of the file: (Note masterProxyClass's filename MUST be 'masterProxyClass.php')

Code: Select all

class testProxy {
	
	public function __construct($one = 'blank', $two = 'blank') {
		echo 'Var 1 = ' . $one . '<br />';
		echo 'Var 2 = ' . $two . '<br /><br />';
	}
	
	public function kill($who) {
		echo 'You killed ' . $who . '! <br /><br />';
	}
	
	public function create($who, $mother, $father) {
		echo 'You created ' . $who . '! <br />';
		echo 'Mother: ' . $mother . '<br />';
		echo 'Father: ' . $father . '<br /><br />';
	}
}

$masterProxy = new masterProxyClass('testProxy', './masterProxyClass.php', array('Value One Works','Value Two Works'));

$masterProxy->create('Kenny','Daisy','Bob');

$masterProxy->kill('Kenny');
Any feedback would be great; even to say you hate me and I should hang myself via my USB cable from a oil rig wearing a pink-stripped bowl on my head.

Posted: Fri Jan 05, 2007 11:03 pm
by nickvd
I am by no way an OOP master, or pattern identifier, but that looks like a over complicated registry to me.

I read that article you mentioned, and I just can't see how using this pattern would be any faster than just directly calling the object, or using a lazy-loading registry ($reg->xmlproc->getrootnode())

Also, what would happen if two or different classes have a method the same (think getter's and setter's) $proxy->get('name');... what class would receive the call to get()?

Posted: Sat Jan 06, 2007 12:39 am
by Christopher
This is an interesting subject because PHP provides a number of ways to deal with the problem(s) that this class solves. It is essentially a PHP5 update of the Invoker class that lastcraft posted a while back. I believe the actual name for this class is Dynamic Linkage, which is a specific type of proxy.

I use something like this regularly to deal with the Lazy Loading that is not handled by the Front Controller. It is handy for forwarding and for the states of a state machine such as in an Application Controller. I'd be interested to know specifically how you have used it?

Posted: Sat Jan 06, 2007 5:31 am
by Ollie Saunders
Looks interesting. I recommend that you use a prototype like this for your constructor, will improve performance and readability.

Code: Select all

public function __construct($className, $classFile, array $parameters = array())
Note that in PHP 5.1.4 you can use reflection to accomplish this:

Code: Select all

eval('$this->_class = new ' . "$this->_className" . '(' . "$this->_classArgs" . ');');

Code: Select all

class ReflectionClass
{
    public stdclass newInstanceArgs(array args);
}

Posted: Sat Jan 06, 2007 9:44 am
by DaveTheAve
NOTE: I have UPDATED the class code in the first post to include ole's prototype idea; though it does not help much if I can't figure out the refraction idea of his.

For everyone's enjoyment and curiousity, here is the article that provided me with this idea: An Introduction to Building Proxy Classes with PHP 5.

Now to answer replies:
nickvd wrote: I am by no way an OOP master, or pattern identifier, but that looks like a over complicated registry to me.

I read that article you mentioned, and I just can't see how using this pattern would be any faster than just directly calling the object, or using a lazy-loading registry ($reg->xmlproc->getrootnode())
Yea, that was the idea behind it; it’s basically a registry for a single object that will only initiate the object IF it needs to be, essentially saving performance from unneeded includes/requires.
nickvd wrote: Also, what would happen if two or different classes have a method the same (think getter's and setter's) $proxy->get('name');... what class would receive the call to get()?
Might wanna read up on your "Overloading" for PHP5; don't worry 'bout it though, we all had too (the PHP5 OOP users). However, to answer your question, and a review to our OOP programmers, the __set and __get functions, as well as __isset and __unset, are forwarded to the correct class. The ONLY function I believe that would interfere with the WANTED (non-proxy) class would be ___createClass(). (Note thats three underscores before function name)
Question: Should I get rid of this and include it in all four places were it is needed to remove this small limitation?
arborint wrote: This is an interesting subject because PHP provides a number of ways to deal with the problem(s) that this class solves. It is essentially a PHP5 update of the Invoker class that lastcraft posted a while back. I believe the actual name for this class is Dynamic Linkage, which is a specific type of proxy.
I'm sorry I know nothing of these solutions that PHP provides; do you mind sharing them with me?
arborint wrote: I'd be interested to know specifically how you have used it?
Currently, I have not used this code, I JUST created it and posted it lol; however, I will be using it for EVERY class in my projects. Well except for a couple of classes such as a visitor tracker that gets run EVERY page-load; I'm hoping this will add extra performance where unnecessary files are loaded.
ole wrote: Looks interesting. I recommend that you use a prototype like this for your constructor, will improve performance and readability.
WOW! Boy did you just make me feel like a ID-10-T. I'm really surprised I didn't think of that and to make matters worse, I completely forgot what a function prototype was. lol Thank you, its fixed now.
ole wrote: Note that in PHP 5.1.4 you can use reflection to accomplish this:
I'm sorry I have no clue what you mean by this; thus I have not fixed this in my current release. Don't fret, I'm looking it up via the link you sent me and the book PHP 5 Objects, Patterns, and Practice.

Posted: Sat Jan 06, 2007 10:41 am
by DaveTheAve
Thank YOU ole!!! I was able to successfully implement your ReflectionClass idea; for thanks, I added a little tribute to ya in the function description.

Now i'm just stuck with this question in terms of functionality:
Should I get rid of ___createClass() and include it in all four places were it is needed to remove the small limitation of the prioxied class not being able to use this function name?

Edit: Apparently I seem to have miscalculated there are five limitations, the one noted above and the variables used in the class; i'll see if I can get around this limitation soon. If you have any idea please feel free to post.

Posted: Sat Jan 06, 2007 12:14 pm
by Christopher
DaveTheAve wrote:I'm sorry I know nothing of these solutions that PHP provides; do you mind sharing them with me?
It sounds like you are using it for Lazy Load of classes/objects. I would think that Front Controller and __autoload() are the most common ways to do Lazy Load. Those are usually used for primary Lazy Load, whereas Dynamic Linkage is a secondary method.

Posted: Sat Jan 06, 2007 12:20 pm
by DaveTheAve
A proxy method is not for loading dependences, its made so you don't load performance-hungry classes that don't need to be loaded.

Posted: Sat Jan 06, 2007 12:23 pm
by feyd
DaveTheAve wrote:A proxy method is not for loading dependences, its made so you don't load performance-hungry classes that don't need to be loaded.
That sounds like Lazy Loading.

Posted: Sun Jan 07, 2007 12:48 pm
by Ollie Saunders
Yeah __autoload is probably the best way to go about doing this because that essentially provides class overloading which is exactly what you are doing here.
DaveTheAve, be careful with writing this kind of stuff that you don't have a specific reason to do so, often writing this kind of stuff will further your knowledge of the language but they can end up being a bit of a waste of time and add more complexity to your code.

I spent forever writing this crazy piece of code that allowed you to create classes dynamically:

Code: Select all

class SuperObject
{
    function __addProperty($name, $value = null);
    function __addMethod($name, $parms, $code);
    function __removeProperty($name);
    function __removeMethod($name);
    function __call($name, $params);
    function __set($name, $value);
    function __get($name);
}
If you tried to use a property that you hadn't created with __addProperty() it would throw an exception. Lots of create_function() was used to make the dynamic method stuff possible. I could have even wrote in method overloading so that you could add two methods with the same name and a different params list.

After writing all this I couldn't bring myself to use it because of the performance issues I know it would create. I think I was trying to turn PHP into JS :D

I wrote another one that implemented ArrayAccess and Iterator and restricted arrays to numeric or associative indexes only (chosen at construction). That was also a bit of a mental idea too.