57

I would like to know whether there's a way to chain methods on a newly created object in PHP?

Something like:

class Foo {
    public function xyz() { ... return $this; }
}

$my_foo = new Foo()->xyz();

Anyone know of a way to achieve this?

user2864740
  • 60,010
  • 15
  • 145
  • 220
aefxx
  • 24,835
  • 6
  • 45
  • 55

7 Answers7

105

In PHP 5.4+, the parser's been modified so you can do something like this

(new Foo())->xyz();

Wrap the instantiation in parenthesis, and chain away.

Prior to PHP 5.4, when you're using the

new Classname();

syntax, you can't chain a method call off the instantiation. It's a limitation of PHP 5.3's syntax. Once an object is instantiated, you can chain away.

One method I've seen used to get around this is a static instantiation method of some kind.

class Foo
{
    public function xyz()
    {
        echo "Called","\n";
        return $this;
    }

    static public function instantiate()
    {
        return new self();
    }
}


$a = Foo::instantiate()->xyz();

By wrapping the call to new in a static method, you can instantiate a class with method call, and you're then free to chain off that.

Alana Storm
  • 164,128
  • 91
  • 395
  • 599
  • 2
    +1 Yes I was going to answer "use a factory method," and that's basically what you're showing. – Bill Karwin Feb 03 '10 at 00:38
  • 6
    I prefer to leave the OO lingo out of the answer when the questioner doesn't appear to be steeped in CS culture. Better they learn the concept and later identify it by its "proper" name than be scared off by new terms. – Alana Storm Feb 03 '10 at 03:09
  • -1 This doesn't solve the constructor problem, furthermore you will have issues with inheritance. – Kenaniah Jul 12 '11 at 18:35
  • 2
    Um, yes it does? "The constructor problem" I was talking about was the inability to instantiate an object and immediately chain, and yes, there's tradeoffs related to inheritance and which version of PHP you're using w/r/t static binding. – Alana Storm Jul 12 '11 at 19:26
  • 3
    If you're going to use the static instantiate method, you should have it call "new static" instead of "new self". That way if you're calling it on a subclass of Foo, it will create an instance of the subclass, and not of Foo itself. – JW. Dec 15 '14 at 18:44
24

Define a global function like this:

function with($object){ return $object; }

You will then be able to call:

with(new Foo)->xyz();
Kenaniah
  • 5,171
  • 24
  • 27
  • 3
    I personally prefer this answer to Alan's, it can be easily implemented in a framework's base class and therefore solves the problem once, in one place, rather than having to implement Alan's answer in every class! I like DRY programming!! – Chris Aug 21 '12 at 09:21
  • 2
    This is also called [identity function](https://en.wikipedia.org/wiki/Identity_function), some libraries have it as `id()`. It has a lot of use in PHP due to it's syntax and parser. – hakre Jan 01 '14 at 10:40
  • 1
    This would be nice, but what would be the PHPDoc for this method? PHPStorm relies on PHPDoc to tell it what data types a function receives and returns; this isn't Java, so generics are not available. I can write it like `@param object $object` + `@return object`, but then it won't recognise the right data type and auto-completion/suggestions won't work. – jurchiks Nov 21 '14 at 15:50
  • Really a bad idea to pollute the global namespace with a user function of the name "with". You never know when that becomes a reserved keyword or if other libraries might use it as well. So use something unique instead. – TheStoryCoder May 30 '16 at 13:29
11

In PHP 5.4 you can chain off a newly instantiated object:

http://docs.php.net/manual/en/migration54.new-features.php

For older versions of PHP, you can use Alan Storm's solution.

Jackson
  • 607
  • 2
  • 6
  • 20
6

This answer is outdated - therefore want to correct it.

In PHP 5.4.x you can chain a method to a new-call. Let's take this class as example:

<?php class a {
    public function __construct() { echo "Constructed\n"; }
    public function foo() { echo "Foobar'd!\n"; }
}

Now, we can use this: $b = (new a())->foo();

And the output is:

Constructed
Foobar'd!

Further information may be found on the manual: http://www.php.net/manual/en/migration54.new-features.php

Ingwie Phoenix
  • 2,703
  • 2
  • 24
  • 33
  • 1
    Note in your example, `$b` is whatever `foo()` returns, not (necessarily) the instantiated object. – artfulrobot Jan 28 '15 at 09:50
  • @artfulrobot I only understood that the one asking the question only wanted to know how to instantiate and directly invoke a class' method. But your statement is valid; instead of the class instance, the return value of `foo()` - which is NULL in this case - become the contents of `$b`. – Ingwie Phoenix Jan 28 '15 at 13:12
3

Well, this may be an old question but as with a lot of things in programming - eventually the answer changes.

Regarding PHP 5.3, no, you can't chain directly from the constructor. To expand on the accepted answer however, in order to properly accommodate for inheritance, you can do:

abstract class Foo 
{    
    public static function create() 
    {
        return new static;
    }
}

class Bar extends Foo
{
    public function chain1()
    {
        return $this;
    }

    public function chain2()
    {
        return $this;
    }
}

$bar = Bar::create()->chain1()->chain2();

That will work just fine and will return you a new Bar() instance.

In PHP 5.4, however, you can simply do:

$bar = (new Bar)->chain1()->chain2();

Hopefully this helps someone stumbling across the question like I have!

Lukey
  • 922
  • 1
  • 7
  • 9
1

It would be really helpful if they 'fix this' in a future release. I really appreciate the ability to chain (especially when populating collections):

I added a method to the base class of my framework called create() that can be chained off of. Should work with all descendant classes automatically.

class baseClass
{
    ...
    public final static function create()
    {
        $class = new \ReflectionClass(get_called_class());
        return $class->newInstance(func_get_args());
    }
    ...
    public function __call($method, $args)
    {
        $matches = array();
        if (preg_match('/^(?:Add|Set)(?<prop>.+)/', $method, $matches) > 0)
        {
            //  Magic chaining method
            if (property_exists($this, $matches['prop']) && count($args) > 0)
            {
                $this->$matches['prop'] = $args[0];
                return $this;
            }
        }
    }
    ...
}

Class::create()->SetName('Kris')->SetAge(36);

Kris
  • 21
  • 2
  • 2
    -1 This forces all classes to inherit from a single source, furthermore you had to instantiate ReflectionClass to accomplish your goal. – Kenaniah Jul 12 '11 at 18:37
0

Just for the sake of completeness (and for the fun of it...), since nobody seems to have mentioned the solution with the shortest (and least sophisticated) code.

For frequently used short-lived objects, especially when writing test cases, where you typically do lots of object creation, you may want to optimize for typing convenience (rather than purity), and sorta' combine Alan Storm's Foo::instantiate() factory method and Kenaniah's with() global function technique.

Simply make the factory method a global function with the same name as the class!. ;-o (Either add it as a convenience wrapper around the proper static Foo::instantiate() or just move it out there while nobody is looking.)

class Foo
{
    public function xyz()
    {
        echo "Called","\n";
        return $this;
    }
}

function Foo()
{
    return new Foo();
}

$a = Foo()->xyz();

NOTE:

  • I WOULDN'T DO THIS on production code. While kinda' sexy, this is an abuse on basic coding principles (like "principle of least surprise" (although this is actually rather intuitive syntax), or "don't repeat yourself", esp. if wrapping a real factory method with some parameters, which itself, BTW, is already an abuse of DRY...), plus PHP may change in he future to break code like this in funny ways.
Sz.
  • 3,342
  • 1
  • 30
  • 43