【问题标题】:lose the globals in functions失去函数中的全局变量
【发布时间】:2015-09-30 09:47:47
【问题描述】:

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

【问题讨论】:

  • 当然,使用类是一个很好的起点。但很少有一个单一的类是一个好主意。类旨在描述特定的事物或功能,而不仅仅是 all。无论如何,使用一个类你可以摆脱全局变量。您甚至不必使用对象属性。如果这些值真的是(伪)常量,那么为什么不使用类常量呢?
  • 谢谢@arkascha,我不知道你可以在不使用对象属性的情况下使用类。你能指出一个使用类常量的例子吗?
  • $db 本身没有任何问题。习惯性地和广泛地这样做并不是出于恶作剧,而是因为它确实恰好适合常​​见情况并且不会引入无意识的抽象。 -- 现在$BLOG_ID 完全不同了。如果它相当于配置设置或确实是恒定的,那么无论哪种方式都可以摆脱它。
  • 就像class myClass { const BLOG_ID = 15; }; $useCase = myClass::BLOG_ID; 这样就不会污染全局命名空间。
  • @arkascha 在我尝试分配 BLOG_ID = $BLOG_ID 之前效果很好。它不会让我将 const 分配给现有的 var? $BLOG_ID 是在类之外定义的(在它之前)

标签: php mysql pdo globals


【解决方案1】:

有不同的方法可以解决这个问题,具体取决于你想花多少时间做这件事,以及你想做得有多“好”。

保持原样

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

归根结底,在所有函数中使用全局变量(在我看来)与拥有一个包含 50 个没有类的单独函数的文件一样多或少是不好的做法。

将全局变量传入

如果您将全局变量更改为函数参数,它可以让您在调用方法时能够准确地知道变量的值是什么以及它来自哪里,然后您将变量传递给方法并消除对全局变量的需求。

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

这可能是“最好的”方法,但也是需要最长时间才能实现的方法。老实说,我还是建议你这样做。

因此假设您的 50 个方法文件包含用于不同目的的各种方法,您可能会决定需要 1、2 或 3 个基类和 5-10 个具有目的或角色的类,例如,您可能有一个抽象设置您的 PDO(DB 类)连接的基类,然后您的 Blog 类(示例)可能会扩展 Base 类。这样,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 类只包含特定于博客文章的逻辑。任何格式化函数、安全函数等都可以在基类中,也可以在基类使用类似于 DB 的另一个辅助类中,例如格式化程序类。

你应该能够做到这一点:

<?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,可以向您的 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

【讨论】:

  • 感谢您提供的丰富信息!我想现在,我将使用您的后一个更简单的解决方案,然后开始将所有内容重写为类,因为我有时间。我想以正确的方式去做。没有太多的课程经验,但我最好现在就学习它,以便以后随着我的应用程序的增长免于麻烦。
【解决方案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;

【讨论】:

  • 谢谢,我将如何在我的函数中使用像 $db 这样的变量?我该怎么称呼他们?
  • 这对我不起作用。我假设 Singleton:: 应该是 Config::?但我无法让它从变量中输出任何数据。
  • 刚改了,有错误。我对其进行了测试,它按预期工作。
  • 正常工作,但我仍然需要从现有的 $var 定义 $db 和 $BLOG_id。这不可能吗?当我在你的课堂上尝试时,它崩溃了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-08
  • 2020-09-03
  • 1970-01-01
相关资源
最近更新 更多