没有不值得去解决的问题,也没有不值得去学习的技术!

Fixing the “Datasource Configuration Tmp Was Not Found” Error in PHP 7.0.22 and CakePHP 2.10.3

Dynamically create a DataSource object at runtime with a given name and settings, eliminating the need to modify the core library

1. Under PHP 7.0.22 and CakePHP 2.10.3, the following error occurs: Error: The datasource configuration tmp was not found in database.php., as shown in Figure 1

Under PHP 7.0.22 and CakePHP 2.10.3, the following error occurs: Error: The datasource configuration tmp was not found in database.php.
Figure 1

2. Print $this->useDbConfig, and its value is tmp, as shown in Figure 2

Print $this->useDbConfig, and its value is tmp
Figure 2

3. Confirm that $tmp does not exist in database.php, as shown in Figure 3

Confirm that $tmp does not exist in database.php
Figure 3

4. However, it was previously confirmed to work fine in CakePHP 2.6.4, so I decided to compare the differences and found the cause (Note: the core library was modified, but after the upgrade, the core library’s _init() was reverted), as shown in Figure 4

However, it was previously confirmed to work fine in CakePHP 2.6.4, so I decided to compare the differences and found the cause (Note: the core library was modified, but after the upgrade, the core library's _init() was reverted)
Figure 4

\lib\Cake\Model\ConnectionManager.php

PHP
<?php
    protected static function _init() {
//      @include_once APP . 'Config' . DS . 'database.php';
//      if (class_exists('DATABASE_CONFIG')) {
//          self::$config = new DATABASE_CONFIG();
//      }
        self::$_init = true;
    }
?>

Note: The core library was modified, but after the upgrade, the core library’s _init() was reverted

PHP
<?php
    protected static function _init() {
        include_once CONFIG . 'database.php';
        if (class_exists('DATABASE_CONFIG')) {
            static::$config = new DATABASE_CONFIG();
        }
        static::$_init = true;
    }
 
?>

5. Print static::$config in the getDataSource method of \lib\Cake\Model\ConnectionManager.php, as shown in Figure 5

Print static::$config in the getDataSource method of \lib\Cake\Model\ConnectionManager.php
Figure 5

6. The print result confirms that $tmp does not exist, as shown in Figure 6

The print result confirms that $tmp does not exist
Figure 6

7. The current requirement should be to ensure that $tmp exists in static::$config without modifying the core library. Review the previous implementation, as shown in Figure 7

The current requirement should be to ensure that $tmp exists in static::$config without modifying the core library. Review the previous implementation
Figure 7

\app\Model\AppModel.php

PHP
<?php
 
    public function getDbConfig($tenantid){
         if(!$conn = Cache::read($tenantid,'_cake_config_')){
            $tenant = new Tenant();
            $conn = $tenant->getTenantEnv($tenantid);
            $this->log($conn);
            Cache::write($tenantid,$conn,'_cake_config_');
        }
        return $conn;
    }
 
    public function setDataSource($dataSource = null) {
        if($_GET['tenantid']){
            $tenantid = [addslashes($_GET['tenantid'])];
        }else{
            preg_match('@^(?:http://)?([^/]+)@i', FULL_BASE_URL, $matches);
            $tenantid = explode('.', $matches[1]);
        }
        $conn = $this->getDbConfig($tenantid[0]);
        if(!$conn) die('系统错误506,无法链接数据库');
 
        $this->useDbConfig = $dataSource = 'tmp';
        $oldConfig = $this->useDbConfig;
 
        if ($dataSource) {
            $this->useDbConfig = $dataSource;
        }
 
        $config = new stdClass();
        $config->tmp = array(
           'datasource' => 'Database/Mysql',
           'persistent' => false,
           'host' =>$conn['db_info']['host'],
           'login' => $conn['db_info']['login'],
           'password' => $conn['db_info']['password'],
           'database' => $conn['db_info']['database'],
           'port' => 3306,
           'prefix' => 'operation_',
           'encoding' => 'utf8'
       );
//      if(!$conn){
//       pr($config->tmp);
//         $config->tmp = array(
//             'datasource' => 'Database/Mysql',
//             'persistent' => false,
//             'host' => '127.0.0.1',
//             'login' =>'root',
//             'password' =>'root',
//             'database' =>"jifen",
//             'port' => 3306,
//             'prefix' => 'operation_',
//             'encoding' => 'utf8'
//         );
//      }
//        $this->log($config->tmp);
        ConnectionManager::$config = $config;
        $db = ConnectionManager::getDataSource($this->useDbConfig);
        if (!empty($oldConfig) && isset($db->config['prefix'])) {
            $oldDb = ConnectionManager::getDataSource($oldConfig);
 
            if (!isset($this->tablePrefix) || (!isset($oldDb->config['prefix']) || $this->tablePrefix === $oldDb->config['prefix'])) {
                $this->tablePrefix = $db->config['prefix'];
            }
        } elseif (isset($db->config['prefix'])) {
            $this->tablePrefix = $db->config['prefix'];
        }
        $schema = $db->getSchemaName();
        $defaultProperties = get_class_vars(get_class($this));
        if (isset($defaultProperties['schemaName'])) {
            $schema = $defaultProperties['schemaName'];
        }
        $this->schemaName = $schema;
     //pr($config->tmp);  
    }
?>

8. Dynamically create a DataSource object at runtime with a given name and settings, eliminating the need to modify the core library, as shown in Figure 8

Dynamically create a DataSource object at runtime with a given name and settings, eliminating the need to modify the core library
Figure 8

\app\Model\AppModel.php

PHP
<?php
    public function getDbConfig($tenantid){
         if(!$conn = Cache::read($tenantid,'_cake_config_')){
            $tenant = new Tenant();
            $conn = $tenant->getTenantEnv($tenantid);
            $this->log($conn);
            Cache::write($tenantid,$conn,'_cake_config_');
        }
        return $conn;
    }
 
    public function setDataSource($dataSource = null) {
        if($_GET['tenantid']){
            $tenantid = [addslashes($_GET['tenantid'])];
        }else{
            preg_match('@^(?:http://)?([^/]+)@i', FULL_BASE_URL, $matches);
            $tenantid = explode('.', $matches[1]);
        }
        $conn = $this->getDbConfig($tenantid[0]);
        if(!$conn) die('系统错误506,无法链接数据库');
 
        $dataSource = 'tmp';
 
        $config = array(
            'datasource' => 'Database/Mysql',
            'persistent' => false,
            'host' =>$conn['db_info']['host'],
            'login' => $conn['db_info']['login'],
            'password' => $conn['db_info']['password'],
            'database' => $conn['db_info']['database'],
            'port' => 3306,
            'prefix' => 'operation_',
            'encoding' => 'utf8'
        );
        ConnectionManager::create($dataSource, $config);
 
        $this->useDbConfig = $dataSource;
        $oldConfig = $this->useDbConfig;
 
        if ($dataSource) {
            $this->useDbConfig = $dataSource;
        }
 
        $db = ConnectionManager::getDataSource($this->useDbConfig);
        if (!empty($oldConfig) && isset($db->config['prefix'])) {
            $oldDb = ConnectionManager::getDataSource($oldConfig);
 
            if (!isset($this->tablePrefix) || (!isset($oldDb->config['prefix']) || $this->tablePrefix === $oldDb->config['prefix'])) {
                $this->tablePrefix = $db->config['prefix'];
            }
        } elseif (isset($db->config['prefix'])) {
            $this->tablePrefix = $db->config['prefix'];
        }
        $schema = $db->getSchemaName();
        $defaultProperties = get_class_vars(get_class($this));
        if (isset($defaultProperties['schemaName'])) {
            $schema = $defaultProperties['schemaName'];
        }
        $this->schemaName = $schema;
    }
?>

9. Print static::$config in the getDataSource method of \lib\Cake\Model\ConnectionManager.php, and $tmp now exists, as shown in Figure 9

Print static::$config in the getDataSource method of \lib\Cake\Model\ConnectionManager.php, and $tmp now exists
Figure 9

10. Run it again, and it works normally, as shown in Figure 10

Run it again, and it works normally
Figure 10

11. Summary: The reason why setDataSource() needs to be overridden in \app\Model\AppModel.php is that the database configuration is dynamically fetched from an API rather than originating from \app\Config\database.php. Using ConnectionManager::create, a datasource object can be dynamically created.

关于作者 · 持续学习与技术实践

我是一名拥有 15 年以上开发经验的技术从业者,自 2013 年起持续运营个人技术博客,记录工作与个人项目中遇到的真实问题,以及解决问题过程中的探索、实践和思考。

除了技术写作,我也在持续开发和维护自己的独立项目,探索新技术和新工具在实际场景中的应用。我始终相信,没有不值得去解决的问题,也没有不值得去学习的技术。

如果这篇文章对你有所帮助,欢迎浏览博客中的其他文章,也欢迎交流技术经验与想法。

关于我  ·  GitHub  ·  邮件联系