18-1跨类调用__call()和__callStatic()–PHP实战开发教程

18-1跨类调用__call()和__callStatic()–PHP实战开发教程

洁涣蔡突镰弘暖栖使图遍献少

新建目录18  新建Query.php类文件和demo1.php

18-1跨类调用__call()和__callStatic()–PHP实战开发教程第1张

Query.php为空

demo1.php

<?php
//自动加载类文件
spl_autoload_register(function($className)
{
   require "./class/".$className.".php";
});

class Db
{
    public $pdo=null;

    public function __construct($host="127.0.0.1",$user="root",$pass="root",$dbname="myuser")
    {
        try{
            $this->pdo=new PDO("mysql:host={$host};dbname={$dbname}",$user,$pass);

        }catch (PDOException $e)
        {
            echo "Connect Error:".$e->getMessage();
        }

    }
}
//实例化Db
$db=new Db('127.0.0.1','root','root','myuser');
var_dump($db);

执行,pdo连接数据库成功.

18-1跨类调用__call()和__callStatic()–PHP实战开发教程第2张


当访问不存在的方法报错:

<?php
//自动加载类文件
spl_autoload_register(function($className)
{
   require "./class/".$className.".php";
});

class Db
{
    public $pdo=null;

    public function __construct($host="127.0.0.1",$user="root",$pass="root",$dbname="myuser")
    {
        try{
            $this->pdo=new PDO("mysql:host={$host};dbname={$dbname}",$user,$pass);

        }catch (PDOException $e)
        {
            echo "Connect Error:".$e->getMessage();
        }

    }
}
//实例化Db
$db=new Db('127.0.0.1','root','root','myuser');
$db->table();

执行:

18-1跨类调用__call()和__callStatic()–PHP实战开发教程第3张

__call当访问一个不存在的动态方法时会自动触发

<?php
//自动加载类文件
spl_autoload_register(function($className)
{
   require "./class/".$className.".php";
});

class Db
{
    public $pdo=null;

    public function __construct($host="127.0.0.1",$user="root",$pass="root",$dbname="myuser")
    {
        try{
            $this->pdo=new PDO("mysql:host={$host};dbname={$dbname}",$user,$pass);

        }catch (PDOException $e)
        {
            echo "Connect Error:".$e->getMessage();
        }

    }
    public function __call($name, $args)
    {
        //$name:方法名  $args(可以取其他名称如arguments等)是数组,即参数,所以下方
        //使用implode()将数组转化为字符串
        return "方法名:".$name."参数:".implode(",",$args);
    }
}
//实例化Db
$db=new Db('127.0.0.1','root','root','myuser');
echo $db->table('staff');

执行:

18-1跨类调用__call()和__callStatic()–PHP实战开发教程第4张


跨类(域)调用实现

Query.php

<?php
//数据库查询类
class Query
{
    private $pdo=null;
    private $stmt=null;
    private $table="";
    private $fields="";
    private $where="";
    private $order="";

    //构造器
    public function __construct($pdo)
    {
        //接收外部传入的pdo对象,赋给本类的pdo
        $this->pdo=$pdo;
    }

    //设置要查询的数据表名称
    public function table($table)
    {
        echo "我是Query中的table方法";
    }

}

demo1.php

<?php
//自动加载类文件
spl_autoload_register(function($className)
{
   require "./class/".$className.".php";
});

class Db
{
    public $pdo=null;

    public function __construct($host="127.0.0.1",$user="root",$pass="root",$dbname="myuser")
    {
        try{
            $this->pdo=new PDO("mysql:host={$host};dbname={$dbname}",$user,$pass);

        }catch (PDOException $e)
        {
            echo "Connect Error:".$e->getMessage();
        }

    }
    public function __call($name, $args)
    {
        //$name:方法名  $args(可以取其他名称如arguments等)是数组,即参数,所以下方
        $query=new Query($this->pdo);//传入pdo属性  实例化Query类
        //call_user_func_array执行回调函数
        //跳转到Query类中的table($name)方法  参数为staff($args)
        return call_user_func_array([$query,$name],$args);
    }
}
//实例化Db
$db=new Db('127.0.0.1','root','root','myuser');
echo $db->table('staff');

实现原理解析:

执行$db->table(‘staff’)时,因为Db中没有table()方法,所以会调用魔术方法__call(),在魔术方法中,会跳转到Query类中的table()方法

执行结果:

18-1跨类调用__call()和__callStatic()–PHP实战开发教程第5张


非链式调用实现数据查询:

Query.php

<?php
//数据库查询类
class Query
{
    private $pdo=null;
    private $stmt=null;
    private $table="";
    private $fields="";
    private $where="";
    private $order="";

    //构造器
    public function __construct($pdo)
    {
        //接收外部传入的pdo对象,赋给本类的pdo
        $this->pdo=$pdo;
    }

    //设置要查询的数据表名称
    public function table($table)
    {
        $this->table=$table;
    }

    //设置要查询的数据表字段
    public function fields($fields)
    {
        $this->fields=$fields;
    }

}

demo1.php

<?php
//自动加载类文件
spl_autoload_register(function($className)
{
   require "./class/".$className.".php";
});

class Db
{
    public $pdo=null;

    public function __construct($host="127.0.0.1",$user="root",$pass="root",$dbname="myuser")
    {
        try{
            $this->pdo=new PDO("mysql:host={$host};dbname={$dbname}",$user,$pass);

        }catch (PDOException $e)
        {
            echo "Connect Error:".$e->getMessage();
        }

    }
    public function __call($name, $args)
    {
        //$name:方法名  $args(可以取其他名称如arguments等)是数组,即参数,所以下方
        $query=new Query($this->pdo);//传入pdo属性  实例化Query类
        //call_user_func_array执行回调函数
        //跳转到Query类中的table($name)方法  参数为staff($args)
        return call_user_func_array([$query,$name],$args);
    }
}
//实例化Db
$db=new Db('127.0.0.1','root','root','myuser');
echo $db->table('staff');
echo $db->fields('name,sex,age');

没有报错,表明正确:

18-1跨类调用__call()和__callStatic()–PHP实战开发教程第6张


链式调用实现数据查询

Query.php

<?php
//数据库查询类
class Query
{
    private $pdo=null;
    private $stmt=null;
    private $table="";
    private $fields="";
    private $where="";
    private $order="";

    //构造器
    public function __construct($pdo)
    {
        //接收外部传入的pdo对象,赋给本类的pdo
        $this->pdo=$pdo;
    }

    //设置要查询的数据表名称
    public function table($table)
    {
        $this->table=$table;
        //返回当前对象,用于链式调用
        return $this;
    }

    //设置要查询的数据表字段
    public function fields($fields)
    {
        $this->fields=$fields;
        //返回当前对象,用于链式调用
        return $this;
    }
    //设置查询条件
    public function where($where)
    {
        $this->where=$where;
        //返回当前对象,用于链式调用
        return $this;
    }

    //返回满足条件的第一条记录
    public function find()
    {
        //事实上,之前的所有方法都是在给本类的属性赋值
        //本方法做的就是拼接sql语句,执行sql语句,返回结果集
        $table=$this->table;
        $fields=$this->fields;
        $where=$this->where;

        //拼接sql语句
        $sql="select {$fields} from {$table} where {$where} limit 1";

        die($sql);
    }

}

demo1.php

<?php
//自动加载类文件
spl_autoload_register(function($className)
{
   require "./class/".$className.".php";
});

class Db
{
    public $pdo=null;

    public function __construct($host="127.0.0.1",$user="root",$pass="root",$dbname="myuser")
    {
        try{
            $this->pdo=new PDO("mysql:host={$host};dbname={$dbname}",$user,$pass);

        }catch (PDOException $e)
        {
            echo "Connect Error:".$e->getMessage();
        }

    }
    public function __call($name, $args)
    {
        //$name:方法名  $args(可以取其他名称如arguments等)是数组,即参数,所以下方
        $query=new Query($this->pdo);//传入pdo属性  实例化Query类
        //call_user_func_array执行回调函数
        //跳转到Query类中的table($name)方法  参数为staff($args)
        return call_user_func_array([$query,$name],$args);
    }
}
//实例化Db
$db=new Db('127.0.0.1','root','root','myuser');
echo $db->table('staff')->fields('name,sex,salary')->where("age < 20")->find();


拼接sql语句正确,说明逻辑没有问题

18-1跨类调用__call()和__callStatic()–PHP实战开发教程第7张


完善链式调用的单条语句查询:

Query.php

<?php
//数据库查询类
class Query
{
    private $pdo=null;
    private $stmt=null;
    private $table="";
    private $fields="";
    private $where="";
    private $order="";

    //构造器
    public function __construct($pdo)
    {
        //接收外部传入的pdo对象,赋给本类的pdo
        $this->pdo=$pdo;
    }

    //设置要查询的数据表名称
    public function table($table)
    {
        $this->table=$table;
        //返回当前对象,用于链式调用
        return $this;
    }

    //设置要查询的数据表字段
    public function fields($fields)
    {
        $this->fields=$fields;
        //返回当前对象,用于链式调用
        return $this;
    }
    //设置查询条件
    public function where($where)
    {
        $this->where=$where;
        //返回当前对象,用于链式调用
        return $this;
    }

    //返回满足条件的第一条记录
    public function find()
    {
        //事实上,之前的所有方法都是在给本类的属性赋值
        //本方法做的就是拼接sql语句,执行sql语句,返回结果集
        $table=$this->table;
        $fields=$this->fields;
        $where=$this->where;

        //拼接sql语句
        $sql="select {$fields} from {$table} where {$where} limit 1";

        //生成预处理对象:
        $this->stmt=$this->pdo->prepare($sql);
        //执行sql语句
        $this->stmt->execute();
        //返回结果集  指定为关联的一唯数组
        //只查询一条数据 使用fetch()即可
        return $this->stmt->fetch(PDO::FETCH_ASSOC);

    }

}

demo1.php

<?php
//自动加载类文件
spl_autoload_register(function($className)
{
   require "./class/".$className.".php";
});

class Db
{
    public $pdo=null;

    public function __construct($host="127.0.0.1",$user="root",$pass="root",$dbname="myuser")
    {
        try{
            $this->pdo=new PDO("mysql:host={$host};dbname={$dbname}",$user,$pass);

        }catch (PDOException $e)
        {
            echo "Connect Error:".$e->getMessage();
        }

    }
    public function __call($name, $args)
    {
        //$name:方法名  $args(可以取其他名称如arguments等)是数组,即参数,所以下方
        $query=new Query($this->pdo);//传入pdo属性  实例化Query类
        //call_user_func_array执行回调函数
        //跳转到Query类中的table($name)方法  参数为staff($args)
        return call_user_func_array([$query,$name],$args);
    }
}
//实例化Db
$db=new Db('127.0.0.1','root','root','myuser');
$row = $db->table('staff')->fields('name,age,salary')->where("age < 40")->find();
//查看单条执行结果
echo "<pre>";
print_r($row);
echo "</pre>";

执行:

18-1跨类调用__call()和__callStatic()–PHP实战开发教程第8张


__callStatic(),访问静态的方法不存在时,会调用这个魔术方法

demo1.php

<?php
//自动加载类文件
spl_autoload_register(function($className)
{
   require "./class/".$className.".php";
});

class Db
{
    public $pdo=null;

    public function __construct($host="127.0.0.1",$user="root",$pass="root",$dbname="myuser")
    {
        try{
            $this->pdo=new PDO("mysql:host={$host};dbname={$dbname}",$user,$pass);

        }catch (PDOException $e)
        {
            echo "Connect Error:".$e->getMessage();
        }

    }
    public function __call($name, $args)
    {
        //$name:方法名  $args(可以取其他名称如arguments等)是数组,即参数,所以下方
        $query=new Query($this->pdo);//传入pdo属性  实例化Query类
        //call_user_func_array执行回调函数
        //跳转到Query类中的table($name)方法  参数为staff($args)
        return call_user_func_array([$query,$name],$args);
    }
    public static function __callStatic($name, $arguments)
    {
        return "方法名:".$name." 参数:".implode(',',$arguments);
    }
}

//使用原生的mysqli
$db=mysqli_connect('127.0.0.1','root','root','myuser');
echo Db::select('staff','name,sex');

执行:

18-1跨类调用__call()和__callStatic()–PHP实战开发教程第9张


跨类调用实现多记录查询__callstatic()

Query.php

<?php
//数据库查询类
class Query
{
    private $pdo=null;
    private $stmt=null;
    private $table="";
    private $fields="";
    private $where="";
    private $order="";

    //构造器
    public function __construct($pdo)
    {
        //接收外部传入的pdo对象,赋给本类的pdo
        $this->pdo=$pdo;
    }

    //设置要查询的数据表名称
    public function table($table)
    {
        $this->table=$table;
        //返回当前对象,用于链式调用
        return $this;
    }

    //设置要查询的数据表字段
    public function fields($fields)
    {
        $this->fields=$fields;
        //返回当前对象,用于链式调用
        return $this;
    }
    //设置查询条件
    public function where($where)
    {
        $this->where=$where;
        //返回当前对象,用于链式调用
        return $this;
    }

    //返回满足条件的第一条记录
    public function find()
    {
        //事实上,之前的所有方法都是在给本类的属性赋值
        //本方法做的就是拼接sql语句,执行sql语句,返回结果集
        $table=$this->table;
        $fields=$this->fields;
        $where=$this->where;

        //拼接sql语句
        $sql="select {$fields} from {$table} where {$where} limit 1";

        //生成预处理对象:
        $this->stmt=$this->pdo->prepare($sql);
        //执行sql语句
        $this->stmt->execute();
        //返回结果集  指定为关联的一唯数组
        //只查询一条数据 使用fetch()即可
        return $this->stmt->fetch(PDO::FETCH_ASSOC);

    }

    //多条记录查询
    //注意 select()必须是静态方法
    public static function select($db,$table,$fields,$where,$order)
    {
        $sql="select {$fields} from {$table} where {$where} {$order};";
        //执行sql语句
        $res=mysqli_query($db,$sql);
        //获取结果集
        return mysqli_fetch_all($res);//返回结果集:二维数组
    }

}

demo1.php

<?php
//自动加载类文件
spl_autoload_register(function($className)
{
   require "./class/".$className.".php";
});

class Db
{
    public $pdo=null;

    public function __construct($host="127.0.0.1",$user="root",$pass="root",$dbname="myuser")
    {
        try{
            $this->pdo=new PDO("mysql:host={$host};dbname={$dbname}",$user,$pass);

        }catch (PDOException $e)
        {
            echo "Connect Error:".$e->getMessage();
        }

    }
    public function __call($name, $args)
    {
        //$name:方法名  $args(可以取其他名称如arguments等)是数组,即参数,所以下方
        $query=new Query($this->pdo);//传入pdo属性  实例化Query类
        //call_user_func_array执行回调函数
        //跳转到Query类中的table($name)方法  参数为staff($args)
        return call_user_func_array([$query,$name],$args);
    }
    public static function __callStatic($name, $arguments)
    {
        //静态调用使用名称即可
        //$name:静态方法  $arguments:参数名称
        return call_user_func_array(['Query',$name],$arguments);
    }
}

//使用原生的mysqli
$db=mysqli_connect('127.0.0.1','root','root','myuser');
$table="staff";
$fields="name,age,salary";
$where="age < 40";
$order="order by age desc";
//因为是静态调用,没有使用到__call()方法,即类中的pdo对象并没有赋值
//所以要使用自己传递的数据库连接对象
$rows = Db::select($db,$table,$fields,$where,$order);
echo "<pre/>";
print_r($rows);


执行:

18-1跨类调用__call()和__callStatic()–PHP实战开发教程第10张


廉想恃刷钎熊囤侵僵郴奠浇温