首页 > 解决方案 > 在 PHP 方法中使用函数

问题描述

我有一个多年来一直在使用的 db 类。今天我注意到一件有趣的事。

function example_function($key)
{
    global $db; // db variable - class
    $a_id = 1; // example
    $admins = $db->table('admins')->where('id','=',$a_id)->order('id','desc')->limit(1)->get();
    $admin_data = $admins['data'][0];
    $return = $admin_data[$key];
    return $return;
}

$id = example_function('id');
$new = $db->table('rooms')->where('id','=',$id)->order('id','desc')->limit(2)->get();
print_r($new);
//WORKS


$new = $db->table('rooms')->where('id','=',2)->order('id','desc')->limit(2)->get();
print_r($new);
//WORKS

$new = $db->table('rooms')->where('id','=',example_function('id'))->order('id','desc')->limit(2)->get();
print_r($new);
//NOT WORKING ?

->where('id','=',example_function('id'))->

当我在这部分使用外部函数时,事情变得一团糟。

但以下也有效。在相关功能中对 db 进行另一项操作时,事情会变得一团糟。

function example_function($key)
{
    return '1';
}


$new = $db->table('rooms')->where('id','=',example_function('id'))->order('id','desc')->limit(2)->get();

是全局使用的问题吗?还是数据库类错了?

数据库类

class DB
{
    protected $connect;
    protected $db_database = DB_DATABASE;
    protected $db_host = DB_HOST;
    protected $db_username = DB_USERNAME;
    protected $db_password = DB_PASSWORD;
    protected $db_name = DB_NAME;
    protected $db_prefix = DB_PREFIX;
    
    
    //parameters
    
    protected $error = null;
    protected $last_id = null;
    protected $query = null;
    protected $from = null;
    protected $select = '*';
    protected $where = null;
    protected $group = null;
    protected $order = null;
    protected $limit = null;
    protected $pagination_type = false;
    protected $per_page = 0;
    protected $get_arr = null;
    function __construct() 
    {      
        try {
             $this->connect = new PDO("{$this->db_database}:host={$this->db_host};dbname={$this->db_name};charset=utf8mb4", "{$this->db_username}", "{$this->db_password}");
             $this->connect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
             $this->connect->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES utf8mb4");
        } catch ( PDOException $e ){
             echo  "<b>Veritabanı bağlantısı sağlanamadı! - Hata Kodu: </b>".$e->getMessage();
             exit;
        }
    }
    public function now($type="datetime")
    {
        switch($type)
        {
            case 'timestamp';
            return time();
            case 'date';
            return date('-m-d');
            case 'datetime';
            return date('Y-m-d H:i:s');
            default:
            return date('Y-m-d H:i:s');
            
        }
        
    }
    public function m_empty($data)
    {
        if($data=='' and $data!='0')
        {
            return true;
        }
        else
        {
            return false;
        }
        
    }
    public function query($query,$arr=false)
    {
        $this->reset();
        if($query!="")
        {
            
            try 
            {
                
                $run =  $this->connect->prepare($query);
                if($arr)
                {
                    if(is_array($arr))
                    {
                        $run->execute($arr);
                        return $run;
                    }
                    else
                    {
                        $run->execute(array($arr));
                        return $run;
                    }
                }
                else
                {
                    $run->execute();
                    return $run;
                }
            } catch (PDOException $e) {
                $this->error = $e->getMessage();
                return false;
            }
        }
        
        return false;
    }
    public function table($table)
    {
         $this->from = $this->db_prefix . $table;
        return $this;
    }
    public function select($fields)
    {
        $this->select = $fields;
        return $this;
    }
    public function where($field,$op='=',$values,$and="AND")
    {
        if(!$this->m_empty($field))
        {
            if(is_null($this->where))
            {
                $this->where="{$field} {$op} ?";
                $this->get_arr[] = $values;
            }
            else
            {
                
                $this->where.=" {$and} {$field} {$op} ?";
                $this->get_arr[] = $values;
            }
        }
        return $this;
    }
    public function where_set($field,$op='=',$values,$and="AND")
    {
        if(!$this->m_empty($field) and !$this->m_empty($values))
        {
            if(is_null($this->where))
            {
                $this->where="{$field} {$op} {$values}";
            }
            else
            {
                
                $this->where.=" {$and} {$field} {$op} {$values}";
            }
        }
        return $this;
    }
    public function group($fields)
    {
        if(is_null($this->group))
        {
            $this->group = $fields;
        }
        else
        {
            $this->group .=",{$fields}";
        
        }
        return $this;
    }
    public function order($field,$type="desc")
    {
        if(!is_null($field))
        {
            if(is_null($this->order))
            {
                if(strtolower($field)=="rand()")
                {
                    $this->order = $field;
                }
                else
                {
                    $this->order = $field.' '.strtoupper($type);
                }
            }
            else
            {
                if(strtolower($field)=="rand()")
                {
                    $this->order .=",{$fields}";
                }
                else
                {
                    $this->order .= ','.$field.' '.strtoupper($type);
                }
            }
        
        }
        return $this;
    }
    public function limit($start,$end=null)
    {
        if(is_null($end))
        {
            if(is_numeric($start))
            {
                $this->limit = $start;
            }
        }
        else
        {
            if(is_numeric($start) and is_numeric($end))
            {
                $this->limit = "{$start},{$end}";
            }
        
        }
        return $this;
    }
    public function pagination($per_page)
    {
        if(is_numeric($per_page))
        {
            $this->per_page = $per_page;
            $this->pagination_type = true;
        }
        return $this;
    }
    public function get()
    {
        $query = "SELECT {$this->select} FROM {$this->from}";
        if(!is_null($this->where))
        {
            $query.=" where {$this->where}";
        }
        if(!is_null($this->group))
        {
            $query.=" group by {$this->group}";
        }
        if(!is_null($this->order))
        {
            $query.=" order by {$this->order}";
        }
        if($this->pagination_type)
        {
            $c_per_page = $this->per_page;
            $c_arr = $this->get_arr;
            $extra_query = preg_replace("#SELECT (.*?) FROM#","SELECT COUNT(*) as total_count FROM",$query);
            $all = $this->query($extra_query,$this->get_arr);
            if($all)
            {
                    $total_count = $all->fetchAll(PDO::FETCH_ASSOC)[0]['total_count'];
                    $page_count = ceil($total_count/$c_per_page);
                    $current_page = (integer)m_u_g(DB_PAGINATION_GET) ? (integer)m_u_g(DB_PAGINATION_GET) : 1;
                    $current_limit=($current_page - 1) * $c_per_page;
                    $query = $query." limit {$current_limit},{$c_per_page}";
                    $current_rows = $this->query($query,$c_arr);
                    $data = $current_rows->fetchAll(PDO::FETCH_ASSOC);
                    $return = array();
                    $return['total_count'] = $total_count;
                    $return['current_count'] = count($data);
                    $return['total_page'] = $page_count;
                    $return['current_page'] = $current_page;
                    $return['data'] = $data;
                    return $return;
            }
            else
            {
                return false;
            }
        }
        else
        {
            if(!is_null($this->limit))
            {
                $query.=" limit {$this->limit}";
            }
            
            $gets = $this->query($query,$this->get_arr);
            if($gets)
            {
                    $data = $gets->fetchAll(PDO::FETCH_ASSOC);
                    $return = array();
                    $return["total_count"] = count($data);
                    $return["data"] = $data;
                    return $return;
            }
            else
            {
                return false;
            }
        }
    }
    public function get_var($field)
    {
        if($this->m_empty($field))
        {
            return false;
        }
        else
        {
            $query = "SELECT {$field} FROM {$this->from}";
            if(!is_null($this->where))
            {
                $query.=" where {$this->where}";
            }
            if(!is_null($this->order))
            {
                $query.=" order by id desc limit 1";
            }
            $gets = $this->query($query,$this->get_arr);
            if($gets)
            {
                    return  $gets->fetchAll(PDO::FETCH_ASSOC)[0][$field];
            }
            else
            {
                return false;
            }
        }
        
    }
    public function get_vars($table,$where,$var,$type=true)
    {
        if($type)
        {
                $data = $this->connect->query("select $var from $table where $where order by id desc limit 1")->fetch(PDO::FETCH_ASSOC);
        }
        else
        {
               $data = $this->connect->query("select $var from $table where $where")->fetch(PDO::FETCH_ASSOC);
        }
    
        return $data["$var"];
        
    }
    public function count()
    {
        $grouped = false;
        $query = "SELECT count(*) as total_count FROM {$this->from}";
        if(!is_null($this->where))
        {
            $query.=" where {$this->where}";
        }
        if(!is_null($this->group))
        {
            $grouped = true;
            $query.=" group by {$this->group}";
        }
        if(!is_null($this->order))
        {
            $query.=" order by {$this->order}";
        }
        $gets = $this->query($query,$this->get_arr);
        if($gets)
        {
                if($grouped)
                {
                    $count = 0;
                    foreach($gets->fetchAll(PDO::FETCH_ASSOC) as $counts)
                    {
                        $count = $count+$counts["total_count"];
                    }
                    return $count;
                }
                else
                {
                    return $gets->fetch(PDO::FETCH_ASSOC)["total_count"];
                }
        }
        else
        {
            return false;
        }
    }
    public function insert(array $data)
    {
        if(is_array($data))
        {
            $query = 'INSERT INTO '.$this->from;
            $keys = array_keys($data);
            $query.=' ('.implode(',',$keys).') values (';
            $query_add='';
            $values = array_values($data);
            foreach($values as $val)
            {
                $query_add.='?,';
            }
            $query_add = trim($query_add,',');
            $query.=$query_add.')';
            if($this->query($query,$values))
            {
                
                $this->last_id = $this->connect->lastInsertId();
                return $this->last_id;
            }
            else
            {
                
                return false;
            }
        }
        return false;

    }
    public function update(array $data)
    {
        if(is_array($data))
        {
            $query = "UPDATE {$this->from} set";
            $keys = array_keys($data);
            $values = array_values($data);
            $query_add = '';
            foreach($keys as $key)
            {
                $query_add.=" {$key} = ?,";
            }
            $query_add = trim($query_add,',');
            $query.=$query_add;
            
            if(!is_null($this->where))
            {
                $query.=" where {$this->where}";
            }
            $new = array_merge($values,$this->get_arr);
            if($this->query($query,$new))
            {
                
                return true;
            }
            else
            {
                
                return false;
            }
        }
        return false;

    }
    public function delete()
    {
        $query = "DELETE FROM {$this->from}";
        if(!is_null($this->where))
        {
            $query.=" where {$this->where}";
        }
        if($this->query($query,$this->get_arr))
        {
            
            return true;
        }
        else
        {
            
            return false;
        }

    }
    public function error()
    {
        return $this->error;
        
    }
    public function last_id()
    {
        return $this->last_id;
    }
    protected function reset()
    {
        $this->error = null;
        $this->last_id = null;
        $this->query = null;
        $this->from = null;
        $this->select = '*';
        $this->where = null;
        $this->group = null;
        $this->order = null;
        $this->limit = null;
        $this->pagination_type = false;
        $this->per_page = 0;
        $this->get_arr = null;
    }
}
$db = new DB();
?>```

标签: php

解决方案


该问题是由在完成所需查询之前更改可变对象属性引起的Db::get()。导致在类Db::$table的同一实例上调用时更改属性Db

这是不鼓励使用 的最佳实践背后的原因之一global,因为当前变量状态的意图/上下文不容易确定。

你的代码发生了什么是

Db::$table = 'rooms'
Db::$table = 'admin'
Db::$where = 'admin.id =  1'
Db::get() //SELECT * FROM admin WHERE admin.id = 1
Db::$table = null
Db::$where = null
Db::$where = 'rooms.id = 1'
Db::get() //SELECT * FROM WHERE rooms.id = 1

为避免此问题并确保所需的操作顺序,您需要Db::get()在调用任何其他DB修改调用时发出的查询属性的方法之前调用DB::get()

$roomId = example_function('id'); // (int) 1
//SELECT * FROM admin WHERE id = 1

$db->table('rooms')->where('id','=', $roomId)->get(); 
//SELECT * FROM rooms WHERE id = 1

或者,为了确保保留两个单独查询的意图,您需要DB为第二个查询创建一个单独的实例。但是,这也会PDO使用您当前的代码创建一个单独的实例。

为了避免第二个实例,PDO您必须更改$this->connect为静态属性并使用toself::$connect重构任何方法。静态属性将确保每次调用时都只存在一个 PDO 实例。$this->connectself::$connectnew Db()

class Db
{
    protected static $connect;

    //...

    public function __construct() 
    {      
        try {
            if (!isset(self::$connect)) {
                self::$connect = new PDO("{$this->db_database}:host={$this->db_host};dbname={$this->db_name};charset=utf8mb4", "{$this->db_username}", "{$this->db_password}");
                self::$connect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                self::$connect->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES utf8mb4");
            }
        } catch ( PDOException $e ){
            echo  "<b>Veritabanı bağlantısı sağlanamadı! - Hata Kodu: </b>".$e->getMessage();
            exit;
        }
    }

   //...
}

global $db;然后将应用程序中的所有用法替换为$db = new Db();.

这种方法还将确保您的整个代码库免受可变对象属性覆盖问题的影响,并更好地遵循最佳实践。

function example_function($key)
{
    $db = new Db();
    $a_id = 1; // example
    $admins = $db->table('admins')->where('id','=',$a_id)->order('id','desc')->limit(1)->get();
    $admin_data = $admins['data'][0];
    $return = $admin_data[$key];
    return $return;
}

$new = $db->table('rooms')->where('id','=', example_function('id'))->get();

另一种方法可能是用于clone解决可变对象问题。但是,usingclone会对PDOinstance产生不利影响,因为它位于Db类中。很可能需要您将 PDO 连接代码与查询生成器代码分开。

function example_function($key)
{
    global $db;
    $db2 = clone $db;
    $a_id = 1; // example
    $admins = $db2->table('admins')->where('id','=',$a_id)->order('id','desc')->limit(1)->get();
    //...
}

推荐阅读