1: | <?php
|
2: |
|
3: | |
4: | |
5: | |
6: | |
7: | |
8: | |
9: | |
10: | |
11: | |
12: | |
13: | |
14: | |
15: |
|
16: |
|
17: | namespace LucidFrame\Core;
|
18: |
|
19: | |
20: | |
21: | |
22: |
|
23: | class Database
|
24: | {
|
25: | private $namespace = 'default';
|
26: |
|
27: | private $config = array();
|
28: |
|
29: | private $driver = 'mysql';
|
30: | private $host;
|
31: | private $port;
|
32: | private $username;
|
33: | private $password;
|
34: | private $name;
|
35: | private $charset = 'utf8';
|
36: | private $collation = 'utf8_unicode_ci';
|
37: | private $prefix = '';
|
38: |
|
39: | private $connection;
|
40: |
|
41: | public $schemaManager;
|
42: |
|
43: | private static $queries = array();
|
44: | private static $bindParams = array();
|
45: |
|
46: | private $errorCode;
|
47: | private $error;
|
48: |
|
49: | private static $FETCH_MODE_MAP = array(
|
50: | LC_FETCH_OBJECT => \PDO::FETCH_OBJ,
|
51: | LC_FETCH_ASSOC => \PDO::FETCH_ASSOC,
|
52: | LC_FETCH_ARRAY => \PDO::FETCH_NUM
|
53: | );
|
54: |
|
55: | public function __construct($namespace = null)
|
56: | {
|
57: | $this->config = _cfg('databases');
|
58: | if ($namespace === null) {
|
59: | $this->namespace = _cfg('defaultDbSource');
|
60: | }
|
61: |
|
62: | _app('db', $this);
|
63: |
|
64: | $this->connect();
|
65: | }
|
66: |
|
67: | |
68: | |
69: |
|
70: | public function getConnection()
|
71: | {
|
72: | return $this->connection;
|
73: | }
|
74: |
|
75: | |
76: | |
77: |
|
78: | public function getSchemaManager()
|
79: | {
|
80: | return $this->schemaManager;
|
81: | }
|
82: |
|
83: | |
84: | |
85: |
|
86: | public function setSchemaManager($schemaManager)
|
87: | {
|
88: | $this->schemaManager = $schemaManager;
|
89: | }
|
90: |
|
91: | |
92: | |
93: | |
94: | |
95: |
|
96: | public function connect($namespace = null)
|
97: | {
|
98: | if ($namespace) {
|
99: | $this->namespace = $namespace;
|
100: | }
|
101: |
|
102: | $this->driver = $this->getDriver();
|
103: | $this->host = $this->getHost();
|
104: | $this->port = $this->getPort();
|
105: | $this->username = $this->getUser();
|
106: | $this->password = $this->getPassword();
|
107: | $this->name = $this->getName();
|
108: | $this->prefix = $this->getPrefix();
|
109: | $this->charset = $this->getCharset();
|
110: | $this->collation = $this->getCollation();
|
111: |
|
112: | if ($this->driver) {
|
113: | if ($file = _i('helpers' . _DS_ . 'db_helper.php', false)) {
|
114: | include $file;
|
115: | }
|
116: |
|
117: | if ($this->driver === 'mysql') {
|
118: | require HELPER . 'db_helper.mysqli.php';
|
119: | } else {
|
120: | require HELPER . 'db_helper.' . $this->driver . '.php';
|
121: | }
|
122: |
|
123: | if ($this->getHost() && $this->getUser() && $this->getName()) {
|
124: |
|
125: | $dsn = sprintf('%s:host=%s;dbname=%s;charset=%s', $this->driver, $this->host, $this->name, $this->charset);
|
126: | $options = array(
|
127: | \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
|
128: | \PDO::MYSQL_ATTR_INIT_COMMAND => sprintf('SET NAMES %s COLLATE %s', $this->charset, $this->collation)
|
129: | );
|
130: |
|
131: | $this->connection = new \PDO($dsn, $this->username, $this->password, $options);
|
132: |
|
133: |
|
134: | $schema = _schema($this->namespace, true);
|
135: | $this->schemaManager = new SchemaManager($schema);
|
136: | if (!$this->schemaManager->isLoaded()) {
|
137: | $this->schemaManager->build($namespace);
|
138: | }
|
139: | }
|
140: | }
|
141: |
|
142: | return $this->connection;
|
143: | }
|
144: |
|
145: | |
146: | |
147: | |
148: | |
149: | |
150: | |
151: | |
152: |
|
153: | public function getNamespace($namespace = null)
|
154: | {
|
155: | if (!empty($namespace)) {
|
156: | return $namespace;
|
157: | }
|
158: |
|
159: | return $this->namespace;
|
160: | }
|
161: |
|
162: | |
163: | |
164: | |
165: | |
166: |
|
167: | public function getConfig($namespace = null)
|
168: | {
|
169: | $namespace = $this->getNamespace($namespace);
|
170: |
|
171: | if (!isset($this->config[$namespace])) {
|
172: | die('Database configuration error for ' . $namespace . '!');
|
173: | }
|
174: |
|
175: | return $this->config[$namespace];
|
176: | }
|
177: |
|
178: | |
179: | |
180: | |
181: | |
182: |
|
183: | public function getDriver($namespace = null)
|
184: | {
|
185: | $conf = $this->getConfig($this->getNamespace($namespace));
|
186: |
|
187: | return $conf['driver'];
|
188: | }
|
189: |
|
190: | |
191: | |
192: | |
193: | |
194: |
|
195: | public function getHost($namespace = null)
|
196: | {
|
197: | $conf = $this->getConfig($this->getNamespace($namespace));
|
198: |
|
199: | return $conf['host'];
|
200: | }
|
201: |
|
202: | |
203: | |
204: | |
205: | |
206: |
|
207: | public function getPort($namespace = null)
|
208: | {
|
209: | $conf = $this->getConfig($this->getNamespace($namespace));
|
210: |
|
211: | return $conf['port'];
|
212: | }
|
213: |
|
214: | |
215: | |
216: | |
217: | |
218: |
|
219: | public function getName($namespace = null)
|
220: | {
|
221: | $conf = $this->getConfig($this->getNamespace($namespace));
|
222: | if (!isset($conf['database'])) {
|
223: | die('Database name is not set.');
|
224: | }
|
225: |
|
226: | return $conf['database'];
|
227: | }
|
228: |
|
229: | |
230: | |
231: | |
232: | |
233: |
|
234: | public function getUser($namespace = null)
|
235: | {
|
236: | $conf = $this->getConfig($this->getNamespace($namespace));
|
237: | if (!isset($conf['username'])) {
|
238: | die('Database username is not set.');
|
239: | }
|
240: |
|
241: | return $conf['username'];
|
242: | }
|
243: |
|
244: | |
245: | |
246: | |
247: | |
248: |
|
249: | private function getPassword($namespace = null)
|
250: | {
|
251: | $conf = $this->getConfig($this->getNamespace($namespace));
|
252: | if (!isset($conf['password'])) {
|
253: | die('Database password is not set.');
|
254: | }
|
255: |
|
256: | return $conf['password'];
|
257: | }
|
258: |
|
259: | |
260: | |
261: | |
262: | |
263: |
|
264: | public function getPrefix($namespace = null)
|
265: | {
|
266: | $conf = $this->getConfig($this->getNamespace($namespace));
|
267: |
|
268: | return isset($conf['prefix']) ? $conf['prefix'] : $this->prefix;
|
269: | }
|
270: |
|
271: | |
272: | |
273: | |
274: | |
275: |
|
276: | public function getCharset($namespace = null)
|
277: | {
|
278: | $conf = $this->getConfig($this->getNamespace($namespace));
|
279: |
|
280: | return isset($conf['charset']) ? $conf['charset'] : $this->charset;
|
281: | }
|
282: |
|
283: | |
284: | |
285: | |
286: | |
287: |
|
288: | public function getCollation($namespace = null)
|
289: | {
|
290: | $conf = $this->getConfig($this->getNamespace($namespace));
|
291: |
|
292: | return isset($conf['collation']) ? $conf['collation'] : $this->collation;
|
293: | }
|
294: |
|
295: | |
296: | |
297: | |
298: | |
299: | |
300: | |
301: | |
302: | |
303: | |
304: | |
305: | |
306: | |
307: |
|
308: | public function query($sql, $args = array())
|
309: | {
|
310: | if (!is_array($args)) {
|
311: | $args = array();
|
312: | }
|
313: |
|
314: | $params = array();
|
315: | foreach ($args as $key => $value) {
|
316: | if (is_numeric($key)) {
|
317: | $params[$key] = $value;
|
318: | continue;
|
319: | }
|
320: |
|
321: | if (strpos($key, ':') === false) {
|
322: | $key = ':' . $key;
|
323: | }
|
324: |
|
325: | $params[$key] = $value;
|
326: | }
|
327: |
|
328: | try {
|
329: | if (empty($params)) {
|
330: | $stmt = $this->connection->query($sql);
|
331: | self::$queries[] = $sql;
|
332: | } else {
|
333: | $stmt = $this->connection->prepare($sql);
|
334: | $stmt->execute($params);
|
335: | self::$queries[] = $sql;
|
336: | self::$bindParams = $params;
|
337: | }
|
338: |
|
339: | if (_g('db_printQuery')) {
|
340: | return $this->getQueryStr();
|
341: | }
|
342: | } catch (\PDOException $e) {
|
343: | $this->errorCode = $e->getCode();
|
344: | $this->error = $e->getMessage();
|
345: |
|
346: | throw $e;
|
347: | }
|
348: |
|
349: | return $stmt;
|
350: | }
|
351: |
|
352: | |
353: | |
354: | |
355: | |
356: | |
357: |
|
358: | public function getQueryStr()
|
359: | {
|
360: | $arg = func_get_args();
|
361: | $index = count($arg) == 0 ? count(self::$queries) - 1 : 0;
|
362: |
|
363: | $sql = isset(self::$queries[$index]) ? self::$queries[$index] : '';
|
364: |
|
365: | if ($sql && count(self::$bindParams)) {
|
366: | foreach (self::$bindParams as $key => $value) {
|
367: | if (strpos($key, ':') === false) {
|
368: | $key = ':'.$key;
|
369: | }
|
370: |
|
371: | if (is_array($value)) {
|
372: | $value = implode(',', $value);
|
373: | $regex = '/'.$key.'\b/i';
|
374: | $sql = preg_replace($regex, $value, $sql);
|
375: | } else {
|
376: | $regex = '/'.$key.'\b/i';
|
377: | $sql = preg_replace($regex, $value, $sql);
|
378: | }
|
379: | }
|
380: | }
|
381: |
|
382: | return $sql;
|
383: | }
|
384: |
|
385: | |
386: | |
387: | |
388: | |
389: |
|
390: | public function fetchAssoc($stmt)
|
391: | {
|
392: | return $stmt ? $stmt->fetch(\PDO::FETCH_ASSOC) : false;
|
393: | }
|
394: |
|
395: | |
396: | |
397: | |
398: | |
399: | |
400: |
|
401: | public function fetchArray($stmt)
|
402: | {
|
403: | return $stmt ? $stmt->fetch(\PDO::FETCH_NUM) : false;
|
404: | }
|
405: |
|
406: | |
407: | |
408: | |
409: | |
410: |
|
411: | public function fetchObject($stmt)
|
412: | {
|
413: | return $stmt ? $stmt->fetch(\PDO::FETCH_OBJ) : false;
|
414: | }
|
415: |
|
416: | |
417: | |
418: | |
419: | |
420: | |
421: | |
422: | |
423: | |
424: | |
425: | |
426: | |
427: | |
428: |
|
429: | public function fetchAll($sql, $args = array(), $resultType = LC_FETCH_OBJECT)
|
430: | {
|
431: | if (is_numeric($args)) {
|
432: | if (in_array($args, array(LC_FETCH_OBJECT, LC_FETCH_ASSOC, LC_FETCH_ARRAY))) {
|
433: | $resultType = $args;
|
434: | }
|
435: | $args = array();
|
436: | }
|
437: |
|
438: | $stmt = $this->query($sql, $args);
|
439: | $data = $stmt->fetchAll(self::$FETCH_MODE_MAP[$resultType]);
|
440: |
|
441: | return count($data) ? $data : false;
|
442: | }
|
443: |
|
444: | |
445: | |
446: | |
447: | |
448: | |
449: | |
450: | |
451: | |
452: | |
453: | |
454: | |
455: | |
456: | |
457: | |
458: | |
459: |
|
460: | function fetchResult($sql, $args = array())
|
461: | {
|
462: | $sql = $this->appendLimit($sql);
|
463: |
|
464: | if ($result = $this->query($sql, $args)) {
|
465: | if ($row = $this->fetchObject($result)) {
|
466: | return $row;
|
467: | }
|
468: | }
|
469: |
|
470: | return false;
|
471: | }
|
472: |
|
473: | |
474: | |
475: | |
476: | |
477: | |
478: | |
479: | |
480: | |
481: | |
482: | |
483: | |
484: | |
485: | |
486: | |
487: | |
488: |
|
489: | public function fetchColumn($sql, $args = array())
|
490: | {
|
491: | $sql = $this->appendLimit($sql);
|
492: |
|
493: | if ($result = $this->query($sql, $args)) {
|
494: | return $result->fetchColumn();
|
495: | }
|
496: |
|
497: | return false;
|
498: | }
|
499: |
|
500: | |
501: | |
502: | |
503: | |
504: |
|
505: | public function getNumRows($stmt)
|
506: | {
|
507: | return $stmt->rowCount();
|
508: | }
|
509: |
|
510: | |
511: | |
512: | |
513: | |
514: | |
515: | |
516: | |
517: | |
518: | |
519: | |
520: | |
521: | |
522: | |
523: | |
524: | |
525: | |
526: |
|
527: | public function getCount($arg1, $arg2 = null, $arg3 = null)
|
528: | {
|
529: | QueryBuilder::clearBindValues();
|
530: |
|
531: | if ($arg1 && QueryBuilder::validateName($arg1)) {
|
532: | $table = $arg1;
|
533: | $alias = 'count';
|
534: |
|
535: | $qb = new QueryBuilder($table);
|
536: |
|
537: | if ($arg3 && QueryBuilder::validateName($arg3)) {
|
538: | $alias = $arg3;
|
539: | }
|
540: |
|
541: | if ($arg2 && QueryBuilder::validateName($arg2)) {
|
542: | $field = $arg2;
|
543: | $qb->count($field, $alias);
|
544: | } else {
|
545: | $qb->count('*', 'count');
|
546: | }
|
547: |
|
548: | return $qb;
|
549: | } else {
|
550: | $sql = $arg1;
|
551: | $args = $arg2;
|
552: |
|
553: | if ($result = $this->fetchColumn($sql, $args)) {
|
554: | return $result;
|
555: | }
|
556: | }
|
557: |
|
558: | return 0;
|
559: | }
|
560: |
|
561: | |
562: | |
563: | |
564: | |
565: |
|
566: | public function getInsertId()
|
567: | {
|
568: | return $this->connection ? $this->connection->lastInsertId() : 0;
|
569: | }
|
570: |
|
571: | |
572: | |
573: | |
574: |
|
575: | public function getError()
|
576: | {
|
577: | return $this->error;
|
578: | }
|
579: |
|
580: | |
581: | |
582: | |
583: |
|
584: | public function getErrorCode()
|
585: | {
|
586: | return $this->errorCode;
|
587: | }
|
588: |
|
589: | |
590: | |
591: | |
592: |
|
593: | public function close()
|
594: | {
|
595: | $this->connection = null;
|
596: | }
|
597: |
|
598: | |
599: | |
600: | |
601: | |
602: |
|
603: | public function getTable($table)
|
604: | {
|
605: | $prefix = $this->getPrefix();
|
606: |
|
607: | if (empty($prefix)) {
|
608: | return $table;
|
609: | }
|
610: |
|
611: | if ($prefix == substr($table, 0, strlen($prefix))) {
|
612: | return $table;
|
613: | }
|
614: |
|
615: | return $prefix . $table;
|
616: | }
|
617: |
|
618: | |
619: | |
620: | |
621: | |
622: | |
623: | |
624: |
|
625: | public function hasSlug($table, $useSlug = true)
|
626: | {
|
627: | if ($useSlug == false) {
|
628: | return false;
|
629: | }
|
630: |
|
631: | return $this->schemaManager->hasSlug($table);
|
632: | }
|
633: |
|
634: | |
635: | |
636: | |
637: | |
638: | |
639: |
|
640: | public function hasTimestamps($table)
|
641: | {
|
642: | return $this->schemaManager->hasTimestamps($table);
|
643: | }
|
644: |
|
645: | |
646: | |
647: | |
648: | |
649: | |
650: | |
651: | |
652: | |
653: | |
654: | |
655: | |
656: | |
657: | |
658: | |
659: |
|
660: | public function exp($field, $value, $exp = '')
|
661: | {
|
662: | if ($exp) {
|
663: | $field = strtoupper($field) . '(' . $value . ')';
|
664: | } else {
|
665: | $field = '';
|
666: | }
|
667: |
|
668: | return array(
|
669: | 'value' => $value,
|
670: | 'exp' => $exp,
|
671: | 'field' => $field
|
672: | );
|
673: | }
|
674: |
|
675: | |
676: | |
677: | |
678: | |
679: |
|
680: | private function appendLimit($sql)
|
681: | {
|
682: | if (! preg_match('/LIMIT\s+[0-9]{1,}\b/i', $sql)) {
|
683: | $sql .= ' LIMIT 1';
|
684: | }
|
685: |
|
686: | return $sql;
|
687: | }
|
688: | }
|
689: | |