无法执行Model_Hotel::__construct()"使用mysql_fetch_object,属性保


"Could not execute Model_Hotel::__construct()" with mysql_fetch_object, properties remain uninitialized

我在Mac OS X Lion上运行PHP 5.3.6和MAMP 2.0.1,我使用Kohana 3.1.3.1。

我尝试用自己的方法覆盖标准Kohana ORM模块的__set()和__get()方法。我的一些模型扩展了我的类,它扩展了ORM类。

<?php
class ORM_Localization extends ORM
{
    protected static $_language_cache = array();
    protected $_languages = array();
    public function __construct($id = NULL)
    {
        parent::__construct($id);
        // Load languages which are accepted by the client
        $this->_languages = array_keys(Application::languages());
    }
    public function __get($name)
    {
        /* Debugging #1 */ echo 'GET'.$name;
        /* Debugging #2 */ // exit;
        return $this->localization($name);
    }
    public function __set($name, $value)
    {
        /* Debugging #3 */ var_dump($this->_table_columns);
        /* Debugging #4 */ echo 'SET::'.$name;
        /* Debugging #5 */ // exit;
        $this->localization($name, $value);
    }
    public function localization($name, $value = NULL)
    {
        if (!array_key_exists($name, $this->_table_columns)) {
            // Load avaiable languages from database if not already done
            if (empty(ORM_Localization::$_language_cache)) {
                ORM_Localization::$_language_cache = ORM::factory('languages')
                    ->find_all()
                    ->as_array('id', 'identifier');
            }
            $languages = array();
            // Find the IDs of the languages from the database which match the given language
            foreach ($this->languages as $language) {
                $parts = explode('-', $language);
                do {
                    $identifier = implode('-', $parts);
                    if ($id = array_search($identifier, ORM_Localization::$_language_cache)) {
                        $languages[] = $id;
                    }
                    array_pop($parts);
                } while($parts);
            }
            // Find localization
            foreach ($languages as $language) {
                $localization = $this->localizations->where('language_id', '=', $language)->find();
                try {
                    if ($value === NULL) {
                        return $localization->$name;
                    } else {
                        $localization->$name = $value;
                    }
                } catch (Kohana_Exception $exception) {}
            }
        }
        if ($value === NULL) {
            return parent::__get($name);
        } else {
            parent::__set($name, $value);
        }
    }
}

但是在PHP 5.3.6中,我得到以下错误消息:

异常[0]:Could not execute Model_Hotel::__construct()

Model_Hotel继承了这个类,但没有自己的构造。这是Model_Hotel的代码,一个标准的Kohana ORM模型:http://pastie.org/private/vuyig90phwqr9f34crwg

使用PHP 5.2.17,我得到了另一个:

ErrorException [Warning]: array_key_exists() [function。:第二个参数应该是一个数组或一个对象

在Kohana中,扩展类的模型由mysql_fetch_object()调用,在form模块代码的某个深处。

然而,如果我在__set()中回显被调用的属性并退出(#4和#5),它输出"SET::id"并且不显示错误消息。

如果我var_dump() $this->_table_columns(或该类的任何其他属性,#3),我得到"NULL",这是该属性在初始化之前的值。如果我对$this->_languages重复相同的操作,我将得到一个空数组,其中应该填充几种语言。就好像这个类从来没有用__construct初始化过。这解释了我在PHP 5.2.17中得到的错误,因为$ This ->_table_columns是NULL而不是数组。

我可以取消注释我的__construct,但我仍然得到相同的错误,错误必须在我的localization()方法中。

我已经找了好几天了,完全不知道哪里出了问题。

ORM::__set()期望从mysql_fetch_object()调用加载强制转换数据。您必须允许它为这个原因设置强制转换值:

public function __set($column, $value)
{
    if ( ! isset($this->_object_name))
    {
        // Object not yet constructed, so we're loading data from a database call cast
        $this->_cast_data[$column] = $value;
    }
    else
    {
        // Set the model's column to given value
        $this->localization($column, $value);
    }
}

虽然我也不喜欢这个解决方案,但你应该重写ORM::set()

__construct()在对象初始化后自动调用。

$class = new Object;

我有更多关于这个问题的信息,因为我遇到了同样的问题。

1)我发现了一个PHP bug报告,在mysqli和mysql_fetch_object中__set()在__construct()之前被调用。该报告是从2009年开始的,据说是固定的,但在5.3版本中,我正在运行它似乎仍然发生。网址:https://bugs.php.net/bug.php?id=48487

2)如果,在执行你的__set()方法的过程中,你抛出一个异常或一些其他错误发生,你会得到这个模糊的"不能调用X::__construct()"的消息,而不是真正的异常/错误消息。

我可以这样解决我的问题:

public function __set($key, $val){
    if($constructor_has_not_run){
        $this->__construct();
    }
    //do stuff
}

在我的例子中,我有点幸运,因为我可以让构造函数运行两次而没有任何问题,但您可能不会遇到同样的情况。