2015-11-29 34 views
0

我正在重構一個我編寫的(過程式)PHP庫,然後回到一個輕量級的OOP框架中。我正忙於嘗試傳遞一個PDO對象在課堂中使用。這是迄今爲止我所擁有的。將PDO對象傳遞給類 - PHP致命錯誤:調用一個非對象的成員函數execute()

CONFIG.PHP

<?php 

class Config { 
    // Database Variables 
    private $db_type; 
    private $db_host; 
    private $db_user; 
    private $db_pass; 
    private $db_name; 
    private $db_path; // for sqlite database path 
    private $db_char; // charset 

    // Site Variables 
    private $s_protocol; 
    private $s_subdomain; 
    private $s_domain; 
    private $s_tld; 
    private $s_dir; 
    private $s_name; 
    private $s_description; 
    private $s_path; 
    private $s_visibility; 
    private $s_pipe; 
    private $s_apps; 
    private $s_hooks; 
    private $s_blocks; 
    private $s_assets; 

    // User Default 
    private $u_groupid; 

    public function __construct($config) { 
     $this->set($config); 
    } 

    public function set($config) { 
     if (!empty($config) && is_array($config)) { 
      foreach ($config as $k => $v) { 
       if (property_exists(get_class($this), $k)) { 
        $this->$k = $v; 
       } 
      } 
      return true; 
     } 
     else { return false; } 
    } 

    public function get($config) { 
     if (!empty($config)) { 
      return $this->$config; 
     } 
    } 

    public function domain() { 
     return $this->get('s_protocol') .'://'. $this->get('s_domain') . $this->get('s_tld') .'/'. $this->get('s_dir'); 
    } 
} 
?> 

database.php中

<?php 

class Database extends PDO { 
    private $config; 

    public function __construct($config) { 
     $this->config = $config; 
     switch($this->config->get('db_type')) { 
      case 'mysql': 
      case 'pgsql': 
       try { 
        return new PDO(
           $this->config->get('db_type') .':dbname='. $this->config->get('db_name') .';host='. $this->config->get('db_host'), 
           $this->config->get('db_user'), 
           $this->config->get('db_pass') 
        ); 
       } 
       catch(PDOException $e) { 
        die($e->getMessage()); 
       } 
       break; 
      case 'sqlite': 
       try { 
        return new PDO($this->config->get('db_type') .':'. $this->config->get('db_path')); 
       } 
       catch(PDOException $e) { 
        die($e->getMessage()); 
       } 
       break; 
      case 'firebird': 
       try { 
        return new PDO(
           $this->config->get('db_type') .':dbname='. $this->config->get('db_host') .':'. $this->config->get('db_path'), 
           $this->config->get('db_user'), 
           $this->config->get('db_pass') 
        ); 
       } 
       catch(PDOException $e) { 
        die($e->getMessage()); 
       } 
       break; 
      case 'informix': 
       try { 
        return new PDO(
           $this->config->get('db_type') .':DSN='. $this->config->get('db_name'), 
           $this->config->get('db_user'), 
           $this->config->get('db_pass') 
        ); 
       } 
       catch(PDOException $e) { 
        die($e->getMessage()); 
       } 
       break; 
      case 'oracle': 
       try { 
        return new PDO(
           'OCI:dbname='. $this->config->get('db_name') .';charset='. $this->config->get('db_char'), 
           $this->config->get('db_user'), 
           $this->config->get('db_pass') 
        ); 
       } 
       catch(PDOException $e) { 
        die($e->getMessage()); 
       } 
       break; 
     } 
    } 


} 
?> 

Auth.php

<?php 

class Auth { 
    // Set Database object 
    protected $db; 

    // User fields in users table 
    private $id; 
    private $email; 
    private $password; 
    private $firstname; 
    private $lastname; 
    private $displayname; 
    private $groupid; 
    private $ip; 
    private $created; 
    private $updated; 
    private $cookie; 
    private $sessionid; 
    private $lastlogin; 
    private $token; 
    private $active; 

    public function __construct($dbh) { 
     $this->db = $dbh; 
    } 

    public function add($params) { 
     $sql = ' 
      INSERT INTO 
       `users` (
     '; 
     $cols = array_keys($params); 
     $col_string = implode(', ', $cols); 
     $sql .= $col_string .' 
       ) 
      VALUES (
     '; 
     array_walk($cols, function(&$v, $k) { $v = ':'. $v; }); 
     $col_string = implode(', ', $cols); 

     $sql .= $col_string .' 
       ) 
     '; 
     $stmt = $this->db->prepare($sql); 
     $stmt->execute($params); 

    } 

    public function remove($params) { 

    } 

    public function update($params) { 

    } 

    public function get($params) { 

    } 

    protected function set($params) { 
     if (!empty($params) && is_array($params)) { 
      foreach ($params as $k => $v) { 
       if (property_exists(get_class($this), $k)) { 
        $this->$k = $v; 
       } 
      } 
      return true; 
     } 
     else { return false; } 
    } 
} 

?> 

的init.php

<?php 
session_start(); 
$params = array(
       'db_type' => 'mysql', 
       'db_host' => '127.0.0.1', 
       'db_user' => 'user', 
       'db_pass' => 'password', 
       'db_name' => 'database', 
       'u_groupid' => 4 
      ); 
require_once('Config.php');   $c = new Config($params); 
require_once('Database.php');  $db = new Database($c); 
require_once('Auth.php');   $u = new Auth($db); 

$user = array(
    'email' => '[email protected]', 
    'password' => md5('password'), 
    'firstname' => 'Jeff', 
    'lastname' => 'Wilson', 
    'displayname' => 'Jeff Wilson', 
    'groupid' => $c->get('u_groupid'), 
    'ip' => $_SERVER['REMOTE_ADDR'], 
    'created' => date('Y-m-d H:i:s'), 
    'sessionid' => session_id(), 
    'active' => 1, 
); 
$u->add($user); 
?> 

PHP Fatal error: Call to a member function execute() on a non-object in Auth.php on line 46

這是第46行: $stmt->execute($params);

據我知道我正確地傳遞PDO對象的驗證類。它不應該說它是一個非對象。任何人都可以看到這裏有什麼問題嗎?

+1

看起來像'$這個 - > DB->準備($ SQL)'失敗,PDO實例未設置爲ERRMODE_EXCEPTION - > $語句「是」FALSE。 – VolkerK

+0

@VolkerK - 我已經添加了這些設置,頁面仍然會拋出500內部服務器錯誤,並檢查http日誌,它仍然說同樣的事情 - PHP致命錯誤:調用一個成員函數execute()在一個非對象的身份驗證.php在線46. – Armin

回答

2

除非PDO實例設置明確用於錯誤異常報告,你必須檢查PDO::prepare

$stmt = $this->db->prepare($sql); 
if (!$stmt) { 
    // prepare failed 
    // the array returned by $this->db->errorinfo() most likely contains more info about the error 
    // don't send it unconditionally,directly to the browser, see https://www.owasp.org/index.php/Top_10_2013-A6-Sensitive_Data_Exposure 

} 
else { 
    $result = $stmt->execute($params); 
    if (!$result) { 
     // not ok 
    } 
    else { 
     // ok 
    } 
} 

編輯的返回值:我可以離開了我的答案的第一部分,因爲亞歷克斯·艾維已經wrote it。 ;-)

現場PHP僅僅落後圖像做這樣

function __internal_newDatabase() { 
    // just image the parser/compiler creates this function 
    // from your class defintion 
    $instance = array(
     'properties'=>array('config'=>null), 
     'methods'=>array('beginTransaction'=>PDO::beginTransaction, 'prepare'=>PDO::prepare, ...) 
    ); 
    // some magic function that calls a method and "replaces" $this by the second parameter 
    invoke(Database::__construct, $instance); 
    return $instance; 
} 

,當你的腳本包含new Database而是調用__internal_newDatabase()東西。這是(ooooversimplified)發生了什麼,因此你可以而不是只是通過返回一個不同的實例「改變」你的構造函數「方法」中的實例。你的構造函數應該使這個實例飛(或通過拋出異常來保護)。
您的班級數據庫來自PDO,即它應該表現爲PDO。在其他語言中,暗示必須調用基類的構造函數必須調用。 PHP不強制執行。但在這種情況下,您的數據庫實例將不可用。 Alex的答案顯示,你必須明確地呼叫父母的構造函數。

但這個班還有其他問題。 (第一個懺悔:我對class Database有偏見,在幾乎所有的在所有情況下,這只是錯誤的,因此自動爲我提出一個紅旗)
首先:鑑於它的名字和你使用它的方式,這是多餘的。這只是一個配置細節,不是從PDO派生的類。更有可能是IoC容器中的工廠和/或其他東西(如果使用的話)。
另一方面,它可能不只是一個配置細節,但可能(並可能會)導致不同的數據庫實現。 PDO不是數據庫抽象,只是一個統一的訪問層。
您的類Auth.php不關心使用的具體sql方言 - 而且這個特定的查詢很可能適用於PDO支持的所有數據庫系統。但遲早會有必須針對不同RDBMS定製的查詢。然後你的基類,可能會被稱爲像DB_Adapter並會有MySQL_Adapter extends DB_Adapter等等....

+0

當我把它放在它不給我一個500內部服務器錯誤 - 但檢查http日誌它拋出:PHP注意:未定義的屬性:Database :: $ errorinfo – Armin

+0

errorinfo不是一個屬性但是返回一個數組的方法。看到http://docs.php.net/pdo.errorinfo – VolkerK

+0

好吧!我很抱歉。這是什麼在HTTP日誌中返回 - PHP警告:PDO :: errorInfo():SQLSTATE [00000]:沒有錯誤:沒有調用PDO構造函數 – Armin

1

添加這個方法到您的數據庫類

public function getConnection() { 
     return new PDO(
      $this->config->get('db_type') .':dbname='. $this->config->get('db_name') .';host='. $this->config->get('db_host'), 
      $this->config->get('db_user'), 
      $this->config->get('db_pass') 
     ); 
    } 

備調用語句是這樣的:

$conn = $this->db->getConnection(); 
$stmt = $conn->prepare($sql); 

你$康恩必須PDO對象在這種情況下

這裏的主要問題是WHI樂$db = new Database($c);似乎是第一眼細,要求$db->prepare是不好的,因爲$dbDatabase實例,但必須是PDO

一個改進位的連接處理方式的實例是: 在你Database類有private $conn連接

class Database extends PDO { 
    private $config; 
    private $conn; 

    public function __construct($config) { 
     $this->config = $config; 
     switch($this->config->get('db_type')) { 
      case 'mysql': 
      case 'pgsql': 
       try { 
        $this->conn = new PDO(
         $this->config->get('db_type') . ':dbname=' . $this->config->get('db_name') . ';host=' . $this->config->get('db_host'), 
         $this->config->get('db_user'), 
         $this->config->get('db_pass') 
        ); 
       } catch(PDOException $e) { 
        die($e->getMessage()); 
       } 

      break; 

     // ... 

     } 
    } 

然後在同一類新的方法,以返回連接:

public function getConnection() { 
     return $this->conn; 
    } 

,最後把它叫做:

$this->db->getConnection()->prepare($sql) 
2

在數據庫類的構造函數返回一個值(PDO對象)。 __construct()函數不應該顯式返回一個值。由於您的數據庫類擴展PDO,調用父類的構造來代替:

parent::__construct(
    $this->config->get('db_type') .':dbname='. $this->config->get('db_name') .';host='. $this->config->get('db_host'), 
    $this->config->get('db_user'), 
    $this->config->get('db_pass') 
); 
相關問題