PHP类_exists始终返回true
我有一个PHP类,在包含文件之前需要一些预定义的全局变量: 文件:includes/Product.inc.phpPHP类_exists始终返回true,php,Php,我有一个PHP类,在包含文件之前需要一些预定义的全局变量: 文件:includes/Product.inc.php if (class_exists('Product')) { return; } // This class requires some predefined globals if ( !isset($gLogger) || !isset($db) || !isset($glob) ) { return; } class Product { ... }
if (class_exists('Product')) {
return;
}
// This class requires some predefined globals
if ( !isset($gLogger) || !isset($db) || !isset($glob) ) {
return;
}
class Product
{
...
}
上述内容包含在需要使用产品的其他PHP文件中,这些文件使用require\u once。然而,任何想要使用该产品的人都必须确保这些globals是可用的,至少这是一个想法
我最近调试了Product类中的函数中的一个问题,该问题是由于$gLogger为null引起的。需要上面Product.inc.php的代码没有费心创建$gLogger。所以问题是,如果$gLogger为null,这个类是如何包含的
我试图调试代码(NetBeans中的xdebug),在Product.inc.php的开始处放置一个断点来查找,每次遇到if(class_exists('Product'))子句时,它都会简单地介入并返回,因此永远不会进入全局检查。那么它第一次是如何收录的呢
这是在MAMP(Apache/MySQL)下运行的PHP5.1+。我没有定义任何自动加载器
谢谢各位提供的信息丰富的答案。我相信当你
包含一个文件PHP从第一行开始逐行执行它,所以
如果全局文件不存在,它将不允许我包含该文件
定义我将把检查移到构造函数中。基于
原始问题,我接受@deceze的回答
文件在执行之前会被解析。类通过解析“加载”,但函数在解析后执行。通过将函数调用与类放在同一个文件中,类总是在函数执行之前被解析和“加载”,因此它总是true
如果您总是使用
require\u一次包含文件(这很好),那么该检查就没有意义了。类定义不应该有条件地依赖于某些全局变量。重新考虑一下你在这里做什么。文件在执行之前会被解析。类通过解析“加载”,但函数在解析后执行。通过将函数调用与类放在同一个文件中,类总是在函数执行之前被解析和“加载”,因此它总是true
如果您总是使用require\u一次包含文件(这很好),那么该检查就没有意义了。类定义不应该有条件地依赖于某些全局变量。重新思考你在这里做什么。我看到了一个主要问题:
// This class requires some predefined globals
这可能会让您感到惊讶,但我认为您实际上想要做的是,如果是这样,那么在定义类时,您不会检查它,而是在实例化它时检查它
当一个类被实例化时,它的构造函数被调用。对我来说,这似乎是检查这一点的最佳场所:
class Product
{
public function __construct() {
// This class requires some predefined globals
$this->needGlobal('gLogger', 'db', 'glob');
}
private function needGlobal() {
foreach (func_get_args() as $global) {
if (!isset($GLOBALS[$global])) {
throw new RuntimeException(sprintf('Global %s needed but not set.', $global));
}
}
}
...
}
实例化产品时,它会自动检查是否满足先决条件:
$blueShoes = new Product();
如果不满足先决条件,这将不起作用,但如果满足先决条件,它将起作用
但这只是部分解决了你的问题。代码的真正问题是产品
需要全局变量才能工作
取而代之的是,让产品只带上需要使用的东西:
class Product
{
private $gLogger;
private $db;
private $glob;
public function __construct(LoggerInterface $gLogger, DbInterface $db, GlobInterface $glob) {
$this->gLogger = $gLogger;
$this->db = $db;
$this->glob = $glob;
}
...
}
用法:
$redShoes = new Product($gLogger, $db, $glob);
这样,您就不必再关心产品中的任何全局内容了
您评论说您希望逐步改进代码。你可以这样做,这里是如何做到的。如前所述,上面的第二个变体是可行的,但目前遗留代码与之不兼容。在任何情况下,如果产品
类是新代码,则应该使用依赖项注入来编写它。这对于将旧代码与新代码分开很重要。你不想让遗留的东西被你的新代码吞没。这将使新代码成为遗留代码,因此您将无法逐步改进。您只需添加新的遗留代码
因此,以依赖项注入的类定义为例。为满足您的遗留需求,编写第二个类来屏蔽:
class ProductLegacy extends Product
{
public function __construct() {
// This class requires some predefined globals
list($gLogger, $db, $glob) = $this->needGlobal('gLogger', 'db', 'glob');
parent::__construct($gLogger, $db, $glob);
}
private function needGlobal() {
$variables = array();
foreach (func_get_args() as $global) {
if (!isset($GLOBALS[$global])) {
throw new RuntimeException(sprintf('Global %s needed but not set.', $global));
}
$variables[] = $GLOBALS[$global];
}
return $variables;
}
}
正如你所看到的,这个小存根将全球的做事方式与新的方式结合在一起。您可以在新代码中使用Product
类,如果需要与旧代码交互,可以使用ProductLegacy
类,该类与全局变量一起用于类实例化
您还可以创建一个帮助器函数来执行此操作,以便可以将其用于不同的类。这取决于你的需要。只要找到一个边界,在那里你可以在旧代码和新代码之间划清界限。我在这里看到一个主要问题:
// This class requires some predefined globals
这可能会让您感到惊讶,但我认为您实际上想要做的是,如果是这样,那么在定义类时,您不会检查它,而是在实例化它时检查它
当一个类被实例化时,它的构造函数被调用。对我来说,这似乎是检查这一点的最佳场所:
class Product
{
public function __construct() {
// This class requires some predefined globals
$this->needGlobal('gLogger', 'db', 'glob');
}
private function needGlobal() {
foreach (func_get_args() as $global) {
if (!isset($GLOBALS[$global])) {
throw new RuntimeException(sprintf('Global %s needed but not set.', $global));
}
}
}
...
}
实例化产品时,它会自动检查是否满足先决条件:
$blueShoes = new Product();
如果不满足先决条件,这将不起作用,但如果满足先决条件,它将起作用
但这只是部分解决了你的问题。代码的真正问题是产品
需要全局变量才能工作
取而代之的是,让产品只带上需要使用的东西:
class Product
{
private $gLogger;
private $db;
private $glob;
public function __construct(LoggerInterface $gLogger, DbInterface $db, GlobInterface $glob) {
$this->gLogger = $gLogger;
$this->db = $db;
$this->glob = $glob;
}
...
}
用法:
$redShoes = new Product($gLogger, $db, $glob);
这样,您就不必再关心产品中的任何全局内容了
您评论说您希望逐步改进代码。你可以这样做,这里是如何做到的。如前所述,上面的第二个变体是可行的,但目前遗留代码与之不兼容。在任何情况下,如果产品
类是新代码,则应该使用依赖项注入来编写它。这对于将旧代码与新代码分开很重要。你不想让遗留的东西被你的新代码吞没。这将使新代码成为遗留代码,因此您将无法升级