如何防止调用不存在的方法而出错?一样的道理,使用call魔术重载方法。
call方法原型如下:
- mixed call ( string $name , array $arguments )
当调用一个不可访问的方法(如未定义,或者不可见)时,call()会被调用。其中$name参数是要调用的方法名称。$arguments参数是一个数组,包含着要传递给方法的参数,如下所示:
网站建设
- public function call($name, $arguments) {
- switch(count($arguments)){
- case 2:
- echo $arguments[0]*$arguments[1],PHP_EOL;
- break;
- case 3:
- echo array_sum($arguments),PHP_EOL;
- break;
- default:
- echo '参数不对',PHP_EOL;
- break;
- }
- }
- $a->make(5);
- $a->make(5,6);
以上代码模拟了类似其他语言中的根据参数类型进行重载。跟call方法配套的魔术方法是callStatic。当然,使用魔术方法“防止调用不存在的方法而报错”,并不是魔术方法的本意。实际上,魔术方法使方法的动态创建变为可能,这在MVC等框架设计中是很有用的语法。假设一个控制器调用了不存在的方法,那么只要定义了call魔术方法,就能友好地处理这种情况。
试着理解代码清单1-3所示代码。这段代码通过使用callStatic这一魔术方法进行方法的动态创建和延迟绑定,实现一个简单的ORM模型。
代码清单1-3 simpleOrm.php
- <?php
- abstract class ActiveRecord {
- protected static $table;
- protected $fieldvalues;
- public $select;
- static function findById($id) {
- $query = "select *from "
- .static::$table
- ." where id=$id";
- return self::createDomain($query);
- }
- function get($fieldname) {
- return $this->fieldvalues[$fieldname];
- }
- static function callStatic($method, $args) {
- $field = preg_replace('/^findBy(\w*)$/', '${1}', $method);
- $query = "select *from "
- .static::$table
- ." where $field='$args[0]'";
- return self::createDomain($query);
- }
- private static function createDomain($query) {
- $klass = get_called_class();
- $domain = new $klass();
- $domain->fieldvalues = array();
- $domain->select = $query;
- foreach($klass::$fields as $field => $type) {
- $domain->fieldvalues[$field] = 'TODO: set from sql result';
- }
- return $domain;
- }
- }
- class Customer extends ActiveRecord {
- protected static $table = 'custdb';
- protected static $fields = array(
- 'id' => 'int',
- 'email' => 'varchar',
- 'lastname' => 'varchar'
- );
- }
- class Sales extends ActiveRecord {
- protected static $table = 'salesdb';
- protected static $fields = array(
- 'id' => 'int',
- 'item' => 'varchar',
- 'qty' => 'int'
- );
- }
- assert ("select *from custdb where id=123" ==
- Customer::findById(123)->select);
- assert ("TODO: set from sql result" ==
- Customer::findById(123)->email);
- assert ("select *from salesdb where id=321" ==
- Sales::findById(321)->select);
- assert ("select *from custdb where Lastname='Denoncourt'" ==
- Customer::findByLastname('Denoncourt')->select);
再举个类似的例子。PHP里有很多字符串函数,假如要先过滤字符串首尾的空格,再求出字符串的长度,一般会这么写:
- strlen(trim($str));
如果要实现JS里的链式操作,比如像下面这样,应该怎么实现?
- $str->trim()->strlen()
很简单,先实现一个String类,对这个类的对象调用方法进行处理时,触发call魔术方法,接着执行call_user_func即可。



