Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

PHP interface chain method

Junior Spellweaver
Joined
Nov 26, 2008
Messages
196
Reaction score
62
Class chaining method is a useful technique when writing classes that have lots of methods that store data within their class. Builders such as the make great use of this technique. If you are unfamiliar with Chaining, check out the .

The following code demonstrates the Chainable Interface, which can be used to contract objects who implement it to build methods consistent with the chaining philosophy.

First, we start with the Interface, which instructs the implementor to use the PHP magic method __call(). Place the following code in chainable.php.

PHP:
<?php

interface Chainable
{
	/**
	 * Overload method calls.
	 */
	public function __call($name, array $arguments);
}

Next, we build our Chain class and implement Chainable. Let's build our __call() method to prefix calls to nonexistent functions with 'chain_'. This allows us to create chain methods that the user can call without even knowing the difference. In this way, we can encapsulate the functionality and the user is blind and happy!

PHP:
<?php

class Chain implements Chainable
{
	/**
	 * Descendants of this class must use `chain_` prefix for all
	 * methods they wish to make chainable. The method can then be
	 * called by its base name.
	 * 
	 * Return values from called methods are lost.
	 * 
	 * @param string $name name of the method called
	 * @param array $arguments array of arguments supplied to method
	 * @return PlainChain $this instance of this class
	 */
	public function __call($name, array $arguments)
	{
		$method = "chain_{$name}";		
		call_user_func_array(array($this, $method), $arguments);
		
		return $this;
	}
}

Save this file as chain.php.

Create the following Test Class in test.php.

PHP:
<?php

class Test extends Chain
{	
	/**
	 * Name functions according to Parent __call requirements to have
	 * them automagically return $this.
	 */	
	public function chain_hello()
	{
		print 'Hello';
		
		return TRUE;
	}
	
	public function make_array()
	{
		return array('foo' => 'bar', 'baz' => 'faz');		
	}
}

Notice that we have one method using the 'chain_' prefix, chain_hello(). When we call hello(), the __call() magic method will kick in and call chain_hello(), which is what we really want. After that, it will return the $this reference to allow us to continue chaining additional method calls. Also note that we have left the make_array( method intact. We can call this method normally, although, it cannot be chained.

Let's test our code with sample_1.php:

PHP:
<?php

//get dependencies
require('chainable.php');
require('chain.php');
require('test.php');

$test = new Test;

//execute a chain
$test
->hello()
->hello()
->make_array();

Let's suppose we wanted to make our make_array() method chainable as well. The only problem we'll encounter, is that this method returns an array. By returning $this we will kill its functionality entirely! We need a way to store the original return values from methods and still allow them to be chained.

Let's put together another contract with the ReturnStackable Interface:

PHP:
<?php

interface ReturnStackable
{	
	/**
	 * A way to get the return stack values
	 */
	public function return_stack($key = NULL);
}

Now, if we extend our Chain class to implement ReturnStackable in addition to Chainable, we end up with the following:

PHP:
<?php

class Chain implements Chainable, ReturnStackable
{
	/**
	 * Keeps track of original return values that would otherwise be
	 * lost when returning $this
	 */
	private $return_stack = array();
	
	/**
	 * Descendants of this class must use `chain_` prefix for all
	 * methods they wish to make chainable. The method can then be
	 * called by its base name.
	 * 
	 * Stores returns from methods for later retrieval by contract of
	 * ReturnStackable.
	 * 
	 * @param string $name name of the method called
	 * @param array $arguments array of arguments supplied to method
	 * @return Chain $this instance of this class
	 */
	public function __call($name, array $arguments)
	{
		$method = "chain_{$name}";		
		$this->return_stack[$name][] = call_user_func_array
		(
			array($this, $method), 
			$arguments
		);
		
		return $this;
	}
	
	/**
	 * Return array stack of original return values generated by
	 * methods that were lost due to $this being returned instead.
	 * 
	 * For a particular key, the return values for all calls to that
	 * method will be returned in array format in ascending 
	 * chronological order.
	 */
	public function return_stack($key = NULL)
	{
		if (isset($key))
		{
			return $this->return_stack[$key];
		}
		
		return $this->return_stack;
	}
}

The return_stack() method returns either the specified array value by index, or the entire array of returns as they were stored by the __call() magic method.

If we modify our Test class, we can turn the make_array() method into a chainable method. While we're at it, let's go ahead and add some functionality to store a supplied array in the return stack.

PHP:
<?php

class Test extends Chain
{	
	/**
	 * Name functions according to Parent __call requirements to have
	 * them automagically return $this.
	 */	
	public function chain_hello()
	{
		print 'Hello';
		
		return TRUE;
	}
	
	public function chain_make_array(array $array = NULL)
	{
		if (isset($array))
		{
			return $array;
		}
		else
		{
			return array('foo' => 'bar', 'baz' => 'faz');
		}
	}
}

Another bit of test code:

PHP:
<?php

//get dependencies
require('chainable.php');
require('return_stackable.php');
require('chain_stackable.php');
require('test.php');

$test = new Test;

//execute a chain
$test->hello()
->hello()
->make_array()
->make_array(array('food' => 'tacos'))
->make_array(array('food' => 'burritos'));

//fetch the return stack from functions
$stack = $test->return_stack('make_array');
var_dump($stack);

Now, we've got a chainable class that remembers the original function returns! The beauty of this approach, is that you can implement method chaining in your existing classes with a few simple modifications to the method names, and still have access to their return values via the return stack. Additionally, you could flesh out the Chain class to optionally purge the return stack when it was retrieved. This would allow instant access to the stack, while preventing lingering values from getting in the way.
 
Last edited:
Divine Celestial
Loyal Member
Joined
Sep 13, 2008
Messages
853
Reaction score
14
You missed the ending
PHP:
?>

:p

Im not 100% sure on this but from what I know, the php close tag is not a requirement and when the file contain just php, it is suggested to dont use it to avoid trailing white spaces into the response, i.e. an accidental space, tab, new line, ... after "?>".
 
Legendary Battlemage
Loyal Member
Joined
Apr 7, 2009
Messages
647
Reaction score
25
Im not 100% sure on this but from what I know, the php close tag is not a requirement and when the file contain just php, it is suggested to dont use it to avoid trailing white spaces into the response, i.e. an accidental space, tab, new line, ... after "?>".

That makes absolutely no sense at all.

PHP doesn't care about whitespace.
It'll only be like that if you use "echo" or "print"

if you include that file, you'll be in big trouble.
Example.

PHP:
<?php
echo "hi";

PHP:
<?php
include("hi.php");
echo "After inclusion";
?>

After that all gets compiled, this is your result:

PHP:
<?php
//Inclusion of hi.php
echo "hi";
<?php

echo "After inclusion";
?>

Fatal error.
 
Joined
Jun 8, 2007
Messages
1,985
Reaction score
490
xSilv3rbullet said:
After that all gets compiled, this is your result:
No it's not...

What BBim said is right, and makes perfect sense. PHP needs an opening tag, but does not need a closing tag.. I figured it out by mistake, lol.. When you include a PHP file (the traditional way..) it disregards the PHP tags, and just works on the code.. You always need a '<?php' to start coding your PHP.

The white-space BBim was talking about would be at the end of the file AFTER the trailing '?> '. (see the space, :p) On a typical editor you wouldn't see the space, and if you include a few files, one or two have a space in them, then you can't set cookies, or use headers of any kind.. No session_start() after any output on the document.. etc. All of that is because headers cannot be sent once the document is sent to the client. If you output any white-space, it does what output is intended to do~ gets sent to the client/browser for someone to see/interpret. At that point, a good explanation might be that PHP is switched to: "Ah! load quick and send the data to the client!" mode. The headers have been sent, the client knows the time Mr. Server sent it, and begins to time how long it's going to take your server to finish sending, and how long the client will take to load the rest of the mark-up.. blah blah blah.. Logging slows everything down- yet they say it's to make it all better in the future :/ [Jk, obviously it does]

Anyway, see what BBim, and the PHP peeps mean now?
 
Last edited:
Junior Spellweaver
Joined
Nov 26, 2008
Messages
196
Reaction score
62
Just to add onto s-p-n's big wall of text.

If you are really uptight about the ?>, you could always had the end of file comment to tell it that the script has ended.

Code:
/* EOF */

Doesn't create any effects like ?> does. Major frameworks like Kohana and CodeIgniter use it, as do I.
 
Legendary Battlemage
Loyal Member
Joined
Apr 7, 2009
Messages
647
Reaction score
25
No it's not...

What BBim said is right, and makes perfect sense. PHP needs an opening tag, but does not need a closing tag.. I figured it out by mistake, lol.. When you include a PHP file (the traditional way..) it disregards the PHP tags, and just works on the code.. You always need a '<?php' to start coding your PHP.

The white-space BBim was talking about would be at the end of the file AFTER the trailing '?> '. (see the space, :p) On a typical editor you wouldn't see the space, and if you include a few files, one or two have a space in them, then you can't set cookies, or use headers of any kind.. No session_start() after any output on the document.. etc. All of that is because headers cannot be sent once the document is sent to the client. If you output any white-space, it does what output is intended to do~ gets sent to the client/browser for someone to see/interpret. At that point, a good explanation might be that PHP is switched to: "Ah! load quick and send the data to the client!" mode. The headers have been sent, the client knows the time Mr. Server sent it, and begins to time how long it's going to take your server to finish sendingyou lost me here., and how long the client will take to load the rest of the mark-up.. blah blah blah.. Logging slows everything down- yet they say it's to make it all better in the future :/ [Jk, obviously it does]

Anyway, see what BBim, and the PHP peeps mean now?

...

/post2short-nowitisn't
 
Divine Celestial
Loyal Member
Joined
Sep 13, 2008
Messages
853
Reaction score
14
That makes absolutely no sense at all.

PHP doesn't care about whitespace.
It'll only be like that if you use "echo" or "print"

if you include that file, you'll be in big trouble.
Example.

PHP:
<?php
echo "hi";

PHP:
<?php
include("hi.php");
echo "After inclusion";
?>

After that all gets compiled, this is your result:

PHP:
<?php
//Inclusion of hi.php
echo "hi";
<?php

echo "After inclusion";
?>

Fatal error.

if it was like that, it would be:
PHP:
<?php
//hi.php start
<?php
echo "hi";
//hi.php end
echo "After inclusion";
?>
Parse error: syntax error, unexpected '<' in C:\xampp\htdocs\index.php on line 3

What I was saying, like s-p-n said, was white space after ?>.
Sometimes when I alt+tab or ctrl+tab, I insert a tab on the code, most of times I notice it but if Im on a hurry I dont. Imagine I make a database class and leave the cursor after >, alt+tab and it adds the tab, I dont notice it, I do somethings and come back to it to change something, after that I save the file, if I have a session_start after including db class, it wouldnt work and I would take some minutes to find out why.

This is just a recommendation from zend, it is not a must.
 
Newbie Spellweaver
Joined
Oct 30, 2009
Messages
30
Reaction score
2
You missed the ending
PHP:
?>


Nope.

PHP does care about whitespaces at the end of file after ?>. (With an exception of a single newline character after the ?> tag, which is also part of the closing tag, the link above says that the closing tag can be either "?>\n" or "?>")

And when PHP includes another file, PHP does it at runtime. So it doesn't work like sticking all the files together, but each included files are compiled separately.
 
Back
Top