针对MySQL的PDO链接，如果需要采用其他数据库，可重载此函数
@link https://www.php.net/manual/en/book.pdo.php
@param array $dbCfg 数据库配置
@return PDO
353497
protected function createPDOBy($dbCfg) {
        // 默认mysql
        $type = !empty($dbCfg['type']) ? strtolower($dbCfg['type']) : 'mysql';
        $dsn = sprintf('mysql:dbname=%s;host=%s;port=%d',
            $dbCfg['name'], 
            isset($dbCfg['host']) ? $dbCfg['host'] : 'localhost', 
            isset($dbCfg['port']) ? $dbCfg['port'] : 3306
        );
        
        if ($type == 'sqlserver' || $type == 'sqlsrv') {  // 支持sql server
            $dsn = sprintf('sqlsrv:Server=%s,%d;Database=%s',
                isset($dbCfg['host']) ? $dbCfg['host'] : 'localhost', 
                isset($dbCfg['port']) ? $dbCfg['port'] : 1433, 
                $dbCfg['name']
            );
        } else if ($type == 'pgsql') {  // 支持postgreSQL
            $dsn = sprintf('pgsql:dbname=%s;host=%s;port=%d',
                $dbCfg['name'],
                isset($dbCfg['host']) ? $dbCfg['host'] : 'localhost',
                isset($dbCfg['port']) ? $dbCfg['port'] : 3306
            );
        }

        // 创建PDO连接
        $pdo = new PDO($dsn, $dbCfg['user'], $dbCfg['password']);

        // 设置编码
        $charset = isset($dbCfg['charset']) ? $dbCfg['charset'] : 'UTF8';
        $pdo->exec("SET NAMES '{$charset}'");

        return $pdo;
    }
96145
public function create_database($dbhost, $dbuser, $dbpass, $dbname, array $dboptions=null) {
        global $CFG;

        $this->dbhost = $dbhost;
        $this->dbuser = $dbuser;
        $this->dbpass = $dbpass;
        $this->dbname = $dbname;
        $filepath = $this->get_dbfilepath();
        $dirpath = dirname($filepath);
        @mkdir($dirpath, $CFG->directorypermissions, true);
        return touch($filepath);
    }
855259
public function getResult(array $config = array(), $sql = '', $limit = 0, $offset = 0, $single = false, $key = null, $responseType = null)
    {
        $data = array(
            'error' => true,
            'message' => 'Authentication failed nor No config available ',
        );

        $this->rawConfig = $config;
        
        if ($this->authentication->getSecurity()->isValid($key) && !empty($config)) {
            try {
                $di = new DBInstance($config);
                if (count($this->connectionStrings)) {
                    $di->preventNoLocalDriver();
                    foreach ($this->connectionStrings as $driverName => $connString) {
                        $di->registerConnectionString($connString, $driverName);
                    }
                }
                if (!$di->hasError()) {
                    if ($single) {
                        $di->setSingleQuery($sql);
                    } else {
                        $di->setQuery($sql);
                    }
                    $di->getQuery()->forceLimit(intval($limit ? $limit : 500));
                    $di->getQuery()->forceOffset(intval($offset));
                    $di->run();

                    if (!$di->hasError()) {
                        $data['response'] = $di->getResult(DBInstance::FETCH_ARRAY);
                    }
                }
            } catch (\Exception $e) {
                $di->getQuery()->getError()->setMessage($e->getMessage());
            }

            $data['error'] = $di->hasError();
            $data['message'] = (string) $di->getErrorMessage();
        }

        if (null !== $this->logDir) {
            $logPath = rtrim($this->logDir,'/\\') . '/log-' . microtime(true) . '.json';
            if (!defined('JSON_PRETTY_PRINT')) {
                define('JSON_PRETTY_PRINT', 1);
            }
            file_put_contents($logPath, json_encode($data, JSON_PRETTY_PRINT));
        }

        if ($responseType == 'json') {
            return array('json' => json_encode($data));
        }

        return $data;
    }
42498
public function connection($config = null, $forceConnect = false)
    {

        if ($this->connection && !$forceConnect) {
            return $this->connection;
        }

        !isset($config) || $this->config($config);

        $ref = new \ReflectionClass($this->getHandler());

        try {
            /** @var \PDO $connection */
            $this->connection = $ref->newInstanceArgs([
                $this->getConnectionString(),
                $this->getUser(),
                $this->getPassword(),
                $this->getHandlerOptions()
            ]);
        } catch (\PDOException $e) {
            throw new DatabaseException($e->getMessage());
        }

        return $this->connection;
    }
97917
public static function getFromPDO(array $p_pdo_description)
    {
        $me = self::getNew();
        if (is_array($p_pdo_description)) {
            if (array_key_exists('name', $p_pdo_description)) {
                $me->setFldName($p_pdo_description['name']);
            }
            if (array_key_exists('len', $p_pdo_description)) {
                $me->setFldLength($p_pdo_description['len']);
            }
            if (array_key_exists('precision', $p_pdo_description)) {
                $me->setFldComplement($p_pdo_description['precision']);
            }
            $me->setFldType(FFCST::TYPE_STRING);
            if (array_key_exists('native_type', $p_pdo_description)) {
                switch (strtoupper($p_pdo_description['native_type'])) {
                    case 'LONGLONG':
                        $me->setFldType(FFCST::TYPE_BIGINT);
                        break;
                    case 'TINY':
                        $me->setFldType(FFCST::TYPE_INTEGER);
                        break;
                    case 'BLOB':
                        $me->setFldType(FFCST::TYPE_BLOB);
                        break;
                    case 'TIMESTAMP':
                        $me->setFldType(FFCST::TYPE_DATETIME);
                        break;
                }
            }
            if (array_key_exists('flags', $p_pdo_description)) {
                if (is_array($p_pdo_description['flags'])) {
                    foreach ($p_pdo_description['flags'] as $idx => $flag) {
                        if ($flag == 'primary_key') {
                            $me->setFldPrimary(true);
                        }
                        if ($flag == 'not_null') {
                            $me->setFldRequired(true);
                        }
                    }
                }
            }
        }
        return $me;
    }
10296
public function setDbConn($dbconfig = [])
    {
        if (empty($dbconfig)) {
            $this->dbconfig = config('database');
            //$this->dbconfig = Config::get('database');
        } else {
            $this->dbconfig = $dbconfig;
        }
        return $this;
    }
675940
public static function getInstance(DBFactoryConfig $config)
    {

        if (self::$instances[$config->getHash()] === null) {

            self::$instances[$config->getHash()] = new DB(
                $config->getHost(),
                $config->getDatabase(),
                $config->getUsername(),
                $config->getPassword(),
                $config->getPort()
            );
        }
        return self::$instances[$config->getHash()];
    }
668947
public static function getDatabaseConfig($dbConfig = null)
    {
        $config = Config::getInstance();

        if (is_null($dbConfig)) {
            $dbConfig = $config->database;
        }

        /**
         * Triggered before a database connection is established.
         *
         * This event can be used to change the settings used to establish a connection.
         *
         * @param array *$dbInfos Reference to an array containing database connection info,
         *                        including:
         *
         *                        - **host**: The host name or IP address to the MySQL database.
         *                        - **username**: The username to use when connecting to the
         *                                        database.
         *                        - **password**: The password to use when connecting to the
         *                                       database.
         *                        - **dbname**: The name of the Piwik MySQL database.
         *                        - **port**: The MySQL database port to use.
         *                        - **adapter**: either `'PDO\MYSQL'` or `'MYSQLI'`
         *                        - **type**: The MySQL engine to use, for instance 'InnoDB'
         */
        Piwik::postEvent('Db.getDatabaseConfig', array(&$dbConfig));

        $dbConfig['profiler'] = @$config->Debug['enable_sql_profiler'];

        return $dbConfig;
    }
1172987
public function init($config)
    {
        $this->pdo = null;

        $dns = '';
        if (is_array($config)) {
            $config += array('user' => null, 'password' => null);
            $dns = "{$config['type']}:host={$config['host']};port={$config['port']};dbname={$config['name']}";
        } else if (is_string($config)) {
            $dns = $config;
        } else if ($config instanceof PDO) {
            $this->pdo = $config;
            return $this;
        }

        try {

            // Use pipe for merging fields - sqlite compatibility
            $options = array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET sql_mode='PIPES_AS_CONCAT'");
            $this->pdo = new PDO($dns, $config['user'], $config['password'], $options);
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        } catch (Exception $ex) {
            $this->pdo = null;
            throw new RuntimeException('Cannot connect to database: ' . $ex->getMessage());
        }

        return $this;
    }
476232
private function _hasMany(array $config): ResultSet
    {
        //配置信息
        $tableClass = $config['table'];
        $primaryKey = $config['primaryKey'];
        $foreignKey = $config['foreignKey'];

        //取主键的值
        $primaryValue = $this->$primaryKey;

        /**
         * 子对象表
         * @var $tableInstance Table
         */
        $tableInstance = $tableClass::instance();

        //在子对象表中查询关联记录
        return ResultSet::instance($tableInstance->select('*', [$foreignKey => $primaryValue]))
            ->_setType('hasMany', $config);
    }
954907
public function pdoConnect($activeConfig)
    {
        $this->activeConfig = $activeConfig;
        $dsn = 'mysql:dbname=' . $activeConfig["database"] . ';host=' . $activeConfig["host"] . ';port=' . $activeConfig['port'] ?? 3306;
        $pdo = new \PDO($dsn, $activeConfig["user"], $activeConfig["password"], [
            \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES ' . $activeConfig['charset'] ?? 'utf8'
        ]);
        $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
        $pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
        $this->setPdoConnection($pdo);
        
        return $this;
    }
685198
public static function init(bbn\db $db, array $cfg = []): void
  {
    /** @var string $hash Unique hash for this DB connection (so we don't init twice a same connection) */
    $hash = $db->get_hash();
    if ( !\in_array($hash, self::$dbs, true) && $db->check() ){
      // Adding the connection to the list of connections
      self::$dbs[] = $hash;
      /** @var bbn\db db */
      self::$db = $db;
      $vars = get_class_vars(__CLASS__);
      foreach ( $cfg as $cf_name => $cf_value ){
        if ( array_key_exists($cf_name, $vars) ){
          self::$$cf_name = $cf_value;
        }
      }
      if ( !self::$admin_db ){
        self::$admin_db = self::$db->current;
      }
      self::$table = self::$admin_db.'.'.self::$prefix.'history';
      self::$table_uids = self::$admin_db.'.'.self::$prefix.'history_uids';
      self::$ok = true;
      self::$is_used = true;
      self::$links = self::$db->get_foreign_keys('bbn_uid', self::$prefix.'history_uids', self::$admin_db);
      self::$db->set_trigger('\\bbn\\appui\\history::trigger');
    }
  }
989039
public static function pdo(array $config = []): DB
    {
        [$hash, $config] = static::getKey($config, 'database');

        if (isset(static::$pool[$hash])) {
            return static::$pool[$hash];
        }

        $driver = $config['driver'] ?? '';

        if ($driver === 'develop' && !Debugger::$productionMode) {
            return static::$pool[$hash] = new Develop($config);
        }

        return static::$pool[$hash] = new DB($config);
    }
1150673
public function connect(array $config)
	{
		$dsn = $this->getDsn($config);
		$options = $this->getOptions($config);

		// We need to grab the PDO options that should be used while making the brand
		// new connection instance. The PDO options control various aspects of the
		// connection's behavior, and some might be specified by the developers.
		$this->pdo = $this->createConnection($dsn, $config, $options);

		$this->configureEncoding($config);

		// Next, we will check to see if a timezone has been specified in this config
		// and if it has we will issue a statement to modify the timezone with the
		// database. Setting this DB timezone is an optional configuration item.
		$this->configureTimezone($config);
		$this->setModes($config);

		return $this->pdo;
	}
1023039
public function add(array $cfg): ?string
  {
    if (
      $this->id_user &&
      (null !== $cfg['request']) &&
      $this->check()
    ){
      $t = new bbn\util\timer();
      $t->start();
      if ( is_string($cfg['request']) ){
        $params = self::sanitize_params($cfg['params'] ?? []);
        $request = trim($cfg['request']);
      }
      else if ( is_array($cfg['request']) ){
        $params = null;
        $request = $cfg['request'];
      }
      else{
        return null;
      }
      $res = $this->_exec($request, $params);
      $duration = (int)ceil($t->stop() * 1000);
      if ( is_array($request) ){
        $request = json_encode($request);
      }
      $id_alias = $this->_get_id($request, $params);
      //die(var_dump($id_alias, $this->db->last()));
      // If it is a public observer it will be the id_alias and the main observer
      if (
        !$id_alias &&
        !empty($cfg['public']) &&
        $this->db->insert('bbn_observers', [
          'request' => $request,
          'params' => $params ?: null,
          'name' => $cfg['name'] ?? null,
          'duration' => $duration,
          'id_user' => null,
          'public' => 1,
          'result' => $res
        ])
      ){
        $id_alias = $this->db->last_id();
      }
      // Getting the ID of the observer corresponding to current user
      if ( $id_obs = $this->_get_id_from_user($request, $params) ){
        //
        $this->check_result($id_obs);
        return $id_obs;
      }
      else if ( $id_alias ){
        if ( $this->db->insert('bbn_observers', [
          'id_user' => $this->id_user,
          'public' => 0,
          'id_alias' => $id_alias,
          'next' => null,
          'result' => $res
        ]) ){
          return $this->db->last_id();
        }
      }
      else{
        if ( $this->db->insert('bbn_observers', [
          'request' => $request,
          'params' => $params ?: null,
          'name' => $cfg['name'] ?? null,
          'duration' => $duration,
          'id_user' => $this->id_user,
          'public' => 0,
          'result' => $res
        ]) ){
          return $this->db->last_id();
        }
      }
    }
    return null;
  }
840541
public function generateNewPDO() :\PDO
    {
        if($this->validateConfig()) {
            $dns = $this->driver . ':dbname=' . $this->database . ";host=" . $this->host;
            return new \PDO($dns, $this->user, $this->pass);
        }
        else {
            throw (new \Exception("Invalid or missing database connection parameters"));
        }
    }
143080
public function connect(array $config)
	{
		return new DatabaseQueue(
			$this->connections->connection(Arr::get($config, 'connection')),
			$config['table'],
			$config['queue'],
			Arr::get($config, 'expire', 60)
		);
	}