6 Replies - 487 Views - Last Post: 23 September 2012 - 02:23 PM Rate Topic: -----

#1 e_i_pi  Icon User is offline

  • = -1
  • member icon

Reputation: 800
  • View blog
  • Posts: 1,686
  • Joined: 30-January 09

Return different object on instantiation

Posted 21 September 2012 - 08:38 PM

I'm currently building a new component in my framework where I can set a flag named LOG_METHOD_EXECUTION_TIMES and all the method calls across the application are logged to the DB. The overhead of this is reasonably large, but it's intended to be switched on/off for short intervals on production, and always be on in development. This will allow me to log all the method execution times in order to identify and isolate the methods that are taking too long to run.

The idea is that when the flag is set, object instantiation is via a Decorator pattern, which has no methods, but intercepts method calls using __call() and returns the output of the required method. In a nutshell, it looks like this:
class Decorator
{
	protected $obj;

	public function __construct($obj)
	{
		$this->obj = $obj;
	}

	public function __call($method_name, $args)
	{
		// Start the timer

		// Call the function to get the return value
		$returnValue = call_user_func_array(array($this->obj, $method_name), $args);

		// Stop the timer

		// Log results to the DB

		// Return the value
		return $returnValue;
	}

}



This works well, but the object has to be instantiated via the Decorator class, by using something like this:
if(LOG_METHOD_EXECUTION_TIMES == TRUE)
{
	$myObject = new Decorator(new TheActualObject);
} else {
	$myObject = new TheActualObject;
}


This could be further enhanced with a Factory pattern, by using code like this:
$myObject = Factory::Create(new TheActualObject);


...but I'd need to go through me entire codebase and change every class instantiation to use a Factory pattern method instead. I'd rather do something like this with a core class in the code that all other classes inherit from:
public function __construct()
{
	if(LOG_METHOD_EXECUTION_TIMES == TRUE)
	{
		$myObject = new Decorator(new TheActualObject);
	} else {
		$myObject = new TheActualObject;
	}
	return $myObject;
}


...but it seems that __construct() doesn't honour a return value, so that code doesn't actually work.

Does anyone know of any other way to do this, or will I need to go with the Factory pattern?

This post has been edited by e_i_pi: 21 September 2012 - 08:39 PM


Is This A Good Question/Topic? 0
  • +

Replies To: Return different object on instantiation

#2 CTphpnwb  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 3069
  • View blog
  • Posts: 10,750
  • Joined: 08-August 08

Re: Return different object on instantiation

Posted 21 September 2012 - 09:30 PM

It's late, and I'm tired, but to my blurry eyes this seems to work.
<?php
class Decorator
{
	protected $obj;

	public function __construct($obj)
	{
		$this->obj = $obj;
	}

	public function __call($method_name, $args)
	{
		// Start the timer

		// Call the function to get the return value
		$returnValue = call_user_func_array(array($this->obj, $method_name), $args);

		// Stop the timer

		// Log results to the DB

		// Return the value
		return $returnValue;
	}

}

class super_parent 
{
	public function __construct($obj)
	{
		if($obj == '') return '';
		
		if(LOG_METHOD_EXECUTION_TIMES == TRUE)
		{
			$myObject = new Decorator(new $obj(''));
		} else {
			$myObject = new $obj('');
		}
		return $myObject;
	}
	
	public static function build($obj)
	{
		if(LOG_METHOD_EXECUTION_TIMES == TRUE)
		{
			$myObject = new Decorator(new $obj);
		} else {
			$myObject = new $obj;
		}
		return $myObject;
	}

}

class some_child extends super_parent
{
	public function who_am_i() {
		echo "I am a child of super_parent!";
	}
}

define("LOG_METHOD_EXECUTION_TIMES", True);

$myobject="some_child";
$anobject = new some_child($myobject);
$anobject->who_am_i();


Was This Post Helpful? 1
  • +
  • -

#3 e_i_pi  Icon User is offline

  • = -1
  • member icon

Reputation: 800
  • View blog
  • Posts: 1,686
  • Joined: 30-January 09

Re: Return different object on instantiation

Posted 21 September 2012 - 11:50 PM

No that doesn't work, but it does demonstrate the issue I am talking about. Constructors do not return an object. The new keyword is the instantiating force, the return value of __construct() is disregarded. Take this code for example:
class Decorator
{
	protected $obj;

	public function __construct($obj)
	{
		$this->obj = $obj;
	}

	public function __call($method_name, $args)
	{
		// Start the timer

		// Call the function to get the return value
		$returnValue = call_user_func_array(array($this->obj, $method_name), $args);

		// Stop the timer

		// Log results to the DB

		// Return the value
		return $returnValue;
	}

	public function me()
	{
		return 'Decorator';
	}

}

class super_parent
{
	public function __construct($obj)
	{
		if($obj == '') return '';

		if(LOG_METHOD_EXECUTION_TIMES == TRUE)
		{
			$myObject = new Decorator(new $obj(''));
		} else {
			$myObject = new $obj('');
		}
		return $myObject;
	}

	public function me()
	{
		return 'SuperParent';
	}

}

class some_child extends super_parent
{
	public function who_am_i() {
		echo "I am a child of " . $this->me() . "!";
	}
}

define("LOG_METHOD_EXECUTION_TIMES", True);

$myobject="some_child";
$anobject = new some_child($myobject);
$anobject->who_am_i();



Since LOG_METHOD_EXECUTION_TIMES is true, the expectation is that the new object is of class Decorator, but it isn't. That's because the new keyword will always return an object of the type that is declared after the keyword. Which brings me back to the question, is there a way to achieve this without using the Factory pattern? I imagine there isn't, and I'll just have to implement a Factory pattern throughout my code, which isn't difficult, just time-consuming.

Thanks for the reply though... get some sleep CT ;)

This post has been edited by e_i_pi: 21 September 2012 - 11:50 PM

Was This Post Helpful? 0
  • +
  • -

#4 CTphpnwb  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 3069
  • View blog
  • Posts: 10,750
  • Joined: 08-August 08

Re: Return different object on instantiation

Posted 22 September 2012 - 06:02 AM

Ok, I'm awake now.

There are two problems, the first being that the constructor isn't returning the object. The second is that even if it did, you'd have a recursive issue where the object would be created over and over until you ran out of memory.

My solution is to pass by reference but test if the object has a parent:

<?php
class Decorator
{
	protected $obj;

	public function __construct(&$obj)
	{
		$this->obj = $obj;
	}

	public function __call($method_name, $args)
	{
		// Start the timer

		// Call the function to get the return value
		$returnValue = call_user_func_array(array($this->obj, $method_name), $args);

		// Stop the timer

		// Log results to the DB

		// Return the value
		return $returnValue;
	}

	public function who_am_i() {
		echo "I am a child of Decorator!<br>";
	}

}

class super_parent 
{
	protected $super = false;
	
	public function __construct(&$obj)
	{
		if(LOG_METHOD_EXECUTION_TIMES == TRUE)
		{
			if(!get_parent_class($this)) 
			{
				$obj = new Decorator(new $obj($obj));
			}
		} else {
			if(!get_parent_class($this)) 
			{
				$obj = new $obj($obj);
			}
		}
		$this->super = true;
	}

}

class some_child extends super_parent
{
	
	public function who_am_i() {
		echo "I am a child of super_parent!<br>";
	}
}

define("LOG_METHOD_EXECUTION_TIMES", True);

$myobject="some_child";

$testing = new super_parent($myobject);
$myobject->who_am_i();


Was This Post Helpful? 1
  • +
  • -

#5 e_i_pi  Icon User is offline

  • = -1
  • member icon

Reputation: 800
  • View blog
  • Posts: 1,686
  • Joined: 30-January 09

Re: Return different object on instantiation

Posted 22 September 2012 - 03:41 PM

Yeah that's essentially what I described in the OP. I was hoping maybe there was another way of going about it rather than use a Factory.
Was This Post Helpful? 0
  • +
  • -

#6 CTphpnwb  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 3069
  • View blog
  • Posts: 10,750
  • Joined: 08-August 08

Re: Return different object on instantiation

Posted 23 September 2012 - 07:29 AM

I'm a little confused. I thought that was the way you were trying to do it, the only difference being that I don't return an object. Instead I pass a class name by reference and change that into an object.
Was This Post Helpful? 1
  • +
  • -

#7 e_i_pi  Icon User is offline

  • = -1
  • member icon

Reputation: 800
  • View blog
  • Posts: 1,686
  • Joined: 30-January 09

Re: Return different object on instantiation

Posted 23 September 2012 - 02:23 PM

Oh, no lol. I was hoping there was some way to change a base class of my code so that I wouldn't have to go through the entire codebase and run every class instantiation through a Factory method instead. Considering there are likely hundreds of class instantiations done explicitly throughout my code, I was looking for a centralised method.

Thanks anyway, sorry that you thought I was struggling with deriving a Factory class.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1