## 实战1:__call()实现php对象的链式操作 #### 问题描述: > strlen(trim($str)),如果要实现类似js中的链式操作, > 比如像下面这样应该怎么实现? > $str->trim()->strlen() > #### call_user_func详解 > call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] ) : mixed > call_user_func 函数类似于一种特别的调用函数的方法, > 第一个参数callback是被调用的回调函数,其余参数是回调函数的参数 **使用方法如下:** ~~~ function sum($a,$b){ return $a + $b; } //第一个参数sum是调用的方法名 //第2个参数7和第三个参数8是sum方法调用的参数 $result = call_user_func('sum',7,8); echo $result;//输出结果15 ~~~ **如何通过类的魔术方法__call实现$str->trim()->strlen()的链式操作呢?** **实现思路:** > 首先定义一个字符串类StringHelper,构造函数直接赋值value,然后链式调用trim()和strlen()函数,通过在调用的魔法函数__call()中使用 代码实现如下: ~~~ class StringHelper{ public $value; public function __construct($str) { $this->value = $str; } public function __call($name, $arguments) { $this->value = call_user_func($name,$this->value); return $this; } } $str = new StringHelper(' hello '); $result = $str->trim()->strlen(); //1.$str->trim(),由于StringHelper类没有trim方法,这时候调用_call魔术方法 //2.__call($name, $arguments)中$name的值是trim //3.call_user_func的第一个参数值为调用的方法名trim,第二个参数就是$this->value //$str->strlen()执行流程同$str->trim() echo $result->value; ~~~ 运行结果 > 5 ## 实战2:利用__call进行PDO类的驱动设计 **PDO结构图如下:** ![](https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=801897294,545316323&fm=173&app=25&f=JPEG?w=640&h=660&s=58A03C7251464F4F1AF5C5CE000050B0) #### 第一步:实现数据库实现类Mysql,Oracle,和Sqlite 代码如下: ~~~ class Mysql{ public function select($sql){ return 'Mysql'.$sql;//模拟数据库查询 } } class Oracle{ public function select($sql){ return 'Oracle'.$sql;//模拟数据库查询 } } class Sqlite{ public function select($sql){ return 'Sqlite'.$sql;//模拟数据库查询 } } ~~~ #### 第二步:实现数据库的驱动类 > $驱动类->select(); > 驱动类并没有 select 方法,所以触发驱动类 __call() 调用,然后在__call方法实现对应类调用->select() **驱动类代码实现:** ~~~ class DbDriver{ private $dbClassName;//数据库类名 public function __construct($dbClassName) { //保存数据库类名 $this->dbClassName = $dbClassName; } /** * $驱动类->select($sql),驱动类DbDriver没有select方法,调用__call方法 * @param $name * @param $arguments */ public function __call($name, $arguments) { $dbClassName = $this->dbClassName;//类名 $dbClassObj = new $dbClassName ();//创建类对象 $functionName = $name; //暂不考虑数据类不存在的方法判断 // if(!method_exists($dbClass,$functionName)){ // return false;//未定义该方法 // } return $dbClassObj->$functionName($arguments[0]);//目前select方法只有一个参数 } } ~~~ **PDO代码运行实例:** ~~~ $dbDriver = new DbDriver('Mysql'); $result = $dbDriver->select(''); echo $result;//输出mysql $dbDriver = new DbDriver('Oracle'); $result2 = $dbDriver->select(''); echo $result2;//输出Oracle $dbDriver = new DbDriver('Sqlite'); $result3 = $dbDriver->select(''); echo $result3;//输出Sqlite ~~~ 如果日后更换了数据驱动,只需将$dbDriver = new DbDriver('Mysql'); 换成$dbDriver = new DbDriver('Sqlite'); 就可以了,代码无需做其他改动。 完整实例代码: ~~~ //数据类实现 class Mysql{ public function select($sql){ return 'Mysql'.$sql;//模拟数据库查询 } } class Oracle{ public function select($sql){ return 'Oracle'.$sql;//模拟数据库查询 } } class Sqlite{ public function select($sql){ return 'Sqlite'.$sql;//模拟数据库查询 } } //PDO驱动类实现 class DbDriver{ private $dbClassName;//数据库类名 public function __construct($dbClassName) { //保存数据库类名 $this->dbClassName = $dbClassName; } /** * $驱动类->select($sql),驱动类DbDriver没有select方法,调用__call方法 * @param $name * @param $arguments */ public function __call($name, $arguments) { $dbClassName = $this->dbClassName;//类名 $dbClassObj = new $dbClassName ();//创建类对象 $functionName = $name; //暂不考虑数据类不存在的方法判断 // if(!method_exists($dbClass,$functionName)){ // return false;//未定义该方法 // } return $dbClassObj->$functionName($arguments[0]);//目前select方法只有一个参数 } } //驱动类调用Mysql类 $dbDriver = new DbDriver('Mysql'); $result = $dbDriver->select(''); echo $result;//输出mysql //驱动类调用Oracle类 $dbDriver = new DbDriver('Oracle'); $result2 = $dbDriver->select(''); echo $result2;//输出Oracle //驱动类调用Sqlite类 $dbDriver = new DbDriver('Sqlite'); $result3 = $dbDriver->select(''); echo $result3;//输出Sqlite ~~~