2015-07-11 86 views
0

我有我用PHP編寫的自定義CMS系統。我幾乎完成將所有舊的傳統mysql_函數轉換爲PDO。 但是,我在一個文件中有很多函數,沒有類包裝器。該文件中只有大約50個功能是運行CMS所需的全部功能。許多天前,我鑽進用全局的,像這樣的壞習慣:失去全局函數

function getWidgets($widget_id){ 
global $db, $BLOG_ID; 
$stmt = $db->prepare("SELECT * FROM widget_assoc WHERE bid=? AND aid=?"); 
$stmt->execute(array($BLOG_ID, $widget_id)); 
$matches = $stmt->rowCount(); 
if($matches !== 0){ 
for($i = 0; $path[$i] = $stmt->fetch(); $i++) ; 
array_pop($path); 
return $path; 
} 
} 

的變量,比如$ DB和$ BLOG_ID需要保持恆定,儘可能多的功能依賴於這兩個瓦爾(和一些更) 我不確定這個清理工作的最佳途徑。 我可以將整個函數文件包裝到一個類中嗎? 我是否必須將所有輸出變量從函數更改爲$ this->?

我試圖找到一種無痛的方式來刪除所有的全局變量而不必重寫所有在主題中解析函數輸出的函數和模板。

我已閱讀了很多關於最近全局變差的問題,但我似乎無法用簡單的方法來實現這一點。沒有我的代碼的廣泛運用,這可能是不可能的。這就是我來這裏的原因!謝謝。

編輯: 我使用在每個CMS頁面的標題中調用的config.php。這個配置文件包含像$ BLOG_ID ='12'這樣的變量;並在網站構建中動態創建。這些變量可用於所有頁面,但爲了讓它們進入我的函數中,我必須使用全局變量。我沒有任何類的經驗,並犯了一個錯誤,只是在一個沒有類的函數文件中包含了許多函數。

+0

當然,使用一類是一個很好的點開始。但很少有一個班級是一個好主意。類是爲了描述一個特定的事物或特徵,而不僅僅是所有的。無論如何,使用一個類,你擺脫了全局。你甚至不必使用對象屬性。如果這些值真的是(僞)常量,那麼爲什麼不使用類常量? – arkascha

+0

謝謝@arkascha,我不知道你可以使用類來使用對象屬性。你能指點我一個使用類常量的例子嗎? –

+0

'$ db'本身沒有錯。這種習慣性和廣泛的做法並不是出於惡作劇,而是因爲它確實發生了適合普通情況的事情,並沒有引入無用的抽象。 - 現在'$ BLOG_ID'完全不同。如果它相當於一個配置設置或者實際上是恆定的,那麼就去掉它。 – mario

回答

3

有不同的方法可以解決這個問題,這取決於你有多少時間要花費這樣做,以及如何「以及」你想這樣做。

離開它,因爲它是

您可以將方法就可以假設它們能否正常使用的方式。根據您對不想重寫所有內容的評論,這可能是您最好的方法。

在一天結束時,在所有函數中使用全局變量(在我看來)與包含50個不包含類的獨立函數的文件相比,沒有更多或更差的做法。

傳遞全局中

如果你改變了全局變量函數參數,而不是它給你的洞察力,能夠準確地知道什麼是變量的值是和它來自哪裏,當你打電話給你的方法,那麼你將該變量傳遞給方法並刪除全局變量的需要。

類使用依賴注入/繼承

這可能是「最好」的辦法,也是一個需要最長時間來實現。無論如何,我建議你這樣做,說實話。

所以假設你的50方法文件中包含了多種用於不同目的的方法,你可以決定你需要1,2或3的基類,並且有目的或作用5-10類,例如,你可能有一個抽象基類設置您的PDO(DB類)連接,然後您的Blog類(示例)可能會擴展基類。這樣,Blog類將繼承它生成Blog條目所需的所有外部依賴關係(外部意思是說Blog類可以假定其目的是僅檢索,格式化和輸出Blog帖子 - 應該已經處理DB連接)。

一個實際的例子可能是這樣的:

/** 
* Handle your database connection, querying etc functions 
*/ 
class DB { 
    protected $_pdo; 
    public function getPdo() { 
     if (is_null($this->pdo)) { 
      $this->_pdo = new PDO(...); 
     } 
     return $this->_pdo; 
    } 

    public function __construct() { 
     return $this->getPdo(); 
    } 

    public function query($sql, $binds = []) { 
     // write a function that executes the $sql statement on the 
     // PDO property and return the result. Use $binds if it is not 
     // empty 
     $eg = $this->getPdo()->prepare($sql); 
     return $eg->execute((array) $binds); 
} 

/** 
* Create a basic framework for all purpose-classes to extend 
*/ 
abstract class Base { 
    /** 
    * "DB" property might be broad here to cover other DBs or connection 
    * methods (in theory) 
    */ 
    protected $_db; 
    public function __construct() { 
     $this->_db = new DB; 
    } 

    public function db($sql, $binds) { 
     return $this->_db->query($sql, $binds); 
    } 

    // insert other common methods here that all type-specific classes 
    // can use 
} 

現在的具體動作/角色的實現:

class Blog extends Base { 
    public function get($blogId = null) { 
     // Basic error check 
     if (empty($blogId)) { 
      throw new UnexpectedValueException('Blog post ID was missing!'); 
     } 
     return $this->db('SELECT * FROM `blogposts` WHERE blog_id = ?', $blogId); 
    } 
} 

我沒有測試過這一點,但現在的原則Blog類只包含特定於博客文章的邏輯。任何格式化函數,安全函數等都可以在Base類中,或者在Base類與DB類似的另一個輔助類中使用,例如,一個Formatter類。

你應該能夠做到這一點,那麼:

<?php 
# blogPost.php 
# - Gets a blog post 
require_once 'common.php'; // <--- include your class files, or an autoloader 

// Instantiate the class for this role 
$blog = new Blog; 

// Get the blog post 
$id = (isset($_GET['id'])) ? (int) $_GET['id'] : null; 
$post = $blog->get($id); 

// now other methods: 
$post->toHTML(); // example - function might call a template file, insert the 
       // DB results into it and output it to the browser 

這只是一個粗略的例子,但說明了如何可以結構的一組類實現具有單一作用的一類原則,以及具有單一目的的方法(例如「通過其ID獲得博客文章」)。

這樣,擴展Base的所有東西都會自動訪問數據庫(通過繼承)。如果您想嘗試從實施中刪除SQL,則可以向您的數據庫類添加一些方法來提供基本的ORM。

作出共同的價值觀

一個類

另一個短/快速選擇是建立一個單一的類,它可以起到什麼共同點所有的,將在你的包含文件中的函數,在這種情況下,DB處理程序和博客帖子ID。例如:

class Common { 
    protected static $_db; 
    protected static $_blogId; 

    public function getDb() { 
     if (is_null(static::$_db)) { 
      static::$_db = new PDO(...); 
     } 
     return static::$_db; 
    } 

    public static function getBlogId() { 
     return (int) static::$_blogId; 
    } 
    public static function setBlogId($id) { 
     static::$_blogId = (int) $id; 
    } 
} 

現在你只需要在開始呼喚你的函數之前實例化這個類,並設置博客文章ID(如果需要的話)。只要需要,PDO連接將被延遲創建。這裏

# functions.php 
require_once 'common.php'; 

function getWidgets($widget_id) { 
    $stmt = Common::getDb()->prepare('SELECT * FROM widget_assoc WHERE bid = ? AND aid = ?'); 
    $stmt->execute(array(Common::getBlogId(), $widget_id)); 
    $matches = $stmt->rowCount(); 
    if ($matches !== 0) { 
     for($i = 0; $path[$i] = $stmt->fetch(); $i++); 
     array_pop($path); 
     return $path; 
    } 
} 

你唯一的責任是設置在每個頁面上的博客文章編號,例如:

# blogPost.php 
require_once 'common.php'; 

// Manual dependency blog ID needs to be set before processing: 
$blogId = isset($_GET['blog_id']) ? (int) $_GET['blog_id'] : null; 
Common::setBlogId($blogId); 

// now you call your processing methods and perform your logic flow 
+1

感謝您提供豐富的信息!我認爲現在,我會使用你的後一個更簡單的解決方案,然後開始重寫所有類,因爲我有時間。我想以正確的方式去做。沒有太多的課程經驗,但我現在更好地瞭解它,以便隨着我的應用程序的增長,將我從麻煩中解救出來。 –

2

如果你想在多個地方使用變量,你可以爲這些變量創建一個單例。

class Config 
{ 
/** 
* @var Singleton The reference to *Singleton* instance of this class 
*/ 
private static $instance; 

public $db = 'db'; 
public $BLOG_id = 'id'; 

/** 
* Returns the *Singleton* instance of this class. 
* 
* @return Singleton The *Singleton* instance. 
*/ 
public static function getInstance() 
{ 
    if (null === static::$instance) { 
     static::$instance = new static(); 
    } 

    return static::$instance; 
} 

/** 
* Protected constructor to prevent creating a new instance of the 
* *Singleton* via the `new` operator from outside of this class. 
*/ 
protected function __construct() 
{ 
} 

} 

//To use it 
$config = Config::getInstance(); 
$config->db; 
+0

的頂部,謝謝,我將如何在我的函數中使用$ db這樣的變量?我怎麼稱呼他們? –

+0

這不適合我。我認爲Singleton ::應該是Config ::?但我不能從變量輸出任何數據。 –

+0

只是改變了它,有一個錯誤。我測試了它,它按預期工作。 –