Skip to main content

PHP Method Chaining

Introduction​

Method chaining is a powerful technique in Object-Oriented Programming (OOP) that allows you to call multiple methods on the same object in a single statement. This pattern is sometimes referred to as a "fluent interface" and can make your code more readable, concise, and elegant.

Instead of calling methods separately like this:

php
$object->method1();
$object->method2();
$object->method3();

Method chaining allows you to write:

php
$object->method1()->method2()->method3();

In this tutorial, we'll explore how method chaining works in PHP, why it's useful, and how to implement it in your own classes.

How Method Chaining Works​

The key concept behind method chaining is simple: each method in the chain must return an object that the next method can be called on. Typically, methods return the current object instance (using the $this keyword) to enable chaining.

Basic Implementation​

Here's a simple example of a class that supports method chaining:

php
<?php
class Calculator {
private $result = 0;

public function add($value) {
$this->result += $value;
return $this; // Return the current object instance
}

public function subtract($value) {
$this->result -= $value;
return $this; // Return the current object instance
}

public function multiply($value) {
$this->result *= $value;
return $this; // Return the current object instance
}

public function divide($value) {
if ($value != 0) {
$this->result /= $value;
}
return $this; // Return the current object instance
}

public function getResult() {
return $this->result;
}
}

// Usage example
$calculator = new Calculator();
$result = $calculator->add(5)
->multiply(2)
->add(10)
->divide(5)
->getResult();

echo "Result: " . $result; // Output: Result: 4

In this example:

  1. We create a Calculator class with methods for basic arithmetic operations
  2. Each method modifies the internal $result property and returns $this
  3. The final getResult() method returns the actual value

Benefits of Method Chaining​

Method chaining offers several advantages:

  1. Improved readability: Complex operations can be written as a single, readable sequence
  2. Reduced code verbosity: No need to repeat the object variable name for each operation
  3. More intuitive API: Allows for expressing operations in a natural, fluent way
  4. More maintainable code: Changes to the sequence can be made more easily

Common Use Cases​

Building Query Constructors​

One of the most popular applications of method chaining is in query builders:

php
<?php
class QueryBuilder {
private $table;
private $conditions = [];
private $fields = ['*'];
private $limit;

public function table($table) {
$this->table = $table;
return $this;
}

public function where($field, $operator, $value) {
$this->conditions[] = "$field $operator '$value'";
return $this;
}

public function select($fields) {
if (is_array($fields)) {
$this->fields = $fields;
} else {
$this->fields = func_get_args();
}
return $this;
}

public function limit($limit) {
$this->limit = $limit;
return $this;
}

public function build() {
$query = "SELECT " . implode(', ', $this->fields) . " FROM {$this->table}";

if (!empty($this->conditions)) {
$query .= " WHERE " . implode(' AND ', $this->conditions);
}

if ($this->limit) {
$query .= " LIMIT {$this->limit}";
}

return $query;
}
}

// Usage
$query = (new QueryBuilder())
->table('users')
->select('id', 'name', 'email')
->where('age', '>', 18)
->where('status', '=', 'active')
->limit(10)
->build();

echo $query;
// Output: SELECT id, name, email FROM users WHERE age > '18' AND status = 'active' LIMIT 10

Form Validators​

Method chaining is also useful for validation:

php
<?php
class Validator {
private $value;
private $field;
private $errors = [];

public function __construct($field, $value) {
$this->field = $field;
$this->value = $value;
}

public function required() {
if (empty($this->value)) {
$this->errors[] = "$this->field is required";
}
return $this;
}

public function minLength($length) {
if (strlen($this->value) < $length) {
$this->errors[] = "$this->field must be at least $length characters";
}
return $this;
}

public function maxLength($length) {
if (strlen($this->value) > $length) {
$this->errors[] = "$this->field must not exceed $length characters";
}
return $this;
}

public function email() {
if (!filter_var($this->value, FILTER_VALIDATE_EMAIL)) {
$this->errors[] = "$this->field must be a valid email";
}
return $this;
}

public function hasErrors() {
return !empty($this->errors);
}

public function getErrors() {
return $this->errors;
}
}

// Usage
$email = "invalid-email";
$validator = new Validator("Email", $email);
$validator->required()->email()->minLength(5);

if ($validator->hasErrors()) {
print_r($validator->getErrors());
}
// Output: Array ( [0] => Email must be a valid email )

How to Implement Method Chaining in Your Classes​

Follow these steps to implement method chaining in your own classes:

  1. Return $this from methods: The key is to return the current object instance from methods that you want to chain.

  2. Design methods that modify state: Each chained method should perform a specific operation and update the object's internal state.

  3. Include a terminating method: Often, you'll need a final method that returns the actual result (like getResult() or build()).

Here's a simple diagrammatic representation of how method chaining works:

Best Practices​

  1. Return $this consistently: Make sure all methods that should be chainable return $this.

  2. Document chainable methods: Clearly document which methods support chaining.

  3. Be careful with error handling: In a chain, if one method fails, it could affect subsequent methods. Consider implementing error state tracking.

  4. Design for immutability when appropriate: For some applications, creating a new object instance instead of modifying the existing one can be safer.

  5. Keep methods focused: Each method in the chain should do one thing well.

Practical Example: Building a Text Processor​

Let's implement a more complex example: a text processor that can perform various string operations through method chaining:

php
<?php
class TextProcessor {
private $text;

public function __construct($text = '') {
$this->text = $text;
}

public function append($string) {
$this->text .= $string;
return $this;
}

public function prepend($string) {
$this->text = $string . $this->text;
return $this;
}

public function toUpperCase() {
$this->text = strtoupper($this->text);
return $this;
}

public function toLowerCase() {
$this->text = strtolower($this->text);
return $this;
}

public function replace($search, $replace) {
$this->text = str_replace($search, $replace, $this->text);
return $this;
}

public function truncate($length, $suffix = '...') {
if (strlen($this->text) > $length) {
$this->text = substr($this->text, 0, $length) . $suffix;
}
return $this;
}

public function trim() {
$this->text = trim($this->text);
return $this;
}

public function getText() {
return $this->text;
}
}

// Usage
$processor = new TextProcessor(" Hello, World! ");
$result = $processor->trim()
->replace("World", "PHP")
->append(" Method chaining is powerful.")
->toUpperCase()
->truncate(25)
->getText();

echo $result; // Output: HELLO, PHP! METHOD CHAIN...

Summary​

Method chaining is a powerful technique in PHP OOP that allows for more concise, readable, and elegant code. By returning $this from methods, you can create fluent interfaces that make your API more intuitive and your code more maintainable.

Key points to remember:

  • Return $this from methods to enable chaining
  • Use a terminating method to return the final result
  • Method chaining is particularly useful for builder patterns, validators, and query constructors
  • Well-designed method chains can significantly improve code readability

Exercises​

  1. Modify the Calculator class to add methods for calculating the square root, power, and absolute value.

  2. Create a StringFormatter class with chainable methods for formatting strings (capitalize, camelCase, snake_case, etc.).

  3. Build a simple HTTP request builder class that uses method chaining to set headers, query parameters, and request body.

Additional Resources​



If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)