root/trunk/lib/SGL/DB.php

Revision 3124, 15.2 kB (checked in by demian, 3 weeks ago)

bugfix to trunk merge -2

Line 
1 <?php
2 /* Reminder: always indent with 4 spaces (no tabs). */
3 // +---------------------------------------------------------------------------+
4 // | Copyright (c) 2006, Demian Turner                                         |
5 // | All rights reserved.                                                      |
6 // |                                                                           |
7 // | Redistribution and use in source and binary forms, with or without        |
8 // | modification, are permitted provided that the following conditions        |
9 // | are met:                                                                  |
10 // |                                                                           |
11 // | o Redistributions of source code must retain the above copyright          |
12 // |   notice, this list of conditions and the following disclaimer.           |
13 // | o Redistributions in binary form must reproduce the above copyright       |
14 // |   notice, this list of conditions and the following disclaimer in the     |
15 // |   documentation and/or other materials provided with the distribution.    |
16 // | o The names of the authors may not be used to endorse or promote          |
17 // |   products derived from this software without specific prior written      |
18 // |   permission.                                                             |
19 // |                                                                           |
20 // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       |
21 // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         |
22 // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR     |
23 // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT      |
24 // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,     |
25 // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT          |
26 // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,     |
27 // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY     |
28 // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT       |
29 // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE     |
30 // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.      |
31 // |                                                                           |
32 // +---------------------------------------------------------------------------+
33 // | Seagull 0.6                                                               |
34 // +---------------------------------------------------------------------------+
35 // | DB.php                                                                    |
36 // +---------------------------------------------------------------------------+
37 // | Authors:   Demian Turner <demian@phpkitchen.com>                          |
38 // +---------------------------------------------------------------------------+
39 // $Id: DB.php,v 1.14 2005/06/20 10:56:31 demian Exp $
40
41 define('SGL_DSN_ARRAY',                 0);
42 define('SGL_DSN_STRING',                1);
43
44 /**
45  * Class for handling DB resources.
46  *
47  * @package SGL
48  * @author  Demian Turner <demian@phpkitchen.com>
49  * @version $Revision: 1.14 $
50  */
51 class SGL_DB
52 {
53     /**
54      * Returns a singleton reference to the DB resource.
55      *
56      * example usage:
57      * $dbh = & SGL_DB::singleton();
58      * warning: in order to work correctly, DB handle
59      * singleton must be instantiated statically and
60      * by reference
61      *
62      * @access  public
63      * @static
64      * @param   string  $dsn    the datasource details if supplied: see {@link DB::parseDSN()} for format
65      * @return  mixed           reference to DB resource or false on failure to connect
66      */
67     function &singleton($dsn = null)
68     {
69         $msg = 'Cannot connect to DB, check your credentials, exiting ...';
70         $dsn = (is_null($dsn)) ? SGL_DB::getDsn(SGL_DSN_STRING) : $dsn;
71         if (empty($dsn['phptype'])) {
72             return PEAR::raiseError($msg, SGL_ERROR_DBFAILURE);
73         }
74         $c = &SGL_Config::singleton();
75         $conf = $c->getAll();
76
77         static $aInstances;
78         if (!isset($aInstances)) {
79             $aInstances = array();
80         }
81         $signature = md5($dsn);
82         if (!isset($aInstances[$signature])) {
83             $conn = DB::connect($dsn);
84             $fatal = (defined('SGL_INSTALLED')) ? PEAR_ERROR_DIE : null;
85             if (DB::isError($conn)) {
86                 if (is_file(SGL_VAR_DIR . '/INSTALL_COMPLETE.php') && defined('SGL_INSTALLED')) {
87                     $msg .= 'If you remove the file seagull/var/INSTALL_COMPLETE.php you will be'.
88                     ' able to run the setup again.';
89                 }
90                 $err = PEAR::raiseError($msg,
91                     SGL_ERROR_DBFAILURE, $fatal);
92                 return $err;
93             }
94             if (!empty($conf['db']['postConnect'])) {
95                 $conn->query($conf['db']['postConnect']);
96             }
97             $conn->setFetchMode(DB_FETCHMODE_OBJECT);
98             $aInstances[$signature] = $conn;
99         }
100         return $aInstances[$signature];
101     }
102
103     /**
104      * Returns the default dsn specified in the global config.
105      *
106      * @access  public
107      * @static
108      * @param int $type  a constant that specifies the return type, ie, array or string
109      * @return mixed     a string or array contained the data source name
110      */
111     function getDsn($type = SGL_DSN_ARRAY, $excludeDbName = false)
112     {
113         $c = &SGL_Config::singleton();
114         $conf = $c->getAll();
115         if (!count($conf)) {
116             return false;
117         }
118
119         $locator = &SGL_ServiceLocator::singleton();
120         $dbh = $locator->get('DB');
121         if ($dbh && count($dbh->dsn)) {
122             $locatorDsn = $dbh->dsn;
123             $conf['db']['user'] = $locatorDsn['username'];
124             $conf['db']['pass'] = $locatorDsn['password'];
125             $conf['db']['protocol'] = $locatorDsn['protocol'];
126             $conf['db']['socket'] = $locatorDsn['socket'];
127             $conf['db']['host'] = $locatorDsn['hostspec'];
128             $conf['db']['port'] = $locatorDsn['port'];
129             $conf['db']['name'] = $locatorDsn['database'];
130         }
131
132         //  override default mysql driver to allow for all sequence IDs to
133         //  be kept in a single table
134         $dbType = $conf['db']['type'];
135         if ($type == SGL_DSN_ARRAY) {
136             $dsn = array(
137                 'phptype'  => $dbType,
138                 'username' => $conf['db']['user'],
139                 'password' => $conf['db']['pass'],
140                 'protocol' => $conf['db']['protocol'],
141                 'socket'   => $conf['db']['socket'],
142                 'hostspec' => $conf['db']['host'],
143                 'port'     => $conf['db']['port']
144             );
145             if (!$excludeDbName) {
146                 $dsn['database'] = $conf['db']['name'];
147             }
148         } else {
149             $socket = (isset($conf['db']['protocol'])
150                         && $conf['db']['protocol'] == 'unix'
151                         && !empty($conf['db']['socket']))
152                 ? '(' . $conf['db']['socket'] . ')'
153                 : '';
154             $protocol = isset($conf['db']['protocol'])
155                 ? $conf['db']['protocol'] . $socket
156                 : '';
157             $host = empty($conf['db']['socket']) ? '+' . $conf['db']['host'] : '';
158             $port = (!empty($conf['db']['port'])
159                         && isset($conf['db']['protocol'])
160                         && ($conf['db']['protocol'] == 'tcp'))
161                 ? ':' . $conf['db']['port']
162                 : '';
163             $dsn = $dbType . '://' .
164                 $conf['db']['user'] . ':' .
165                 $conf['db']['pass'] . '@' .
166                 $protocol .
167                 $host . $port;
168             if (!$excludeDbName) {
169                 $dsn .= '/' . $conf['db']['name'];
170             }
171         }
172         return $dsn;
173     }
174
175     /**
176      * Sets the DB_DataObject DB resource to be the same as the sgl DB singleton. You can
177      * use this for sharing connections between PEAR::DataObjects and SGL_DB.
178      * This enables you to use DataObjects and SGL_DB in the same transaction.
179      *
180      * example usage:
181      * $oUser = DB_DataObject::factory($this->conf['table']['user']);
182      * SGL_DB::setConnection();
183      * $dbh->autocommit();
184      * ... do some transactional DO and SGL_DB stuff
185      * $dbh->commit();
186      *
187      * @access  public
188      * @param   string $dsn Supplied database resource name
189      * @static
190      */
191     function setConnection($dsn = null)
192     {
193         $locator = &SGL_ServiceLocator::singleton();
194         $singleton = $locator->get('DB');
195         if (!$singleton) {
196             $singleton = & SGL_DB::singleton();
197             $locator->register('DB', $singleton);
198         }
199
200         $dsn = (is_null($dsn)) ? SGL_DB::getDsn(SGL_DSN_STRING) : $dsn;
201         $dsnMd5 = md5($dsn);
202
203         unset($GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$dsnMd5]);
204         $GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$dsnMd5] = &$singleton;
205     }
206
207     /**
208      * Helper method - Rewrite the query into a "SELECT COUNT(*)" query.
209      *
210      * @param string $sql query
211      * @return string rewritten query OR false if the query can't be rewritten
212      * @access private
213      */
214     function rewriteCountQuery($sql)
215     {
216         if (preg_match('/^\s*SELECT\s+\bDISTINCT\b/is', $sql) || preg_match('/\s+GROUP\s+BY\s+/is', $sql)) {
217             return false;
218         }
219         $queryCount = preg_replace('/(?:.*)\bFROM\b\s+/Uims', 'SELECT COUNT(*) FROM ', $sql, 1);
220         list($queryCount, ) = preg_split('/\s+ORDER\s+BY\s+/is', $queryCount);
221         list($queryCount, ) = preg_split('/\bLIMIT\b/is', $queryCount);
222         return trim($queryCount);
223     }
224
225     /**
226      * @param object $db            PEAR::DB instance
227      * @param string $query         db query
228      * @param array  $pager_options PEAR::Pager options
229      * @param boolean $disabled     Disable pagination (get all results)
230      * @param int    $fetchMode     fetchmode to use
231      * @param mixed  $dbparams      array, string or numeric data passed to DB execute
232      * @return array with links and paged data
233      */
234     function getPagedData(&$db, $query, $pager_options = array(), $disabled = false,
235         $fetchMode = DB_FETCHMODE_ASSOC, $dbparams = array())
236     {
237         if (!array_key_exists('totalItems', $pager_options) || is_null($pager_options['totalItems'])) {
238             //  be smart and try to guess the total number of records
239             if ($countQuery = SGL_DB::rewriteCountQuery($query)) {
240                 $totalItems = $db->getOne($countQuery, $dbparams);
241                 if (PEAR::isError($totalItems)) {
242                     return $totalItems;
243                 }
244             } else {
245                 $res =& $db->query($query, $dbparams);
246                 if (PEAR::isError($res)) {
247                     return $res;
248                 }
249                 $totalItems = (int)$res->numRows();
250                 $res->free();
251             }
252             $pager_options['totalItems'] = $totalItems;
253         }
254
255         require_once 'Pager/Pager.php';
256         // To get Seagull URL Style working for Pager
257         $req =& SGL_Request::singleton();
258         $pager_options['currentPage'] = (array_key_exists('currentPage', $pager_options))
259             ? $pager_options['currentPage']
260             : $req->get('pageID');
261         $pager_options['append'] = isset($pager_options['append'])
262             ? $pager_options['append']
263             : false;
264         $pager_options['fileName'] = isset($pager_options['fileName'])
265             ? $pager_options['fileName']
266             : '/pageID/%d/';
267
268         // translate PEAR::Pager
269         $pager_options['altPrev'] = SGL_String::translate('altPrev');
270         $pager_options['altNext'] = SGL_String::translate('altNext');
271         $pager_options['altPage'] = SGL_String::translate('altPage');
272         $pager_options['prevImg'] = SGL_String::translate('prevImg');
273         $pager_options['nextImg'] = SGL_String::translate('nextImg');
274         $pager = Pager::factory($pager_options);
275
276         $page = array();
277         $page['totalItems'] = $pager_options['totalItems'];
278         $page['links'] = str_replace("/pageID/".$pager->getCurrentPageID()."/", "/", $pager->links);
279         $page['page_numbers'] = array(
280             'current' => $pager->getCurrentPageID(),
281             'total'   => $pager->numPages()
282         );
283         list($page['from'], $page['to']) = $pager->getOffsetByPageId();
284
285         $res = ($disabled)
286             ? $db->limitQuery($query, 0, $page['totalItems'], $dbparams)
287             : $db->limitQuery($query, $page['from']-1, $pager_options['perPage'], $dbparams);
288
289         if (PEAR::isError($res)) {
290             return $res;
291         }
292         $page['data'] = array();
293         while ($res->fetchInto($row, $fetchMode)) {
294            $page['data'][] = $row;
295         }
296         if ($disabled) {
297             $page['links'] = '';
298             $page['page_numbers'] = array(
299                 'current' => 1,
300                 'total'   => 1
301             );
302         }
303         return $page;
304     }
305 }
306
307 /**
308  * ServiceLocator.
309  *
310  * @package    SGL
311  * @author     Luis Correa d'Almeida <luis@awarez.net>
312  * @author     Andrew Hill <andrew@awarez.net>
313  */
314
315 /**
316   * A class that allows services to be globally registered, so that they
317   * can be accessed by any class that needs them. Also allows Mock Objects
318   * to be easily used as replacements for classes during testing.
319   */
320 class SGL_ServiceLocator
321 {
322     var $aServices = array();
323
324     /**
325      * A method to return a singleton handle to the service locator class.
326      */
327     function &singleton()
328     {
329         static $instance;
330         if (!$instance) {
331             $class = __CLASS__;
332             $instance = new $class();
333         }
334         return $instance;
335     }
336
337     /**
338      * A method to register a service with the service locator class.
339      *
340      * @param string $serviceName The name of the service being registered.
341      * @param mixed $oService The object (service) being registered.
342      * @return boolean Always returns true.
343      */
344     function register($serviceName, &$oService)
345     {
346         $this->aServices[$serviceName] = &$oService;
347         return true;
348     }
349
350     /**
351      * A method to remove a registered service from the service locator class.
352      *
353      * @param string $serviceName The name of the service being de-registered.
354      */
355     function remove($serviceName)
356     {
357         unset($this->aServices[$serviceName]);
358     }
359
360     /**
361      * A method to return a registered service.
362      *
363      * @param string $serviceName The name of the service required.
364      * @return mixed Either the service object requested, or false if the
365      *               requested service was not registered.
366      */
367     function &get($serviceName)
368     {
369         if (isset($this->aServices[$serviceName])) {
370             $ret = $this->aServices[$serviceName];
371         } else {
372             $ret = false;
373         }
374         return $ret;
375     }
376
377     /**
378      * A method to return a registered service.
379      *
380      * @param string $serviceName The name of the service required.
381      * @return mixed Either the service object requested, or false if the
382      *               requested service was not registered.
383      * @static
384      */
385     function &staticGet($serviceName)
386     {
387         $oServiceLocator = &SGL_ServiceLocator::singleton();
388         return $oServiceLocator->get($serviceName);
389     }
390 }
391 ?>
392
Note: See TracBrowser for help on using the browser.