0

I have a code problem which stems from the fact that I am using certain libraries of code I cannot change.

I use the following code to pass execution of any undefined methods to another class, and it works fine but it seems like a waste doubling up.

Any suggestions?

Basically I want to know if it's possible to pass an unknown number of parameters to a method (without using call_user_func_array(), just in case they need to be passed by reference). I am not asking how to use func_get_args(), rather the reverse.

Or should I just allow for a few more arguments in the first logic path (the list() code)?

class Foo {
    __construct() {
        $this->external = new ClassThatIHaveNoControlOver();
    }

    function bar($name) {
        return 'Hi '.$name;
    }

    function __call($method, $arguments) {
        if (count($arguments) < 3) {
            // call_user_func_array won't pass by reference, as required by
            // ClassThatIHaveNoControlOver->foobar(), so calling the function
            // directly for up to 2 arguments, as I know that foobar() will only
            // take 2 arguments
            list($first, $second) = $arguments + Array(null, null);
            return $this->external->$method($first, $second);
        } else {
            return call_user_func_array(array($this->external, $method), $arguments);
        }
    }
}

$foo = new Foo();

$firstName = 'Bob';
$lastName = 'Brown';
echo $foo->bar($firstName); // returns Hi Bob as expected
echo $foo->foobar($firstName, $lastName); // returns whatever
        // ClassThatIHaveNoControlOver()->foobar() is meant to return

EDIT Just to clarify, I know I can use this method to rejig the parameters as references, but that would mean passing everything as a reference, even if the method didn't require it - something I was trying to avoid, but seems unlikely at the moment.

hakre
  • 193,403
  • 52
  • 435
  • 836
  • 1
    "just in case they need to be passed by reference" - any valid example of that? Any reason to want that? – zerkms Mar 21 '12 at 03:21
  • @zerkms Methods that aren't in my class are passed onto the other class to be handled, and some require the args to be passed by reference (see the code) - I haven't included that class because I have no control over it. –  Mar 21 '12 at 03:24
  • See: http://stackoverflow.com/questions/295016/is-it-possible-to-pass-parameters-by-reference-using-call-user-func-array , http://stackoverflow.com/questions/6259713/why-does-phps-call-user-func-function-not-support-passing-by-reference – CBusBus Mar 21 '12 at 03:29
  • @SOliver As per [the documentation](http://php.net/manual/en/function.call-user-func-array.php), _This form of call-time pass by reference does not emit a deprecation notice, but it is nonetheless deprecated, and will most likely be removed in the next version of PHP._ ... _Passing by value when the function expects a parameter by reference results in a warning and having call_user_func() return FALSE_ - As of PHP 5.3 the solution on that question doesn't work. –  Mar 21 '12 at 03:34
  • As of php 5.3 (sorry I said 1.5.3 earlier for some reason) pass by reference functions need to be declared at compile time. An alternative would be to $varWithFunctionName($params); – CBusBus Mar 21 '12 at 03:38
  • @SOliver Wouldn't that just send through the parameters as an array? As I can't change the external code, they need to be separated parameters. Or am I missing something? –  Mar 21 '12 at 03:42
  • @cainmi: in the code samples I see that you pass by values, not by references – zerkms Mar 21 '12 at 03:45
  • @zerkms The reference requirement is in the external code, for example `ClassThatIHaveNoControlOver()->foobar()`. Just to clarify, pass by reference in current versions of PHP is limited to the `function foo(&$bar) {}` style, and not `foo(&$bar);` - [see here](http://www.php.net/manual/en/language.references.pass.php). –  Mar 21 '12 at 03:50
  • @cainmi I've posted an example as answer below, not particularly neat but it shows what I meant with passing by reference whilst still using call_user_func. – CBusBus Mar 21 '12 at 03:57
  • @cainmi: I know what reference is and how it works. But you pass a string literals in your example, and they cannot be passed by reference **by definition** – zerkms Mar 21 '12 at 04:10
  • 1
    @zerkms Haha oh yeah just realized, the production code isn't like that obviously, I'll change it now. –  Mar 21 '12 at 04:19

2 Answers2

1

You could use something like this:

public function __call($method, $params = array()) {
    switch (count($params)) {
        case 0:
            return $this->external->{$method}();
        case 1:
            return $this->external->{$method}($params[0]);
        case 2:
            return $this->external->{$method}($params[0], $params[1]);
        case 3:
            return $this->external->{$method}($params[0], $params[1], $params[2]);
        default:
            return call_user_func_array(array(&this->external, $method), $params);
    }
}
Rob
  • 12,659
  • 4
  • 39
  • 56
  • This is basically what the code above does (although I only allowed for 2 parameters), it just seems a little inelegant. –  Mar 21 '12 at 03:36
  • Maybe so but I don't think there is any other solution. – Rob Mar 21 '12 at 03:40
1

As commented in the thread question post's comments this is an example and not necessarily (likely) best practice.

//Some vars
$foo = "shoe";
$bar = "bucket";

//Array of references
$arr = Array(&$foo, &$bar);

//Show that changing variable value affects array content
$foo = "water";
echo $arr[0];

//Sample function
function fooBar($a)
{
    $a[0] = "fire";
}
//Call sample function
call_user_func("fooBar",$arr);

//Show that function changes both array contents and variable value by reference
echo $arr[0];
echo $foo;

Expanding a bit on the discussion, again not the most industry standard approach but it'll do the job.

function pushRefOnArray(&$arr, &$var, $key = false)
{
    if(isset($key))
        $arr[$key] = &$var;
    else
        $arr[] = &$var;
}

Essentially you can dynamically build your array and call pushRefToArray() any time you need to pass an item to be passed as reference rather than by value.

CBusBus
  • 2,321
  • 1
  • 18
  • 26
  • This is the only other thing that I would get to work, but it means I pass everything by reference, not just to the methods that require it. This will definitely work, but I just wanted to know if there was an alternative. –  Mar 21 '12 at 04:00
  • 1
    @cainmi Sort of. Since the array can be built anywhere within your object or by any other means such as a global function this approach doesn't necessitate that you pass an Array of references, only that when you're function's affect on the array would exist outwith the scope of the method and will need to be returned. – CBusBus Mar 21 '12 at 04:06
  • Yes I see what you mean. Given that the array would probably be created within the `Foo` object (in this example), there might be issues with any methods that actually rely on modifying the original variable (through the reference). –  Mar 21 '12 at 04:10
  • Although I suppose it would be anyway, give it's already abstracted out through the `__call()` method. –  Mar 21 '12 at 04:12
  • @cainmi . Once again I can't say with confidence that it's a good idea but I've appended an approach you could use to push references into an array dynamically – CBusBus Mar 21 '12 at 04:32