__call() マジックメソッドを使用したアクセッサの定義
今回は __call() メソッドを利用したアクセッサの定義を試みます。なお、PHP においては __get()メソッド と __set() メソッドでもクラスプロパティへアクセス可能です。 __call() メソッドを利用することで「get〜」もしくは「set〜」といった形での動的なアクセッサ定義が可能になります。
1. Sample Code
複数の Employee オブジェクト(of Person namespace)を持つ Marketing オブジェクト(of Department namespace)を生成し、イテレータによる走査を試みます。 また、両オブジェクトはともにアクセッサを提供するトレイト Accessable を継承します。
2. Employee Class
namespace Person; // A trait which furnishes some dynamic accessors to Employee class. trait Accessable { public function __call($method, $args) { // - Evaluate the expression in back of OR operator lazily when a method given matches the regular expression. // - Store the prefix of accessor and the target property in an associative array, which have the key "accessor" and "property" respectively. if (!preg_match('/(?P<accessor>set|get)(?P<property>[A-Z][a-zA-Z0-9]*)/', $method, $match) || !property_exists(__CLASS__, $match['property'] = lcfirst($match['property']))) { // An exception occurs when an undefined method is requested. throw new \BadMethodCallException(sprintf("'%s' does not exist in '%s'.", $method, get_class(__CLASS__))); } switch ($match['accessor']) { case 'get': return $this->{$match['property']}; case 'set': if (!$args) { throw new \InvalidArgumentException(sprintf("'%s' requires an argument value.", $method)); } $this->{$match['property']} = $args[0]; return $this; } } } class Employee { use Accessable; private $firstName = ''; private $givenName = ''; private $id = ''; // Employee ID private $email = ''; function __construct($id, $firstName, $givenName, $email) { $this->id = $id; $this->firstName = $firstName; $this->givenName = $givenName; $this->email = $email; } }
3. Marketing Class
namespace Department; // A trait which furnishes some dynamic accessors to Marketing class. trait Accessable { public function __call($method, $args) { if (!preg_match('/(?P<accessor>addTo|set|get)(?P<property>[A-Z][a-zA-Z0-9]*)/', $method, $match) || !property_exists(__CLASS__, $match['property'] = lcfirst($match['property']))) { throw new \BadMethodCallException(sprintf("'%s' does not exist in '%s'.", $method, get_class(__CLASS__))); } switch ($match['accessor']) { case 'get': return $this->{$match['property']}; case 'set': if (!$args) { throw new \InvalidArgumentException(sprintf("'%s' requires an argument value.", $method)); } if (get_class($this->{$match['property']}) === 'ArrayObject') { throw new \BadMethodCallException("Accessing array with SET accessor is prohibited."); } else { $this->{$match['property']} = $args[0]; } return $this; case 'addTo': if (!$args) { throw new \InvalidArgumentException(sprintf("'%s' requires an argument value.", $method)); } $this->{$match['property']}[] = $args[0]; return $this; } } } class Marketing { use Accessable; private $name = ''; // Department Name private $employees = NULL; function __construct($name) { $this->name = $name; $this->employees = new \ArrayObject(); } function getEmployeesIterator() { return new \ArrayIterator($this->employees); } }
4. Result
定義したアクセッサを使ってみましょう。
require_once('person.php'); require_once('department.php'); use Department\Marketing as Marketing; use Person\Employee as Employee; $prd = new Marketing('Public Relations Department'); // Add Employee objects to Marketing object by using addToEmployees() method defined as an accessor dynamically. try { $prd->addToEmployees(new Employee('0000000001', 'Kevin', 'Cooper', '[email protected]')); $prd->addToEmployees(new Employee('0000000002', 'Ana', 'Tyler', '[email protected]')); $prd->addToEmployees(new Employee('0000000003', 'John', 'Monroy', '[email protected]')); $prd->addToEmployees(new Employee('0000000004', 'Era', 'Draper', '[email protected]')); $prd->addToEmployees(new Employee('0000000005', 'Florence', 'Brown', '[email protected]')); } catch (Exception $e) { // exception handling // ... } // Enumerate all properties of Employee object by using its get/set accessor. $it = $prd->getEmployeesIterator(); echo sprintf("[%s]\n", $prd->getName()); foreach($it as $key => $val) { echo sprintf("ID: %s\n", $val->getId()); echo sprintf("Name: %s %s\n", $val->getFirstName(), $val->getGivenName()); echo sprintf("E-mail: %s\n", $val->getEmail()); }
出力結果:
[Public Relations Department] ID: 0000000001 Name: Kevin Cooper E-mail: [email protected] ID: 0000000002 Name: Ana Tyler E-mail: [email protected] ID: 0000000003 Name: John Monroy E-mail: [email protected] ID: 0000000004 Name: Era Draper E-mail: [email protected] ID: 0000000005 Name: Florence Brown E-mail: [email protected]
References
Accessors (Getter/Setter) and Singleton Traits in PHP










