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

在 PHP 7.0.22、CakePHP 2.10.3 下,报错:Error: The datasource configuration tmp was not found in database.php.的解决(动态设置数据源对象)

在运行时以给定的名称和设置动态创建一个DataSource对象,便无需修改核心库
1、在 PHP 7.0.22、CakePHP 2.10.3 下,报错:Error: The datasource configuration tmp was not found in database.php.,如图1
在 PHP 7.0.22、CakePHP 2.10.3 下,报错:Error: The datasource configuration tmp was not found in database.php.
图1
2、打印 $this->useDbConfig,其值为 tmp,如图2
打印 $this->useDbConfig,其值为 tmp
图2
3、确定在 database.php 中不存在 $tmp,如图3
确定在 database.php 中不存在 $tmp
图3
4、但是之前在 CakePHP 2.6.4 确定是正常的,决定对比一下区别,发现区别所在(注:修改了核心库,升级之后,核心库的 _init() 被还原),如图4
但是之前在 CakePHP 2.6.4 确定是正常的,决定对比一下区别,发现区别所在(注:修改了核心库,升级之后,核心库的 _init() 被还原)
图4
\lib\Cake\Model\ConnectionManager.php
<pre class="wp-block-syntaxhighlighter-code">

<?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;
	}
?>

</pre>
注:修改了核心库,升级之后,核心库的 _init() 被还原
<pre class="wp-block-syntaxhighlighter-code">

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

?>

</pre>
5、在 \lib\Cake\Model\ConnectionManager.php的getDataSource方法打印static::$config,如图5
在 \lib\Cake\Model\ConnectionManager.php的getDataSource方法打印static::$config
图5
6、打印结果,确定不存在 $tmp,如图6
打印结果,确定不存在 $tmp
图6
7、现在的需求应该为,在不修改核心库的前提下,确保 static::$config 中存在 $tmp,查看之前的实现,如图7
现在的需求应该为,在不修改核心库的前提下,确保 static::$config 中存在 $tmp,查看之前的实现
图7
\app\Model\AppModel.php
<pre class="wp-block-syntaxhighlighter-code">

<?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);  
	}
?>

</pre>
8、在运行时以给定的名称和设置动态创建一个DataSource对象,便无需修改核心库,如图8
在运行时以给定的名称和设置动态创建一个DataSource对象,便无需修改核心库
图8
\app\Model\AppModel.php
<pre class="wp-block-syntaxhighlighter-code">

<?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;
	}
?>

</pre>
9、在 \lib\Cake\Model\ConnectionManager.php的getDataSource方法打印static::$config,$tmp已经存在,如图9
在 \lib\Cake\Model\ConnectionManager.php的getDataSource方法打印static::$config,$tmp已经存在
图9
10、再次运行,正常,如图10
再次运行,正常
图10
11、总结:为何需要在 \app\Model\AppModel.php 覆盖 setDataSource(),原因在于数据库是动态从接口获取的,并非源于 \app\Config\database.php,通过 ConnectionManager::create,便可以实现动态创建一个数据源对象。

需要长期技术维护或远程问题排查?

我是拥有 15+ 年经验的 PHP / Go 后端工程师,长期关注已有系统维护、Bug 修复、性能优化、服务器排查、WordPress 网站维护和小功能迭代。

如果你的项目遇到以下情况,可以先从一次小问题排查开始合作:

  • ✅ PHP / Laravel / Yii2 老项目无人维护
  • ✅ Go / Gin 后端接口需要排查或优化
  • ✅ WordPress 网站访问慢、报错或插件冲突
  • ✅ Nginx / MySQL / Redis / Linux 服务器异常
  • ✅ CDN / Cloudflare / DNS / HTTPS 配置问题
  • ✅ 需要长期远程技术支持或兼职维护

更多介绍请查看:关于我 & 合作

微信:13980074657
邮箱:shuijingwanwq@gmail.com
Telegram:@shuijingwan
GitHub:https://github.com/shuijingwan

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理