2

Is it possible to write series of statements that repeatedly refer to a single object without having to write the object every time?

I came to this because I used to do this in Visual Basic:

With person .setFullName(.firstName+" "+.lastName) .addParent(parent) .save() End With 

This is a shorthand for

person.setFullName(person.firstName+" "+person.lastName) person.addParent(parent) person.save() 

Is it possible to achieve this in PHP? To rewrite the following code without having to write $person 5 times?

$person->setFullName($person->firstName.' '.$person->lastName); $person->addParent($parent); $person->save(); 

Note: I'm not referring to methods chaining for 2 reasons:

1) I want to use public members as well

2) I don't work with classes I wrote, so I cannot add return $this; to all the methods

Thanks

4
  • 1
    Adding return $this to every methods to concat calls would have been a very ugly workaround anyway imo. I'd rather stick to the usual x3 $person-> Commented Jul 18, 2014 at 8:03
  • In your case no, you can't. However if you have long expessions like $one->two->three->prop you can shorten them by defining alias/reference of nested object $tree = $one->two->three, $tree->prop. Commented Jul 18, 2014 at 8:09
  • Could the person who down-voted the question tell us why? I am extremely curios! Commented Jul 18, 2014 at 8:18
  • What you're describing falls into method chaining, and you don't have to write the same sentence 5 times and you can use public members, like I did in my example... check my answer Commented Jul 18, 2014 at 9:05

4 Answers 4

2

There is exists PHP lib that allow to do it: https://github.com/lisachenko/go-aop-php

Example of implementation: http://go.aopphp.com/blog/2013/03/19/implementing-fluent-interface-pattern-in-php/

Create your aspect

<?php use Go\Aop\Aspect; use Go\Aop\Intercept\MethodInvocation; use Go\Lang\Annotation\Around; class FluentInterfaceAspect implements Aspect { /** * Fluent interface advice * * @Around("within(FluentInterface+) && execution(public **->set*(*))") * * @param MethodInvocation $invocation * @return mixed|null|object */ protected function aroundMethodExecution(MethodInvocation $invocation) { $result = $invocation->proceed(); return $result!==null ? $result : $invocation->getThis(); } } 

Add interface for matching

interface FluentInterface { } class User implements FluentInterface { protected $name; protected $surname; protected $password; public function setName($name) { $this->name = $name; } public function setSurname($surname) { $this->surname = $surname; } public function setPassword($password) { if (!$password) { throw new InvalidArgumentException("Password shouldn't be empty"); } $this->password = $password; } } 

Usage

$user = new User; $user->setName('John')->setSurname('Doe')->setPassword('root'); 

But you could write your matching rule without adding new interface.

P.S. It's not an proper answer to the question because required other syntax sugar. PHP do not support such syntax.

Sign up to request clarification or add additional context in comments.

2 Comments

I am intrigued, and I admit I'm not getting how it works yet. The example implementation seems to scream black magic and a quick perusal of the readme didn't clear it up. Mind explaining in a few sentences how that library does what it does with virtually no code changes?
@deceze, after some looking in: tokenizer, streams with caching results.
0

You can't do it in PHP. The syntax of the language does not permit it

1 Comment

Unfortunately, this is the correct answer that wasn't unexpected
0

In PHP you can do this, but of course you can create shorter variable

$p = &$person; $p->setFullName($p->firstName.' '.$p->lastName); $p->addParent($parent); $p->save(); unset($p); 

But in this case you still have -> operator and variable and without already mentioned libs you won't get more.

2 Comments

You don't even need &, object variables already are references.
@deceze not exactly they are references but you are probably right that & is not needed in this case but it's more legibly to add one I think
0

Yes, you can do it. It's called method chaining (if I remember correctly). Let me put you a simple example:

class A { public $attributes; public function __construct() { return $this; } public function methodA($a) { if (!empty($a)) { $this->attributes["a"] = $a; } return $this; } public function methodB($b) { if (!empty($b)) { $this->attributes["b"] = $b; } return $this; } public function methodC($c) { if (!empty($c)) { $this->attributes["c"] = $c; } return $this; } } 

The key to what you're looking for is to return the object itself (hence, the reference to return $this;) and you get the same behaviour you had with VB. In PHP you would do it like this:

$a = new A(); $a->methodA(5)->methodB(50)->methodC(500); 

Or even:

$a = (new A())->methodA(5)->methodB(50)->methodC(500); 

Hope that helps :)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.