<?php

/**
 * Matomo - free/libre analytics platform
 *
 * @link    https://matomo.org
 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 */
namespace Piwik\Db;

use Zend_Db_Table;
use Piwik\Piwik;
class Adapter
{
    /**
     * Create adapter
     *
     * @param string $adapterName database adapter name
     * @param array $dbInfos database connection info
     * @param bool $connect
     * @return AdapterInterface
     */
    public static function factory($adapterName,
#[\SensitiveParameter]
&$dbInfos, $connect = \true)
    {
        if ($connect) {
            if (isset($dbInfos['port']) && is_string($dbInfos['port']) && $dbInfos['port'][0] === '/') {
                $dbInfos['unix_socket'] = $dbInfos['port'];
                unset($dbInfos['host']);
                unset($dbInfos['port']);
            }
            // not used by Zend Framework
            unset($dbInfos['tables_prefix']);
            unset($dbInfos['adapter']);
            unset($dbInfos['schema']);
        }
        $className = self::getAdapterClassName($adapterName);
        // make sure not to pass any references otherwise they will modify $dbInfos
        $infos = array();
        foreach ($dbInfos as $key => $val) {
            $infos[$key] = $val;
        }
        $adapter = new $className($infos);
        if ($connect) {
            try {
                $adapter->getConnection();
                Zend_Db_Table::setDefaultAdapter($adapter);
                // we don't want the connection information to appear in the logs
                $adapter->resetConfig();
            } catch (\Exception $e) {
                // we don't want certain exceptions to leak information
                $msg = self::overriddenExceptionMessage($e->getMessage());
                if ('' !== $msg) {
                    throw new \Exception($msg);
                }
                throw $e;
            }
        }
        return $adapter;
    }
    /**
     * Get adapter class name
     *
     * @param string $adapterName
     * @return string
     * @throws \Exception
     */
    private static function getAdapterClassName($adapterName)
    {
        $className = 'Piwik\\Db\\Adapter\\' . str_replace(' ', '\\', ucwords(str_replace(array('_', '\\'), ' ', strtolower($adapterName))));
        if (!class_exists($className)) {
            throw new \Exception(sprintf("Adapter '%s' is not valid. Maybe check that your Matomo configuration files in config/*.ini.php are readable by the webserver.", $adapterName));
        }
        return $className;
    }
    /**
     * Get default port for named adapter
     *
     * @deprecated use Schema::getDefaultPortForSchema instead
     * @param string $adapterName
     * @return int
     */
    public static function getDefaultPortForAdapter($adapterName)
    {
        $className = self::getAdapterClassName($adapterName);
        return call_user_func(array($className, 'getDefaultPort'));
    }
    /**
     * Get list of adapters
     *
     * @return array
     */
    public static function getAdapters()
    {
        static $adapterNames = array(
            // currently supported by Piwik
            'Pdo\\Mysql',
            'Mysqli',
        );
        $adapters = array();
        foreach ($adapterNames as $adapterName) {
            $className = '\\Piwik\\Db\\Adapter\\' . $adapterName;
            if (call_user_func(array($className, 'isEnabled'))) {
                $adapters[] = strtoupper($adapterName);
            }
        }
        return $adapters;
    }
    /**
     * Checks if the available adapters are recommended by Piwik or not.
     * @param string $adapterName
     * @return bool
     */
    public static function isRecommendedAdapter($adapterName)
    {
        return strtolower($adapterName) === 'pdo/mysql';
    }
    /**
     * Intercepts certain exception messages and replaces leaky ones with ones that don't reveal too much info
     * @param string $message
     * @return string
     */
    public static function overriddenExceptionMessage($message)
    {
        $safeMessageMap = array(
            // add any exception search terms and their replacement message here
            '[2006]' => Piwik::translate('General_ExceptionDatabaseUnavailable'),
            'MySQL server has gone away' => Piwik::translate('General_ExceptionDatabaseUnavailable'),
            '[1698]' => Piwik::translate('General_ExceptionDatabaseAccess'),
            'Access denied' => Piwik::translate('General_ExceptionDatabaseAccess'),
        );
        foreach ($safeMessageMap as $search_term => $safeMessage) {
            if (strpos($message, $search_term) !== \false) {
                return $safeMessage;
            }
        }
        return '';
    }
}
