在函数中丢失全局变量
lose the globals in functions
我有一个用 PHP 编写的自定义 CMS 系统。我几乎完成了将所有旧的遗留 mysql_ 函数转换为 PDO 的工作。
但是,我在一个文件中有很多函数,没有 class 包装器。文件中只有大约 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 这样的变量需要保持不变,因为许多函数依赖于这两个变量(还有一些)
我不确定完成这项清理工作的最佳方法。
我可以将整个函数文件打包成一个 class 吗?
我是否必须将所有输出变量从函数更改为 $this-> ?
我正在尝试找到一种轻松的方法来删除所有全局变量,而不必重写在主题中解析函数输出的所有函数和模板。
我最近读了很多关于全局变量有多么糟糕的文章,但我似乎无法想出一种简化的方法来实现这一点。如果不对我的代码进行大量修改,这可能是不可能的。这就是我来这里的原因!谢谢
编辑:
我使用在每个 CMS 页面的页眉中调用的 config.php。此配置文件包含类似 $BLOG_ID = '12' 的变量;并在站点构建时动态创建。这些变量可用于所有页面,但要将它们放入我的函数中,我必须使用 global。我对 classes 没有任何经验,并且犯了一个错误,就是将许多函数放在一个没有 classes 的函数文件中。
如果你想在多个地方使用变量,你可以为这些变量创建一个单例。
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;
有多种方法可以解决这个问题,具体取决于您想花多少时间以及 "well" 您想要如何做。
保持原样
您可以按照他们假设工作正常的方式保留该方法。根据您关于不想重写所有内容的评论,这可能是您最好的方法。
归根结底,(在我看来)在所有函数中使用全局变量与拥有一个包含 50 个单独函数但没有 classes 的文件一样糟糕。
将全局变量传入
如果您将全局变量更改为函数参数,它会让您有洞察力,能够在您调用方法时准确地知道变量的值是什么以及它来自哪里,然后将变量传递给方法并删除对全局变量的需要。
使用 classes 依赖 injection/inheritance
这可能是 "best" 方法,但也是实现时间最长的方法。老实说,我建议你还是这样做。
因此假设您的 50 种方法文件包含多种用于不同目的的方法,您可能会决定需要 1、2 或 3 个碱基 classes 和 5-10 个 classes一个目的或角色,例如,您可能有一个抽象的 Base class 来设置您的 PDO (DB class) 连接,然后您的博客 class (示例)可能会扩展 Base class。这样,Blog class 将继承生成 Blog 条目所需的所有外部依赖项(外部意味着 Blog class 可能假设其目的是检索、格式化和输出 Blog post 仅 - 应该已经处理了数据库连接。
一个实际的例子可能是这样的:
/**
* 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
}
现具体执行action/role:
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);
}
}
NOTE 这个我没测试过,不过现在的原则是Blog class 只包含Blog 特有的逻辑post.任何格式化函数、安全函数等都可以在 Base class 中或在另一个辅助 class 中,Base class 使用类似于 DB,例如格式化程序 class.
你应该可以做到这一点:
<?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
这只是一个粗略的例子,但展示了您如何可以构建一组classes来实现class具有单一角色和具有单一目的的方法(例如 "get a blog post by its ID")。
这样,所有扩展 Base 的东西都将自动访问数据库(通过继承)。如果您想尝试从实现中删除 SQL,您可以向您的数据库 class 添加一些方法来提供基本的 ORM。
为通用值制作一个class
另一个 short/quick 选项是创建一个 class 可以为您包含的文件中的所有函数提供任何共同的东西,在本例中是数据库处理程序和博客post 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;
}
}
现在您只需在开始调用您的函数之前实例化此 class,并设置博客 post 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;
}
}
您唯一的责任是在每个页面上设置博客 post 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
我有一个用 PHP 编写的自定义 CMS 系统。我几乎完成了将所有旧的遗留 mysql_ 函数转换为 PDO 的工作。 但是,我在一个文件中有很多函数,没有 class 包装器。文件中只有大约 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 这样的变量需要保持不变,因为许多函数依赖于这两个变量(还有一些) 我不确定完成这项清理工作的最佳方法。 我可以将整个函数文件打包成一个 class 吗? 我是否必须将所有输出变量从函数更改为 $this-> ?
我正在尝试找到一种轻松的方法来删除所有全局变量,而不必重写在主题中解析函数输出的所有函数和模板。
我最近读了很多关于全局变量有多么糟糕的文章,但我似乎无法想出一种简化的方法来实现这一点。如果不对我的代码进行大量修改,这可能是不可能的。这就是我来这里的原因!谢谢
编辑: 我使用在每个 CMS 页面的页眉中调用的 config.php。此配置文件包含类似 $BLOG_ID = '12' 的变量;并在站点构建时动态创建。这些变量可用于所有页面,但要将它们放入我的函数中,我必须使用 global。我对 classes 没有任何经验,并且犯了一个错误,就是将许多函数放在一个没有 classes 的函数文件中。
如果你想在多个地方使用变量,你可以为这些变量创建一个单例。
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;
有多种方法可以解决这个问题,具体取决于您想花多少时间以及 "well" 您想要如何做。
保持原样
您可以按照他们假设工作正常的方式保留该方法。根据您关于不想重写所有内容的评论,这可能是您最好的方法。
归根结底,(在我看来)在所有函数中使用全局变量与拥有一个包含 50 个单独函数但没有 classes 的文件一样糟糕。
将全局变量传入
如果您将全局变量更改为函数参数,它会让您有洞察力,能够在您调用方法时准确地知道变量的值是什么以及它来自哪里,然后将变量传递给方法并删除对全局变量的需要。
使用 classes 依赖 injection/inheritance
这可能是 "best" 方法,但也是实现时间最长的方法。老实说,我建议你还是这样做。
因此假设您的 50 种方法文件包含多种用于不同目的的方法,您可能会决定需要 1、2 或 3 个碱基 classes 和 5-10 个 classes一个目的或角色,例如,您可能有一个抽象的 Base class 来设置您的 PDO (DB class) 连接,然后您的博客 class (示例)可能会扩展 Base class。这样,Blog class 将继承生成 Blog 条目所需的所有外部依赖项(外部意味着 Blog class 可能假设其目的是检索、格式化和输出 Blog post 仅 - 应该已经处理了数据库连接。
一个实际的例子可能是这样的:
/**
* 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
}
现具体执行action/role:
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);
}
}
NOTE 这个我没测试过,不过现在的原则是Blog class 只包含Blog 特有的逻辑post.任何格式化函数、安全函数等都可以在 Base class 中或在另一个辅助 class 中,Base class 使用类似于 DB,例如格式化程序 class.
你应该可以做到这一点:
<?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
这只是一个粗略的例子,但展示了您如何可以构建一组classes来实现class具有单一角色和具有单一目的的方法(例如 "get a blog post by its ID")。
这样,所有扩展 Base 的东西都将自动访问数据库(通过继承)。如果您想尝试从实现中删除 SQL,您可以向您的数据库 class 添加一些方法来提供基本的 ORM。
为通用值制作一个class
另一个 short/quick 选项是创建一个 class 可以为您包含的文件中的所有函数提供任何共同的东西,在本例中是数据库处理程序和博客post 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;
}
}
现在您只需在开始调用您的函数之前实例化此 class,并设置博客 post 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;
}
}
您唯一的责任是在每个页面上设置博客 post 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