Generic PHP object wrapper for resources

Posted: March 7th, 2011 | Author: | Filed under: PHP | Tags: , , | No Comments »

I hate working with PHP’s resources. I hate the long function names, I hate repeating the function names prefix, I hate having to pass around the resource variable. It looks like hack that was used instead of real object and classes and it feels dirty compared to OOP code. I personally think it should’ve been replaced with real classes and objects a long time ago. </whining>

So, I thought on a simple way to wrap it with a class that lets you work with those resources as-if they were real objects. Basically, its a simple class that wraps the resource, configured with the functions prefix and has some __call() magic.

For example, when working with GD, that code:

$image = imagecreate(150, 50);
$bg = imagecolorallocate($image, 255, 255, 255);
$red = imagecolorallocate($image, 255, 0, 0);
imagestring($image, 2, 2, 2, 'Hello, world!', $red);
imagejpeg($image);

Can be written as:

$image = new ResourceWrapper('image', 'create', 150, 50);
$bg = $image->colorAllocate(255, 255, 255);
$red = $image->colorAllocate(255, 0, 0);
$image->string(2, 2, 2, 'Hello, world!', $red);
$image->jpeg();

The ResourceWrapper class constructor takes the function names prefix as its first argument (e.g. ‘image’ for GD, ‘mysql_’ for mysql, ‘bz’ for bzip, etc) and the function used to create the resource as the second argument (without the prefix). The rest of the argument are passed untouched to that method. In that example, it’ll call imagecreate(150, 150) and store the resource as a property on the object.

Than, every call on that object is handled using __call() magic. The function prefix is added to method that was called, the stored resource is passed along all the other arguments and the return value from that function is returned.

The resource is passed, by default, as the first argument to the function calls. In some cases, like mysql, it should be passed last. The can be changed using $object->_setPosition(ResourceWrapper::POS_LAST).

This is the code for the ResourceWrapper class:

/**
 * A wrapper class for PHP resourcse.
 * 
 * Used for wrapping PHP resources in an object, and working with them in an
 * Object-Oriented manner.
 * 
 * Some methods are prefixed with `_` even though they're public to avoid
 * clashes with resource-related functions.
 * 
 * @author Shesek <th[e]@RemoveMe.shesek.inf[o]>
 * @license http://creativecommons.org/licenses/by/3.0/ CC-BY 3.0
 * @see http://www.shesek.info/php/generic-php-object-wrapper-for-resources
 */

class ResourceWrapper {
	/**
	 * POS_FIRST and POS_LAST are used to set the correct position of the
	 * resource argument on function calls. 
	 */
	const POS_FIRST = 1;
	const POS_LAST = 2;

	/**
	 * Holds the PHP resource being wrapped 
	 * 
	 * @var resource
	 */
	protected $resource;
	
	/**
	 * The prefix for functions related to this resource
	 * 
	 * e.g. 'image' for GD, 'mysql_' for mysql, 'bz' for bzip, etc
	 * 
	 * @var string
	 */
	protected $prefix;
	
	/**
	 * The position of the resource argument on function calls
	 * 
	 * Either ResourceWrapper::POS_FIRST or ResourceWrapper::POS_LAST
	 * 
	 * @var integer
	 */
	protected $resource_position = 1;

	/**
	 * The constructor creates the PHP resource and stores it locally on the object.
	 * 
	 * The $prefix and $method arguments are used to determine the function name,
	 * and the rest of the arguments are passed untouced to it.
	 * 
	 * For example, passing 'mysql_' as the $prefix and 'connect' as the $method
	 * would cause it to call mysql_connect(), with the other parameters.
	 * 
	 * new ResourceWrapper('mysql_', 'connect', 'localhost', 'nadav')
	 * is translated to mysql_connect('localhost', 'nadav')
	 * 
	 * @param string $prefix
	 * @param string $method
	 */
	public function __construct($prefix, $method) {
		$this->prefix = $prefix;
		$args = func_get_args();
		$this->resource = $this->__call($method, array_slice($args, 2));
	}

	/**
	 * Delegates calls to the original PHP functions while passing the resource.
	 * 
	 * Adds the function prefix (as defined in the constructor) to the method name
	 * being called, passes the resource along with the other arguments, invokes
	 * the original function and return the return value from it. 
	 * 
	 * @param string $method
	 * @param array $args
	 * @return mixed
	 */
	public function __call($method, $args) {
		if (!is_null($this->resource)) {
			if ($this->resource_position === self::POS_FIRST) {
				array_unshift($args, $this->resource);
			}
			else {
				array_push($args, $this->resource);
			}
		}
		return call_user_func_array($this->prefix . $method, $args);
	}

	/**
	 * Set wheter the resource should be passed last or first in the arguments.
	 * 
	 * @param	integer	$position	ResourceWrapper::POS_FIRST or ResourceWrapper::POS_LAST
	 */
	public function _setPosition($position) {
		$this->resource_position = $position;
		return $this;
	}

	/**
	 * Returns the wrapped PHP resource.
	 * 
	 * @return resource
	 */
	public function _getResource() {
		return $this->resource;
	}

	/**
	 * Magic toString conversion
	 */
	public function __toString() {
		return 'ResourceWrapper for '.get_resource_type($this->resource);
	}
}

That’s about it. Its very simple – but makes the code more organized and look nicer. In my opinion, it feels much more natural that way.

You can also download it here. Released under CC-BY 3.0.



Leave a Reply