1: <?php
2: /**
3: * Copyright 2007 Maintainable Software, LLC
4: * Copyright 2006-2012 Horde LLC (http://www.horde.org/)
5: *
6: * @author Mike Naberezny <mike@maintainable.com>
7: * @author Derek DeVries <derek@maintainable.com>
8: * @author Chuck Hagenbuch <chuck@horde.org>
9: * @license http://www.horde.org/licenses/bsd
10: * @category Horde
11: * @package Db
12: * @subpackage Adapter
13: */
14:
15: /**
16: * @author Mike Naberezny <mike@maintainable.com>
17: * @author Derek DeVries <derek@maintainable.com>
18: * @author Chuck Hagenbuch <chuck@horde.org>
19: * @license http://www.horde.org/licenses/bsd
20: * @category Horde
21: * @package Db
22: * @subpackage Adapter
23: */
24: abstract class Horde_Db_Adapter_Pdo_Base extends Horde_Db_Adapter_Base
25: {
26: /*##########################################################################
27: # Connection Management
28: ##########################################################################*/
29:
30: /**
31: * Connect to the db
32: */
33: public function connect()
34: {
35: if ($this->_active) {
36: return;
37: }
38:
39: list($dsn, $user, $pass) = $this->_parseConfig();
40:
41: try {
42: $pdo = @new PDO($dsn, $user, $pass);
43: } catch (PDOException $e) {
44: $msg = "Could not instantiate PDO with DSN \"$dsn\". PDOException: "
45: . $e->getMessage();
46: throw new Horde_Db_Exception($msg);
47: }
48:
49: $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
50: $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
51:
52: $this->_connection = $pdo;
53: $this->_active = true;
54: }
55:
56: /**
57: * Check if the connection is active
58: *
59: * @return boolean
60: */
61: public function isActive()
62: {
63: $this->_lastQuery = $sql = 'SELECT 1';
64: return isset($this->_connection) &&
65: $this->_connection->query($sql);
66: }
67:
68:
69: /*##########################################################################
70: # Database Statements
71: ##########################################################################*/
72:
73: /**
74: * Returns an array of record hashes with the column names as keys and
75: * column values as values.
76: *
77: * @param string $sql
78: * @param mixed $arg1 Either an array of bound parameters or a query name.
79: * @param string $arg2 If $arg1 contains bound parameters, the query name.
80: */
81: public function selectAll($sql, $arg1=null, $arg2=null)
82: {
83: $result = $this->execute($sql, $arg1, $arg2);
84: return $result ? $result->fetchAll(PDO::FETCH_ASSOC) : array();
85: }
86:
87: /**
88: * Returns a record hash with the column names as keys and column values as
89: * values.
90: *
91: * @param string $sql A query.
92: * @param mixed $arg1 Either an array of bound parameters or a query name.
93: * @param string $arg2 If $arg1 contains bound parameters, the query name.
94: *
95: * @return array|boolean A record hash or false if no record found.
96: */
97: public function selectOne($sql, $arg1 = null, $arg2 = null)
98: {
99: $result = $this->execute($sql, $arg1, $arg2);
100: return $result ? $result->fetch(PDO::FETCH_ASSOC) : array();
101: }
102:
103: /**
104: * Returns a single value from a record
105: *
106: * @param string $sql
107: * @param mixed $arg1 Either an array of bound parameters or a query name.
108: * @param string $arg2 If $arg1 contains bound parameters, the query name.
109: * @return string
110: */
111: public function selectValue($sql, $arg1=null, $arg2=null)
112: {
113: $result = $this->execute($sql, $arg1, $arg2);
114: return $result ? $result->fetchColumn(0) : null;
115: }
116:
117: /**
118: * Returns an array of the values of the first column in a select:
119: * selectValues("SELECT id FROM companies LIMIT 3") => [1,2,3]
120: *
121: * @param string $sql
122: * @param mixed $arg1 Either an array of bound parameters or a query name.
123: * @param string $arg2 If $arg1 contains bound parameters, the query name.
124: */
125: public function selectValues($sql, $arg1=null, $arg2=null)
126: {
127: $result = $this->execute($sql, $arg1, $arg2);
128: return $result ? $result->fetchAll(PDO::FETCH_COLUMN, 0) : array();
129: }
130:
131: /**
132: * Returns an array where the keys are the first column of a select, and the
133: * values are the second column:
134: *
135: * selectAssoc("SELECT id, name FROM companies LIMIT 3") => [1 => 'Ford', 2 => 'GM', 3 => 'Chrysler']
136: *
137: * @param string $sql
138: * @param mixed $arg1 Either an array of bound parameters or a query name.
139: * @param string $arg2 If $arg1 contains bound parameters, the query name.
140: */
141: public function selectAssoc($sql, $arg1=null, $arg2=null)
142: {
143: // PDO::FETCH_KEY_PAIR is only available since PHP 5.2.3
144: if (version_compare(PHP_VERSION, '5.2.3') < 0) {
145: return parent::selectAssoc($sql, $arg1, $arg2);
146: }
147: $result = $this->execute($sql, $arg1, $arg2);
148: return $result ? $result->fetchAll(PDO::FETCH_KEY_PAIR) : array();
149: }
150:
151:
152: /*##########################################################################
153: # Quoting
154: ##########################################################################*/
155:
156: /**
157: * Quotes a string, escaping any ' (single quote) and \ (backslash)
158: * characters..
159: *
160: * @param string $string
161: * @return string
162: */
163: public function quoteString($string)
164: {
165: return $this->_connection->quote($string);
166: }
167:
168:
169: /*##########################################################################
170: # Protected
171: ##########################################################################*/
172:
173: protected function _normalizeConfig($params)
174: {
175: // Normalize config parameters to what PDO expects.
176: $normalize = array('database' => 'dbname',
177: 'hostspec' => 'host');
178:
179: foreach ($normalize as $from => $to) {
180: if (isset($params[$from])) {
181: $params[$to] = $params[$from];
182: unset($params[$from]);
183: }
184: }
185:
186: return $params;
187: }
188:
189: protected function _buildDsnString($params)
190: {
191: $dsn = $this->_config['adapter'] . ':';
192: foreach ($params as $k => $v) {
193: if (strlen($v)) {
194: $dsn .= "$k=$v;";
195: }
196: }
197: return rtrim($dsn, ';');
198: }
199:
200: /**
201: * Parse configuration array into options for PDO constructor.
202: *
203: * @throws Horde_Db_Exception
204: * @return array [dsn, username, password]
205: */
206: protected function _parseConfig()
207: {
208: $this->_checkRequiredConfig(array('adapter', 'username'));
209:
210: // try an empty password if it's not set.
211: if (!isset($this->_config['password'])) {
212: $this->_config['password'] = '';
213: }
214:
215: // collect options to build PDO Data Source Name (DSN) string
216: $dsnOpts = $this->_config;
217: unset(
218: $dsnOpts['adapter'],
219: $dsnOpts['username'],
220: $dsnOpts['password'],
221: $dsnOpts['protocol'],
222: $dsnOpts['persistent'],
223: $dsnOpts['charset'],
224: $dsnOpts['phptype'],
225: $dsnOpts['socket']
226: );
227:
228: // return DSN and user/pass for connection
229: return array(
230: $this->_buildDsnString($this->_normalizeConfig($dsnOpts)),
231: $this->_config['username'],
232: $this->_config['password']);
233: }
234: }
235: