【问题标题】:PHP: "Call to undefined method" ... but method is defined?PHP:“调用未定义的方法”......但方法已定义?
【发布时间】:2012-07-27 19:24:54
【问题描述】:

我有一个类 Database.php,它是一个抽象的 Singleton 类:

<?php

abstract class Database
{
    protected static $_instance;
    ...

    public static function instance()
    {
        $class = get_called_class();

        if (!self::$_instance) {
            self::$_instance = new $class();
        }

        return self::$_instance;
    }
}

?>

其他数据库扩展了这个类并实现了抽象函数,这样我就可以改变我使用的数据库,同时确保我的应用程序仍然可以工作,只要它使用这个抽象层中的函数(我省略了函数定义从上面的代码)。

我在 PDO.php 中有一个 PDO 类。它扩展了数据库,还包括与上述相同的instance() 函数,但它没有自己的$_instance 变量。

class PDO extends Database
{
    //...

    public static function instance()
    {
        $class = get_called_class();

        if (!self::$_instance) {
                self::$_instance = new $class();
        }

        return self::$_instance;
    }
}

最初我认为我不必在 PDO 类中包含 instance() 函数,因为它继承自 Database。但我包含它是因为我收到了这个错误:

Fatal error: Call to undefined method PDO::instance()

问题是我仍然收到此错误,即使使用上面的代码也是如此。我不知道这是否相关,但我遇到了另一个奇怪的错误。我要使用的数据库类型(在本例中为 PDO)存储在可通过App::setting('DB') 访问的变量中。我的 MVC 框架中的 Model 基类会加载相应的数据库类并将其存储在 $this-&gt;_db 中。这是模型的代码:

<?php

require_once 'Databases/Database.php';

class Model
{
    protected $_db;

    public function __construct()
    {
        $database = App::setting('DB'); // 'PDO' 
        require_once 'Databases/' . $database . '.php';
        $this->_db = $database::instance(); // this is what triggers the error
    }
}

?>

现在这段代码给了我一个错误,我无法重新声明 PDO 类。我在网上搜索,发现问题通常与使用include()而不是require_once()有关。我到处检查,我的 Autoloader 没有看到任何 include()。即使在上面的模型中使用require_once(),它仍然给我错误(这是我唯一需要 PDO 类的地方......)。

为了解决这个错误,我使用了一种创可贴解决方案,我将模型构造函数中的 require 替换为:

if (!class_exists($database)) {
    require_once 'Databases/' . $database . '.php';
}

谁能解释这里发生了什么?

【问题讨论】:

  • 顺便说一下Logan,以防万一您不知道,您也可以扩展内置类。看到class Database extends PDO 并不少见。尽管您应该远离单例并尽可能使用依赖注入。
  • 我不想扩展 PDO。我使用的是单例,因为我只需要一个用于任何模型的数据库对象,这样如果我要加载两个模型,它们就不会连接到数据库两次。有没有更好的方法来实现这一目标?我不知道依赖注入是如何工作的,但我不是专家。很多 PHP 框架都使用单例,所以我就使用了。

标签: php inheritance static abstract-class


【解决方案1】:

类名PDO 是一个标准的PHP 类,因此使用包含PDO 类的解决方案实际上永远不会包含您的类。

【讨论】:

  • 这会产生不同的错误。 Cannot redeclare class PDO - 完全有可能拥有早于 PDO 的 PHP 版本,或者特别是在编译时禁用 PDO。
  • @Leigh 仅限,当包含文件时,但我在任何地方都看不到匹配的包含。
  • @KingCrunch:有道理,没有包含重新定义的类,没有得到欺骗类错误,然后从基类调用一个也没有定义的方法。
【解决方案2】:

我建议使用自动加载。它会让你的生活更简单,你可以摆脱所有那些require 电话。

PHP manual 中有一个实现,我发现它非常有用。它可能看起来像这样:

function my_autoloader($class) {
    include 'classes/' . $class . '.class.php';
}

spl_autoload_register('my_autoloader');

// Or, using an anonymous function as of PHP 5.3.0
spl_autoload_register(function ($class) {
    include 'classes/' . $class . '.class.php';
});

如果您使用的是 PHP 5.3,还可以考虑使用命名空间。拥有一个好的结构和一个简单的默认实现的自动加载器实际上更容易:

spl_autoload_extensions(".php"); // comma-separated list
spl_autoload_register();

但是,我记得那里有一个错误。通常我有一个“引导”文件,带有这个自动加载器:

spl_autoload_register(function($class) {
    if(file_exists('./lib/' . str_replace('\\', '/', $class) . '.php')) {
        require_once './lib/' . str_replace('\\', '/', $class) . '.php';
    }
});

使用此机制,您将拥有一个模仿命名空间结构的文件夹结构。使用这种方法,您拥有一个良好的结构,并且不需要关心包含您的库和其他类。

【讨论】:

    猜你喜欢
    • 2012-09-17
    • 2018-04-11
    • 2020-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多