Forum Moderators: coopster

Message Too Old, No Replies

Chaining functions on a single line *without* nesting parentheses

simulating javascript notation in PHP

         

whoisgregg

3:16 pm on Feb 13, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



One thing about javascript that I always find handy was the ability to take an object/string and apply multiple functions to it all in one call without having to nest lots of parentheses.

// neato, easy to read and write:

var str = 'StRiiiNg'.toLowerCase().replace(/i+/, 'i').toUpperCase();
alert(str); // STRING

I got to thinking yesterday if you could achieve this effect in PHP to chain together functions. There are two patterns that I use right now when I need to apply multiple transformations to a value:

// sucky, easy to nest parentheses wrong, hard to read

$str = strtoupper(preg_replace('/i+/','i', strtolower('StRiiiNg')));
echo $str; // STRING

// good, easy to read but takes up lots of lines for simple transformations

$str = 'StRiiiNg';
$str = strtolower($str);
$str = preg_replace('/i+/','i',$str);
$str = strtoupper($str);
echo $str; // STRING

Since this was bugging me, I played around and put together the following little test. Since each function returns $this (a handle to the object) you can call functions one right after the other.

class str_functions {
var $a;
function str_functions($string = null){
$this->a = $string;
return $this;
}
function get(){
return $this->a;
}
function strtoupper(){
$this->a = strtoupper($this->a);
return $this;
}
function strtolower(){
$this->a = strtolower($this->a);
return $this;
}
function preg_replace($find, $replace){
$this->a = preg_replace($find, $replace, $this->a);
return $this;
}
}
[b]$str = new str_functions('StRiiiNg');
$str = $str->strtolower()->preg_replace('/i+/','i')->strtoupper()->get();
echo $str; // STRING
[/b]

Would it really suck to have to rewrite every existing function into a custom class just for this benefit? Yeah, definitely.

Could this be applied to new custom classes? Maybe... What do you think? :)

henry0

4:29 pm on Feb 13, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Nice and easy flow

I need to understand your solutions benefits
actually what is the dif between "yours" and:
OOP
$db = new stuffs
$db= conn this and that

and then $num=$db->num_rows($result);
or
while($row=$db->fetch_array($result));

where both functions are custom ()
and both easy to implement and read

the domino effect is also there
A) conn
B) use result of conn for any custom ()

but again I probably miss your point :)

jatar_k

4:46 pm on Feb 13, 2008 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



you could create a free transform function where you pass the names of functions and it executes them, you could probably replicate a similar syntax to the js one

whoisgregg

5:05 pm on Feb 13, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



your solutions benefits

I'm actually not sure it's worth it myself. :)

A definite drawback to having the functions return $this is that if you only need one function performed, then you have to call an extra function to get the actual result back out:

echo $str->strtolower()->get(); // this is less efficient typing :(

whoisgregg

5:39 pm on Feb 13, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



free transform function

Hmm. This is how far I got before realizing I don't have a clue how to do that. :)

// ...inside the str_functions class...
function e($function_name){
if(function_exists($function_name)){
if(func_num_args()==1){
$this->a = $function_name($this->a);
} else {
$_args = func_get_args();
$function_name = array_shift($_args);
$this->a = $function_name($this->a, $_args);
// err, how would i pass an unknown number of arguments to the function?
}
}
return $this;
}

The code above works for simple ones like strtolower and strtoupper but anything with multiple parameters chokes. Obviously, I realize, since I can't pass an array as the second parameter and expect the function to magically extract the values out of that array.

$str = new str_functions('StRiiiNg');
$str = $str->e('strtolower')->e('preg_replace','/i+/','i')->e('strtoupper')->get(); // Warning: Wrong parameter count for preg_replace()

cameraman

6:09 pm on Feb 13, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



since I can't pass an array as the second parameter...

You can with this:
call_user_func_array() [php.net]

whoisgregg

6:49 pm on Feb 13, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I read that function manual page a few times, each time thinking it would only work on user defined functions, not on internal functions. But, you're right, it works. :D

Problem now is that there is no way to know what each function returns and what to do with it. With an assumption of string manipulation, you can always just assign the return of the user function to $this->a. But if you assume array manipulation, assigning the return of the functions often overwrites the $this->a with a boolean, causing you to lose the original array.

class Chain {
private $a;
function chain($string = null){
$this->set($string);
return $this;
}
function get(){
return $this->a;
}
function set($string = null){
$this->a = $string;
return $this;
}
function e($function_name){
if(function_exists($function_name)){
if(func_num_args()==1){
$return = $function_name($this->a);
$this->a = $return; // this is not always what you'd expect :/
} else {
$_args = func_get_args();
$function_name = array_shift($_args);
$return = call_user_func_array($function_name, $_args);
$this->a = $return; // this is not always what you'd expect :/
}
}
return $this;
}
}
// works
echo $c->set('StRiiiNg')->e('strtolower')->e('preg_replace','/i+/','i', $c->get())->e('strtoupper')->get();
// the shuffle hoses the array
print_r($c->set($array)->e('shuffle')->e('array_merge', $c->get(), array(12,13,16))->e('sort')->get());