InfinityQuest - Programming Code Tutorials and Examples with Python, C++, Java, PHP, C#, JavaScript, Swift and more

Menu
  • Home
  • Sitemap

Python Programming Language Best Tutorials and Code Examples

Learn Python Right Now!
Home
PHP
Aggregating Objects in PHP
PHP

Aggregating Objects in PHP

InfinityCoder November 25, 2016

You want to compose two or more objects together so that they appear to behave as a single object.

Aggregate the objects together and use the __call() and __callStatic() magic methods to intercept method invocations and route them accordingly:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Address {
   protected $city;
 
   public function setCity($city) {
      $this->city = $city;
   }
 
   public function getCity() {
      return $this->city;
   }
}
 
class Person {
   protected $name;
   protected $address;
 
   public function __construct() {
      $this->address = new Address;
   }
 
   public function setName($name) {
      $this->name = $name;
   }
 
   public function getName() {
      return $this->name;
   }
 
   public function __call($method, $arguments) {
       if (method_exists($this->address, $method)) {
           return call_user_func_array(
               array($this->address, $method), $arguments);
       }
   }
}
 
$rasmus = new Person;
$rasmus->setName('Rasmus Lerdorf');
$rasmus->setCity('Sunnyvale');
 
print $rasmus->getName() . ' lives in ' . $rasmus->getCity() . '.';

An instance of the Address object is created during the construction of every Person.
When you invoke methods not defined in Person, the __call() method catches them and, when applicable, dispatches them using call_user_func_array().
Use __callStatic() when you need to route static methods.

In this recipe, you cannot say a Person “is an” Address or vice versa. Therefore, it doesn’t make sense for one class to extend the other.
However, it makes sense for them to be separate classes so that they provide maximum flexibility and reuse, as well as reduced duplicated code. So you check if another rule—the “has a” rule—applies.

Because a Person “has an” Address, it makes sense to aggregate the classes together.
With aggregation, one object acts as a container for one or more additional objects.

This is another way of solving the problem of multiple inheritance because you can easily piece together an object out of smaller components.
For example, a Person object can contain an Address object. Clearly, People have addresses. However, addresses aren’t unique to people; they also belong to businesses and other entities.

Therefore, instead of hardcoding address information inside of Person, it makes sense to create a separate Address class that can be used by multiple classes.
Here’s how this works in practice:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Address {
   protected $city;
 
   public function setCity($city) {
      $this->city = $city;
   }
 
   public function getCity() {
      return $this->city;
   }
}
 
class Person {
   protected $name;
   protected $address;
 
   public function __construct() {
      $this->address = new Address;
   }
 
   public function setName($name) {
      $this->name = $name;
   }
 
   public function getName() {
      return $this->name;
   }
 
   public function __call($method, $arguments) {
      if (method_exists($this->address, $method)) {
         return call_user_func_array(
             array($this->address, $method), $arguments);
      }
   }
}

The Address class stores a city and has two accessor methods to manipulate the data, setCity() and getCity().
Person has setName() and getName(), similar to Address, but it also has two other  methods: __construct() and __call().
Its constructor instantiates an Address object and stores it in a protected $address property.

This allows methods inside Person to access $address, but prevents others from talking directly to the class.
Ideally, when you call a method that exists in Address, PHP would automatically execute it. This does not occur, because Person does not extend Address.

You must write code to glue these calls to the appropriate methods yourself. Wrapper methods are one option. For example:

1
2
3
4
5
class Person {
   public function setCity($city) {
      $this->address->setCity($city);
   }
}

This setCity() method passes along its data to the setCity() method stored in $ad dress. This is simple, but it is also tedious because you must write a wrapper for every method.
Using __call() lets you automate this process by centralizing these methods into a single place:

1
2
3
4
5
6
7
8
class Person {
    public function __call($method, $arguments) {
              if (method_exists($this->address, $method)) {
                 return call_user_func_array(
                     array($this->address, $method), $arguments);
               }
    }
}

The __call() method captures any calls to undefined methods in a class. It is invoked with two arguments: the name of the method and an array holding the parameters passed to the method.

The first argument lets you see which method was called, so you can determine whether it’s appropriate to dispatch it to $address.

Here, you want to pass along the method if it’s a valid method of the Address class. Check this using method_exists(), providing the object as the first parameter and the method name as the second.
If the function returns true, you know this method is valid, so you can call it. Unfortunately, you’re still left with the burden of unwrapping the arguments out of the $argu ments array.

That can be painful. The seldom used and oddly named call_user_func_array() function solves this problem.

This function lets you call a user function and pass along arguments in an array. Its first parameter is your function name, and the second is the array of arguments.
In this case, however, you want to call an object method instead of a function. There’s a special syntax to cover this situation. Instead of passing the function name, you pass an array with two elements.

The first element is the object, and the other is the method name.
This causes call_user_func_array() to invoke the method on your object. You must then return the result of call_user_func_array() back to the original caller, or your return values will be silently discarded.
Here’s an example of Person that calls both a method defined in Person and one from Address:

1
2
3
4
5
$rasmus = new Person;
$rasmus->setName('Rasmus Lerdorf');
$rasmus->setCity('Sunnyvale');
 
print $rasmus->getName() . ' lives in ' . $rasmus->getCity() . '.';

Even though setCity() and getCity() aren’t methods of Person, you have aggregated them into that class.
You can aggregate additional objects into a single class, and also be more selective as to which methods you expose to the outside user.

This requires some basic filtering based on the method name.

Share
Tweet
Email
Prev Article
Next Article

Related Articles

Fetching a URL with Arbitrary Headers in PHP
You want to retrieve a URL that requires specific headers …

Fetching a URL with Arbitrary Headers in PHP

Accessing an Object Using Array Syntax in PHP
You have an object, but you want to be able …

Accessing an Object Using Array Syntax in PHP

About The Author

InfinityCoder
InfinityCoder

Leave a Reply

Cancel reply

Recent Tutorials InfinityQuest

  • Adding New Features to bash Using Loadable Built-ins in bash
    Adding New Features to bash Using Loadable …
    June 27, 2017 0
  • Getting to the Bottom of Things in bash
    Getting to the Bottom of Things in …
    June 27, 2017 0

Recent Comments

  • fer on Turning a Dictionary into XML in Python
  • mahesh on Turning a Dictionary into XML in Python

Categories

  • Bash
  • PHP
  • Python
  • Uncategorized

InfinityQuest - Programming Code Tutorials and Examples with Python, C++, Java, PHP, C#, JavaScript, Swift and more

About Us

Start learning your desired programming language with InfinityQuest.com.

On our website you can access any tutorial that you want with video and code examples.

We are very happy and honored that InfinityQuest.com has been listed as a recommended learning website for students.

Popular Tags

binary data python CIDR convert string into datetime python create xml from dict python dictionary into xml python how to create xml with dict in Python how to write binary data in Python IP Address read binary data python tutorial string as date object python string to datetime python

Archives

  • June 2017
  • April 2017
  • February 2017
  • January 2017
  • December 2016
  • November 2016
Copyright © 2021 InfinityQuest - Programming Code Tutorials and Examples with Python, C++, Java, PHP, C#, JavaScript, Swift and more
Programming Tutorials | Sitemap