1<?php
2/**
3 * A Horde_Injector:: based factory for creating Horde_Db_Adapter objects.
4 *
5 * PHP version 5
6 *
7 * @category Horde
8 * @package  Core
9 * @author   Michael Slusarz <slusarz@horde.org>
10 * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
11 */
12
13/**
14 * A Horde_Injector:: based factory for creating Horde_Db_Adapter objects.
15 *
16 * Copyright 2010-2017 Horde LLC (http://www.horde.org/)
17 *
18 * See the enclosed file COPYING for license information (LGPL). If you
19 * did not receive this file, see http://www.horde.org/licenses/lgpl21.
20 *
21 * @category Horde
22 * @package  Core
23 * @author   Michael Slusarz <slusarz@horde.org>
24 * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
25 */
26class Horde_Core_Factory_Db extends Horde_Core_Factory_Base
27{
28    /**
29     * Instances.
30     *
31     * @var array
32     */
33    private $_instances = array();
34
35    /**
36     * Returns the DB instance.
37     *
38     * @param string $app            The application.
39     * @param string|array $backend  The backend, see Horde::getDriverConfig().
40     *                               If this is an array, this is used as the
41     *                               configuration array.
42     *
43     * @return Horde_Db_Adapter  The singleton instance.
44     * @throws Horde_Exception
45     * @throws Horde_Db_Exception
46     */
47    public function create($app = 'horde', $backend = null)
48    {
49        global $registry;
50
51        $pushed = ($app == 'horde')
52            ? false
53            : $registry->pushApp($app);
54
55        $config = is_array($backend)
56            ? $backend
57            : $this->getConfig($backend);
58
59        /* Prevent DSN from getting polluted (this only applies to non-custom
60         * auth type connections. All other custom sql configurations MUST be
61         * cleansed prior to passing to the factory (at least until Horde 5).
62         * @todo Fix with Horde 6. */
63        if (!is_array($backend) && ($backend == 'auth')) {
64            unset(
65                $config['count_bad_logins'],
66                $config['cyradmin'],
67                $config['cyrhost'],
68                $config['cyrpass'],
69                $config['cyrport'],
70                $config['domain_field'],
71                $config['driverconfig'],
72                $config['encryption'],
73                $config['folders'],
74                $config['hidden_accounts'],
75                $config['login_block'],
76                $config['login_block_count'],
77                $config['login_block_time'],
78                $config['password_field'],
79                $config['query_auth'],
80                $config['query_add'],
81                $config['query_getpw'],
82                $config['query_update'],
83                $config['query_resetpassword'],
84                $config['query_remove'],
85                $config['query_list'],
86                $config['query_exists'],
87                $config['show_encryption'],
88                $config['table'],
89                $config['userhierarchy'],
90                $config['username_field']
91            );
92        }
93        unset($config['umask']);
94
95        $e = null;
96
97        ksort($config);
98        $sig = hash('md5', serialize($config));
99
100        /* Determine if we are using the base SQL config. */
101        if (isset($config['driverconfig']) &&
102            ($config['driverconfig'] == 'horde')) {
103            $this->_instances[$sig] = $this->create();
104        } elseif (!isset($this->_instances[$sig])) {
105            try {
106                $this->_createDb($config, $sig);
107            } catch (Horde_Exception $e) {}
108        }
109
110        if ($pushed) {
111            $registry->popApp();
112        }
113
114        if ($e) {
115            throw $e;
116        }
117
118        return $this->_instances[$sig];
119    }
120
121    /**
122     */
123    public function getConfig($backend)
124    {
125        return Horde::getDriverConfig($backend, 'sql');
126    }
127
128    /**
129     */
130    public function createDb($config)
131    {
132        return $this->_createDb($config);
133    }
134
135    /**
136     * @param string $sig     Save instance under this signature key.
137     * @param boolean $cache  Add default cache to driver?
138     */
139    protected function _createDb($config, $sig = null, $cache = true)
140    {
141        unset($config['driverconfig']);
142        $logqueries = !empty($config['logqueries']);
143        unset($config['logqueries']);
144
145        // Split read?
146        if (!empty($config['splitread'])) {
147            $read_config = $config['read'];
148            unset($config['read'], $config['splitread']);
149            $ob = new Horde_Db_Adapter_SplitRead(
150                $this->_createDb(array_merge($config, $read_config), null, false),
151                $this->_createDb($config, null, false)
152            );
153
154            /* Don't attach logger to base split read object. */
155            $config['logger'] = true;
156        } else {
157            if (isset($config['adapter'])) {
158                $class = $this->_getDriverName($config['adapter'], 'Horde_Db_Adapter');
159                unset($config['adapter']);
160            } elseif (empty($config['phptype'])) {
161                throw new Horde_Exception('The database configuration is missing.');
162            } else {
163                switch ($config['phptype']) {
164                case 'mysqli':
165                    $class = 'Horde_Db_Adapter_Mysqli';
166                    break;
167
168                case 'mysql':
169                    $class = extension_loaded('pdo_mysql')
170                        ? 'Horde_Db_Adapter_Pdo_Mysql'
171                        : 'Horde_Db_Adapter_Mysql';
172                    break;
173
174                case 'oci8':
175                    $class = 'Horde_Db_Adapter_Oci8';
176                    break;
177
178                default:
179                    $class = 'Horde_Db_Adapter_Pdo_' . Horde_String::ucfirst($config['phptype']);
180                    break;
181                }
182            }
183
184            if (!empty($config['hostspec'])) {
185                $config['host'] = $config['hostspec'];
186                unset($config['hostspec']);
187            }
188
189            $ob = new $class($config);
190        }
191
192        if ($sig) {
193            $this->_instances[$sig] = $ob;
194        }
195
196        if ($cache && !isset($config['cache'])) {
197            /* Need to add cache ob here, after it is stored as an instance,
198             * or else we enter infinite bootstrapping loop. Bug #13439 */
199            $ob->setCache($this->_injector->getInstance('Horde_Cache'));
200        }
201
202        /* Bug #13463: setting logger before cache causes intermittent issues
203         * with DB object during session shutdown. */
204        if (!isset($config['logger'])) {
205            $ob->setLogger(
206                $this->_injector->getInstance('Horde_Log_Logger'),
207                $logqueries
208            );
209        }
210
211        return $ob;
212    }
213
214}
215