失去函数中的全局变量

时间:2015-07-11 19:51:45

标签: php mysql pdo globals

我有一个用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'之类的变量;并在站点构建时动态创建。这些变量可供所有页面使用,但为了将它们放入我的函数中,我必须使用全局。我没有任何类的经验,并且错误地将一个函数文件中的许多函数放在没有类的情况下。

2 个答案:

答案 0 :(得分:3)

您可以采用不同的方法来解决这个问题,具体取决于您希望花多少时间来完成这项工作以及如何"以及#34;你想要这样做。

保持原样

您可以按照假设它们正常工作的方式保留方法。根据你关于不想重写所有内容的评论,这可能是你最好的方法。

在一天结束时,在所有函数中使用全局变量(在我看来)与包含50个没有类的单独函数的文件相比,没有或多或少有不好的做法。

传递

中的全局变量

如果将全局变量更改为函数参数,则可以让您深入了解变量的值以及调用方法时变量的来源,然后将变量传递给方法并删除对全局变量的需要。

使用具有依赖注入/继承的类

这可能是最好的"方法,但也是最长的实现方法。无论如何,我建议你这样做是诚实的。

因此,假设您的50方法文件包含用于不同目的的各种方法,您可能决定需要1,2或3个基类和5-10个具有目的或角色的类,例如您可能有一个抽象设置PDO(DB类)连接的基类,然后您的Blog类(示例)可能会扩展Base类。这样,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类只包含特定于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

这只是一个粗略的例子,但展示了如何构建一组类来实现具有单一角色的类的原则,以及具有单一目的的方法(例如&#34;通过其ID&#34;)获取博客文章。

这样,扩展Base的所有内容都将自动访问数据库(通过继承)。如果要尝试从实现中删除SQL,可以向DB类添加一些提供基本ORM的方法。

为常用值创建单个类

另一个简短/快速选项是创建一个单独的类,它可以为您所包含文件中的所有函数提供任何通用内容,在本例中为数据库处理程序和博客文章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;
    }
}

此处您唯一的责任是在每个页面上设置博客帖子ID,例如:

# 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;