袁来如此的工作笔记
袁来如此的工作笔记
竹杖芒鞋轻胜马,谁怕? 一蓑烟雨任平生。

php7魔术方法__call的最佳实战

浏览量:176


实战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结构图如下:


第一步:实现数据库实现类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 



打赏