PHP method chaining or fluent interface?
It's rather simple, really. You have a series of mutator methods that all return the original (or other) object. That way, you can keep calling methods on the returned object.
<?php
class fakeString
{
private $str;
function __construct()
{
$this->str = "";
}
function addA()
{
$this->str .= "a";
return $this;
}
function addB()
{
$this->str .= "b";
return $this;
}
function getStr()
{
return $this->str;
}
}
$a = new fakeString();
echo $a->addA()->addB()->getStr();
This outputs "ab"
Try it online!
How do I chain methods in PHP?
To answer your cat example, your cat's methods need to return $this
, which is the current object instance. Then you can chain your methods:
class cat {
function meow() {
echo "meow!";
return $this;
}
function purr() {
echo "purr!";
return $this;
}
}
Now you can do:
$kitty = new cat;
$kitty->meow()->purr();
For a really helpful article on the topic, see here: http://www.talkphp.com/advanced-php-programming/1163-php5-method-chaining.html
How to chain method on a newly created object?
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.
PHP: The name of the previous method in chain
A good and more general way to get a track of the previously called method is to use a private or protected property to keep track of the actual method using the constante __FUNCTION__
or __METHOD__
example:
class colorchanger
{
protected prevMethod;
public function black()
{
/*
your routine
for the black
method here
*/
$this->prevMethod =__FUNCTION__;
return $this;
}
public function white()
{
/*
your routine
for the white
method here
*/
$this->prevMethod =__FUNCTION__;
return $this;
}
public function colour()
{
return $this->prevMethod;
}
}
$c = new ColorChanger();
$c->black->colour();
$c->white()->colour();
$c->black()->white->colourMethod();//will return white instead of black
PHP method chain with loop
I've tried to simplify the code to show just this one aspect of what your after...
class Bar
{
private $list;
public function __construct($a=null) {
$this->list = $a;
}
public function each( callable $fn )
{
foreach ( $this->list as $value ) {
$fn($value);
}
return $this;
}
}
$a=array('bob','andy','sue','rob');
$bar1 = (new Bar($a))->each(function ($value) {
print $value."\n";
});
As you can see, I've created the object with the list you have, and then just called each()
with a callable
. You can see the function just takes the passed in value and echoes it out.
Then in each()
there is a loop across all the items in the list provided in the constructor and calls the closure ($fn($value);
) with each value from the list.
The output from this is...
bob
andy
sue
rob
As for the chained calls, the idea is (as you've worked out) is to return an object which will be the start point for the next call. Some use $this
(as you do) some systems (like Request
commonly does) return a NEW copy of the object passed in. This is commonly linked to the idea of immutable objects. The idea being that you never change the original object, but you create a new object with the changes made in it. Psr7 Http Message, why immutable? gives some more insight into this.
Chain methods ability without force to new keyword using php
Really all you need is for the static firstName
methods to create a new instance of the class and return it.
The other setters just need to return $this
to provide what's referred to as a fluent interface.
If the only way to create an instance is via the static firstName
method, you'll also want to add a private / protected constructor.
For example
class Person
{
private $firstName;
private $lastName;
private $age;
private $father;
private function __construct(string $firstName) {
$this->firstName = $firstName;
}
public static function firstName(string $name) {
return new Person($name);
}
public function lastName(string $lastName) {
$this->lastName = $lastName;
return $this;
}
public function age(int $age) {
$this->age = $age;
return $this;
}
public function setFather(Father $father) {
$this->father = $father;
return $this;
}
public function toArray() {
// just an example
return [
'firstName' => $this->firstName,
'lastName' => $this->lastName,
'age' => $this->age,
'father' => $this->father->toArray(),
];
}
}
I would strongly advise against keeping the $name
property as static. You don't want to change one instance's $name
and have it change all others. This is why I've changed it to private $firstName
in my example above.
Can I somehow chain methods in PHP
What you want is essentially an array reduction:
$resp = array_reduce($methods, function ($o, $p) { return $o->$p; }, $this);
It's unclear whether you want $o->$p
(property access) or $o->$p()
(method call), but you can figure that out.
Method chaining in php
You should make use of exceptions instead, which would handle errors while the methods themselves would always return the current instance.
This becomes:
<?php
class FormValidationException extends \Exception
{
}
class FormValidator
{
private $value;
public function Value($value): self
{
$this->value = trim($value);
return $this;
}
/**
* @return $this
* @throws FormValidationException
*/
public function Required(): self
{
if (empty($this->value)) {
throw new \FormValidationException('Value is required.');
}
return $this;
}
/**
* @param int $length
* @return $this
* @throws FormValidationException
*/
public function MinLength(int $length): self
{
$len = strlen($this->value);
if ($len < $length) {
throw new \FormValidationException("Value should be at least {$length} characters long.");
}
return $this;
}
}
Usage:
$validator = new FormValidator();
try {
$result = $validator->Value("lodddl")->Required()->MinLength(5);
} catch (\FormValidationException $e) {
echo 'Error: ', $e->getMessage();
}
Demo: https://3v4l.org/OFcPV
Edit: since OP is using PHP 5.2 (sadly), here's a version for it, removing \
s before root namespace, return type declarations and argument types.
Demo for PHP 5.2: https://3v4l.org/cagWS
PHP Method Chaining and Strings
$chain->AAA()->BBB()
is doing the first two 'AAA' and 'BBB' - obvious.
Then the $chain->AAA()
which comes inside wrap($chain->AAA())
does the 3rd 'AAA'.
and last, the wrap
method takes all the three and wraps them with ()
and concatenated to the first 'AAA' and 'BBB' using this line: $this->_str[$part] = "({$str})";
which resolves to: AAA BBB (AAA BBB AAA).
UPDATE:
I believe that what you're trying to do, is to avoid the side-effect of returning this
from methods AAA()
and BBB()
- will be achieved with the following changes:
<?php
class Chain {
protected $_str = '';
protected $_part = 0;
public function __toString() {
return implode(' ', $this->_str);
}
public function AAA () {
$this->_str[$this->_part] = 'AAA';
$this->_part++;
return "AAA";
}
public function BBB () {
$this->_str[$this->_part] = 'BBB';
$this->_part++;
return "BBB";
}
public function wrap ($str) {
$part = $this->_part - 1;
$this->_str[$part] = "({$str})";
return $str;
}
}
$chain = new Chain();
$chain->AAA();
$chain->BBB();
$chain->wrap($chain->AAA());
echo $chain->__toString();
?>
Related Topics
Laravel 5.3 - How to Add Sessions to 'Api' Without Csrf
What Is the Best PHP Dom 2 Array Function
How to Get the List of Available Locales in PHP
Count of Duplicate Elements in an Array in PHP
Adding Additional Persist Calls to Preupdate Call in Symfony 2.1
PHP Array_Sum on Multi Dimensional Array
How to Display PHP Code in HTML
How Do We Implement Custom API-Only Authentication in Laravel
How to Use a PHP Includes Across Multiple Directories/Sub Directories with Relative Paths
Add New Data into PHP JSON String
Jquery UI Saving Sortable List
Getting All $_Post from Multiple Select Value
Why Does (0 == 'Hello') Return True in PHP