3

How can I reduce these two lines

$foo = new Bar();
$baz = $foo->methodOne('param')->methodTwo('param');

to

$baz = Bar::methodOne('param')->methodTwo('param');

I´ve seen this specially in Laravel and it´s nice readable Code. But I stuck getting this to work with some custom Helper-Classes. It feels like mixing static + nonstatic functions which is confusing for now ...

Kristo
  • 547
  • 1
  • 7
  • 20

3 Answers3

3

Laravel does it the following way

in: vendor/laravel/framework/src/Illuminate/Database/Capsule

/**
 * Dynamically pass methods to the default connection.
 *
 * @param  string  $method
 * @param  array   $parameters
 * @return mixed
 */
public static function __callStatic($method, $parameters)
{
    return call_user_func_array(array(static::connection(), $method), $parameters);
}

/**
 * Get a connection instance from the global manager.
 *
 * @param  string  $connection
 * @return \Illuminate\Database\Connection
 */
public static function connection($connection = null)
{
    return static::$instance->getConnection($connection);
}

From the PHPDOC:

__callStatic() is triggered when invoking inaccessible methods in a static context.

I think you can simplify this for your class:

class Bar{
    public static function __callStatic($method, $parameters)
    {
        return call_user_func_array(array(new Bar(), $method), $parameters);
    }
    public function hello(){
      echo "hello";
    }
}

Bar::hello();
edi9999
  • 19,701
  • 13
  • 88
  • 127
  • After pasting in your snippet I´m still getting this: Non-static method Bar::methodOne() should not be called statically, assuming $this from incompatible context // any idea? – Kristo Oct 18 '14 at 17:02
  • Am I right that `call_user_func_array(array(new Bar(), $method), $parameteres);` cannot point to it´s own class? It should instanciate another class where - like in my example methodOne and methodTwo - are called. So it could be `call_user_func_array(array(new Foo(), $method), $parameters);` ... right? – Kristo Oct 18 '14 at 22:32
  • Nope, it should work even if it points to its own class. I have edited my post with an example that works for me; – edi9999 Oct 19 '14 at 09:30
  • However, I see that you can't access to the $this in `hello()` . I don't know the solution however – edi9999 Oct 19 '14 at 09:40
2

A more consistent and self explanatory thing to do would be to have a static construction function that returns the object, then call the methods on it like this:

$baz = Bar::create()->methodOne('param')->methodTwo('param');

An alternate syntax to do it directly with what you have now is:

$baz = (new Bar())->methodOne('param')->methodTwo('param');

But that's not very pretty...

  • 1
    Just stumbled upon this answer, your alternative solution may not be very _pretty_ (though thats a subjective matter), it is the cleanest way from all perspectives offered here. The overhead Laravel offers by using something _pretty_ as `Bar::nonStaticMethod()` is far more severe than `( new Bar() )->nonStaticMethod()`. Just write down the call-chain (versus your alternative solution) of how Laravel implemented its solution to something so simple and you'll consider not using it ;) – dbf Apr 22 '16 at 07:49
  • what exactly do you mean with severe? – Kristo Apr 22 '16 at 19:39
  • 2
    @Kristo with `(new Bar())->methodCall()` there is no overhead. The _Facaded_ solution calls a static method which apparently does not exist, has to create an instance first of the called class `Bar` then to call the method with the instance with `call_user_func_array`, has to assign the parameters as an argumented array before it can return the instance itself to call any follow up (chained) calls. If you use `Bar::someMethod()` on various lines/position throughout your script, it does this process over and over again .. I would conclude it's pretty severe for something so simple .. – dbf Apr 23 '16 at 18:14
0

For everyone passing by: Laravel offers a design pattern called Facades to achieve that effect. http://laravel.com/docs/5.1/facades

If you want to build your own Helper-Class there are four things need:

  1. Helper Class itself
  2. Service Container binding of that class
  3. Facade Class "pointing" to Service Container
  4. Alias in app.php
Kristo
  • 547
  • 1
  • 7
  • 20
  • 1
    This was all a bit too complex so Laravel came out with real-time facades to simply the creation: https://laravel.com/docs/5.6/facades#real-time-facades – Eugene van der Merwe Apr 06 '18 at 12:20