Completed
Push — master ( bd5921...d9293e )
by Christian
17:54 queued 08:59
created

src/N98/Magento/DbSettings.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/*
3
 * @author Tom Klingenberg <[email protected]>
4
 */
5
6
namespace N98\Magento;
7
8
use ArrayAccess;
9
use ArrayIterator;
10
use BadMethodCallException;
11
use InvalidArgumentException;
12
use IteratorAggregate;
13
use PDO;
14
use PDOException;
15
use RuntimeException;
16
use SimpleXMLElement;
17
18
/**
19
 * Class DbSettings
20
 *
21
 * Database settings.
22
 *
23
 * The Magento database settings are stored in a SimpleXMLElement structure
24
 *
25
 * @package N98\Magento
26
 */
27
class DbSettings implements ArrayAccess, IteratorAggregate
28
{
29
    /**
30
     * @var string|null known field members
31
     */
32
    private $tablePrefix, $host, $port, $unixSocket, $dbName, $username, $password;
0 ignored issues
show
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
33
34
    /**
35
     * @var array field array
36
     */
37
    private $config;
38
39
    /**
40
     * @param string $file path to app/etc/local.xml
41
     */
42
    public function __construct($file)
43
    {
44
        $this->setFile($file);
45
    }
46
47
    /**
48
     * @param string $file path to app/etc/local.xml
49
     *
50
     * @throws InvalidArgumentException if the file is invalid
51
     */
52
    public function setFile($file)
53
    {
54
        if (!is_readable($file)) {
55
            throw new InvalidArgumentException(
56
                sprintf('"app/etc/local.xml"-file %s is not readable', var_export($file, true))
57
            );
58
        }
59
60
        $saved = libxml_use_internal_errors(true);
61
        $config = simplexml_load_file($file);
62
        libxml_use_internal_errors($saved);
63
64
        if (false === $config) {
65
            throw new InvalidArgumentException(
66
                sprintf('Unable to open "app/etc/local.xml"-file %s and parse it as XML', var_export($file, true))
67
            );
68
        }
69
70
        $resources = $config->global->resources;
71
        if (!$resources) {
72
            throw new InvalidArgumentException('DB global resources was not found in "app/etc/local.xml"-file');
73
        }
74
75
        if (!$resources->default_setup->connection) {
76
            throw new InvalidArgumentException('DB settings (default_setup) was not found in "app/etc/local.xml"-file');
77
        }
78
79
        $this->parseResources($resources);
80
    }
81
82
    /**
83
     * helper method to parse config file segment related to the database settings
84
     *
85
     * @param SimpleXMLElement $resources
86
     */
87
    private function parseResources(SimpleXMLElement $resources)
88
    {
89
        // default values
90
        $config = array(
91
            'host'        => null,
92
            'port'        => null,
93
            'unix_socket' => null,
94
            'dbname'      => null,
95
            'username'    => null,
96
            'password'    => null,
97
        );
98
99
        $config           = ((array) $resources->default_setup->connection) + $config;
100
        $config['prefix'] = (string) $resources->db->table_prefix;
101
102
        // known parameters: host, port, unix_socket, dbname, username, password, options, charset, persistent,
103
        //                   driver_options
104
        //                   (port is deprecated; removed in magento 2, use port in host setting <host>:<port>)
105
106
        unset($config['comment']);
107
108
        /* @see Varien_Db_Adapter_Pdo_Mysql::_connect */
109
        if (strpos($config['host'], '/') !== false) {
110
            $config['unix_socket'] = (string) $config['host'];
111
            $config['host'] = null;
112
            $config['port'] = null;
113
        } else if (strpos($config['host'], ':') !== false) {
114
            list($config['host'], $config['port']) = explode(':', $config['host']);
115
            $config['unix_socket'] = null;
116
        }
117
118
        $this->config      = $config;
119
120
        $this->tablePrefix = $config['prefix'];
121
        $this->host        = $config['host'];
122
        $this->port        = $config['port'];
123
        $this->unixSocket  = $config['unix_socket'];
124
        $this->dbName      = $config['dbname'];
125
        $this->username    = $config['username'];
126
        $this->password    = $config['password'];
127
    }
128
129
    /**
130
     * Get Mysql PDO DSN
131
     *
132
     * @return string
133
     */
134
    public function getDsn()
135
    {
136
        $dsn = 'mysql:';
137
138
        $named = array();
139
140
        // blacklisted in prev. DSN creation: username, password, options, charset, persistent, driver_options, dbname
141
142
        if (isset($this->unixSocket)) {
143
            $named['unix_socket'] = $this->unixSocket;
144
        } else {
145
            $named['host'] = $this->host;
146
            if (isset($this->port)) {
147
                $named['port'] = $this->port;
148
            }
149
        }
150
151
        $options = array();
152
        foreach ($named as $name => $value) {
153
            $options[$name] = "{$name}={$value}";
154
        }
155
156
        return $dsn . implode(';', $options);
157
    }
158
159
    /**
160
     * Connects to the database without initializing magento
161
     *
162
     * @throws RuntimeException if pdo_mysql extension is not installed
163
     * @return PDO
164
     */
165
    public function getConnection()
166
    {
167
        if (!extension_loaded('pdo_mysql')) {
168
            throw new RuntimeException('pdo_mysql extension is not installed');
169
        }
170
171
        $database = $this->getDatabaseName();
172
173
        $connection = new PDO(
174
            $this->getDsn(),
175
            $this->getUsername(),
176
            $this->getPassword()
177
        );
178
179
        /** @link http://bugs.mysql.com/bug.php?id=18551 */
180
        $connection->query("SET SQL_MODE=''");
181
182
        try {
183
            $connection->query('USE ' . $this->quoteIdentifier($database));
184
        } catch (PDOException $e) {
185
            throw new RuntimeException(
186
                sprintf("Unable to use database '%s': %s %s", $database, get_class($e), $e->getMessage()), 0, $e
187
            );
188
        }
189
190
        $connection->query("SET NAMES utf8");
191
192
        $connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
193
        $connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
194
195
        return $connection;
196
    }
197
198
    public function getMysqlClientToolConnectionString()
199
    {
200
        $segments = array();
201
202
        if (null !== $this->config['unix_socket']) {
203
            $segments[] = '--socket=' . escapeshellarg($this->config['unix_socket']);
204
        } else {
205
            $segments[] = '-h' . escapeshellarg($this->config['host']);
206
        }
207
208
        $segments[] = '-u' . escapeshellarg($this->config['username']);
209
        if (null !== $this->config['port']) {
210
            $segments[] = '-P' . escapeshellarg($this->config['port']);
211
        }
212
        if (strlen($this->config['password'])) {
213
            $segments[] = '-p' . escapeshellarg($this->config['password']);
214
        }
215
        $segments[] = escapeshellarg($this->config['dbname']);
216
217
        return implode(' ', $segments);
218
    }
219
220
    /**
221
     * Mysql quoting of an identifier
222
     *
223
     * @param string $identifier UTF-8 encoded
224
     *
225
     * @return string quoted identifier
226
     */
227
    private function quoteIdentifier($identifier)
228
    {
229
        $quote = '`'; // le backtique
230
231
        $pattern = '~^(?:[\x1-\x7F]|[\xC2-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2})+$~';
232
233
        if (!preg_match($pattern, $identifier)) {
234
            throw new InvalidArgumentException(
235
                sprintf(
236
                    'Invalid identifier, must not contain NUL and must be UTF-8 encoded in the BMP: %s (hex: %s)',
237
                    var_export($identifier), bin2hex($identifier)
238
                )
239
            );
240
        }
241
242
        return $quote . strtr($identifier, array($quote => $quote . $quote)) . $quote;
243
    }
244
245
    /**
246
     * @return bool
247
     */
248
    public function isSocketConnect()
249
    {
250
        return isset($this->config['unix_socket']);
251
    }
252
253
    /**
254
     * @return string table prefix, null if not in the settings (no or empty prefix)
255
     */
256
    public function getTablePrefix()
257
    {
258
        return $this->tablePrefix;
259
    }
260
261
    /**
262
     * @return string hostname, null if there is no hostname setup (e.g. unix_socket)
263
     */
264
    public function getHost()
265
    {
266
        return $this->host;
267
    }
268
269
    /**
270
     * @return string port, null if not setup
271
     */
272
    public function getPort()
273
    {
274
        return $this->port;
275
    }
276
277
    /**
278
     * @return string username
279
     */
280
    public function getUsername()
281
    {
282
        return $this->username;
283
    }
284
285
    /**
286
     * @return string password
287
     */
288
    public function getPassword()
289
    {
290
        return $this->password;
291
    }
292
293
    /**
294
     * @return string unix socket, null if not in use
295
     */
296
    public function getUnixSocket()
297
    {
298
        return $this->unixSocket;
299
    }
300
301
    /**
302
     * content of previous $dbSettings field of the DatabaseHelper
303
     *
304
     * @return array
305
     */
306
    public function getConfig()
307
    {
308
        return $this->config;
309
    }
310
311
    /**
312
     * @return string of the database identifier, null if not in use
313
     */
314
    public function getDatabaseName()
315
    {
316
        return $this->dbName;
317
    }
318
319
    /*
320
     * ArrayAccess interface
321
     */
322
323
    /**
324
     * @return boolean true on success or false on failure.
325
     */
326
    public function offsetExists($offset)
327
    {
328
        return isset($this->config[$offset]);
329
    }
330
331
    /**
332
     * @return mixed Can return all value types.
333
     */
334
    public function offsetGet($offset)
335
    {
336
        if (isset($this->config[$offset])) {
337
            return $this->config[$offset];
338
        }
339
340
        return null;
341
    }
342
343
    /**
344
     * @param mixed $offset
345
     * @param mixed $value
346
     *
347
     * @throws BadMethodCallException
348
     */
349
    public function offsetSet($offset, $value)
350
    {
351
        throw new BadMethodCallException('dbSettings are read-only');
352
    }
353
354
    /**
355
     * @param mixed $offset
356
     *
357
     * @throws BadMethodCallException
358
     */
359
    public function offsetUnset($offset)
360
    {
361
        throw new BadMethodCallException('dbSettings are read-only');
362
    }
363
364
    /*
365
     * IteratorAggregate
366
     */
367
368
    /**
369
     * @return ArrayIterator
370
     */
371
    public function getIterator()
372
    {
373
        return new ArrayIterator($this->config);
374
    }
375
}
376