root/branches/1.3/runtime/classes/propel/Propel.php

Revision 989, 25.5 kB (checked in by heltem, 10 months ago)

Cleanup:

  • Replace CRLF by LF
  • Trim right spaces
  • Property svn:keywords set to Id Rev Date Author HeadURL Revision
Line 
1 <?php
2 /*
3  *  $Id$
4  *
5  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16  *
17  * This software consists of voluntary contributions made by many individuals
18  * and is licensed under the LGPL. For more information please see
19  * <http://propel.phpdb.org>.
20  */
21
22 require 'propel/PropelException.php';
23 require 'propel/adapter/DBAdapter.php';
24 require 'propel/util/PropelPDO.php';
25
26 /**
27  * Propel's main resource pool and initialization & configuration class.
28  *
29  * This static class is used to handle Propel initialization and to maintain all of the
30  * open database connections and instantiated database maps.
31  *
32  * @author     Hans Lellelid <hans@xmpl.rg> (Propel)
33  * @author     Daniel Rall <dlr@finemaltcoding.com> (Torque)
34  * @author     Magnús Þór Torfason <magnus@handtolvur.is> (Torque)
35  * @author     Jason van Zyl <jvanzyl@apache.org> (Torque)
36  * @author     Rafal Krzewski <Rafal.Krzewski@e-point.pl> (Torque)
37  * @author     Martin Poeschl <mpoeschl@marmot.at> (Torque)
38  * @author     Henning P. Schmiedehausen <hps@intermeta.de> (Torque)
39  * @author     Kurt Schrader <kschrader@karmalab.org> (Torque)
40  * @version    $Revision$
41  * @package    propel
42  */
43 class Propel
44 {
45     /**
46      * A constant for <code>default</code>.
47      */
48     const DEFAULT_NAME = "default";
49
50     /**
51      * A constant defining 'System is unusuable' logging level
52      */
53     const LOG_EMERG = 0;
54
55     /**
56      * A constant defining 'Immediate action required' logging level
57      */
58     const LOG_ALERT = 1;
59
60     /**
61      * A constant defining 'Critical conditions' logging level
62      */
63     const LOG_CRIT = 2;
64
65     /**
66      * A constant defining 'Error conditions' logging level
67      */
68     const LOG_ERR = 3;
69
70     /**
71      * A constant defining 'Warning conditions' logging level
72      */
73     const LOG_WARNING = 4;
74
75     /**
76      * A constant defining 'Normal but significant' logging level
77      */
78     const LOG_NOTICE = 5;
79
80     /**
81      * A constant defining 'Informational' logging level
82      */
83     const LOG_INFO = 6;
84
85     /**
86      * A constant defining 'Debug-level messages' logging level
87      */
88     const LOG_DEBUG = 7;
89
90     /**
91      * The Propel version.
92      */
93     const VERSION = '1.3.0-dev';
94
95     /**
96      * The class name for a PDO object.
97      */
98     const CLASS_PDO = 'PDO';
99
100     /**
101      * The class name for a PropelPDO object.
102      */
103     const CLASS_PROPEL_PDO = 'PropelPDO';
104
105     /**
106      * The class name for a DebugPDO object.
107      */
108     const CLASS_DEBUG_PDO = 'DebugPDO';
109
110     /**
111      * Constant used to request a READ connection (applies to replication).
112      */
113     const CONNECTION_READ = 'read';
114
115     /**
116      * Constant used to request a WRITE connection (applies to replication).
117      */
118     const CONNECTION_WRITE = 'write';
119
120     /**
121      * @var        string The db name that is specified as the default in the property file
122      */
123     private static $defaultDBName;
124
125     /**
126      * @var        array The global cache of database maps
127      */
128     private static $dbMaps = array();
129
130     /**
131      * @var        array The cache of DB adapter keys
132      */
133     private static $adapterMap = array();
134
135     /**
136      * @var        array Cache of established connections (to eliminate overhead).
137      */
138     private static $connectionMap = array();
139
140     /**
141      * @var        array Propel-specific configuration.
142      */
143     private static $configuration;
144
145     /**
146      * @var        bool flag to set to true once this class has been initialized
147      */
148     private static $isInit = false;
149
150     /**
151      * @var        Log optional logger
152      */
153     private static $logger = null;
154
155     /**
156      * @var        string The name of the database mapper class
157      */
158     private static $databaseMapClass = 'DatabaseMap';
159
160     /**
161      * @var        bool Whether the object instance pooling is enabled
162      */
163     private static $instancePoolingEnabled = true;
164
165     /**
166      * @var        bool For replication, whether to force the use of master connection.
167      */
168     private static $forceMasterConnection = false;
169
170     /**
171      * @var        array A map of class names and their file paths for autoloading
172      */
173     private static $autoloadMap = array(
174         'PropelException' => 'propel/PropelException.php',
175
176         'DBAdapter' => 'propel/adapter/DBAdapter.php',
177         'DBMSSQL' => 'propel/adapter/DBMSSQL.php',
178         'DBMySQL' => 'propel/adapter/DBMySQL.php',
179         'DBMySQLi' => 'propel/adapter/DBMySQLi.php',
180         'DBNone' => 'propel/adapter/DBNone.php',
181         'DBOracle' => 'propel/adapter/DBOracle.php',
182         'DBPostgres' => 'propel/adapter/DBPostgres.php',
183         'DBSQLite' => 'propel/adapter/DBSQLite.php',
184         'DBSybase' => 'propel/adapter/DBSybase.php',
185
186         'BasicLogger' => 'propel/logger/BasicLogger.php',
187         'MojaviLogAdapter' => 'propel/logger/MojaviLogAdapter.php',
188
189         'ColumnMap' => 'propel/map/ColumnMap.php',
190         'DatabaseMap' => 'propel/map/DatabaseMap.php',
191         'MapBuilder' => 'propel/map/MapBuilder.php',
192         'TableMap' => 'propel/map/TableMap.php',
193         'ValidatorMap' => 'propel/map/ValidatorMap.php',
194
195         'BaseObject' => 'propel/om/BaseObject.php',
196         'NodeObject' => 'propel/om/NodeObject.php',
197         'Persistent' => 'propel/om/Persistent.php',
198         'PreOrderNodeIterator' => 'propel/om/PreOrderNodeIterator.php',
199         'NestedSetPreOrderNodeIterator' => 'propel/om/NestedSetPreOrderNodeIterator.php',
200         'NestedSetRecursiveIterator' => 'propel/om/NestedSetRecursiveIterator.php',
201
202         'BasePeer' => 'propel/util/BasePeer.php',
203         'NodePeer' => 'propel/util/NodePeer.php',
204         'Criteria' => 'propel/util/Criteria.php',
205         'PeerInfo' => 'propel/util/PeerInfo.php',
206         'PropelColumnTypes' => 'propel/util/PropelColumnTypes.php',
207         'PropelPDO' => 'propel/util/PropelPDO.php',
208         'PropelPager' => 'propel/util/PropelPager.php',
209         'PropelDateTime' => 'propel/util/PropelDateTime.php',
210         'DebugPDO' => 'propel/util/DebugPDO.php',
211         'DebugPDOStatement' => 'propel/util/DebugPDOStatement.php',
212
213         'BasicValidator' => 'propel/validator/BasicValidator.php',
214         'MatchValidator' => 'propel/validator/MatchValidator.php',
215         'MaxLengthValidator' => 'propel/validator/MaxLengthValidator.php',
216         'MaxValueValidator' => 'propel/validator/MaxValueValidator.php',
217         'MinLengthValidator' => 'propel/validator/MinLengthValidator.php',
218         'MinValueValidator' => 'propel/validator/MinValueValidator.php',
219         'NotMatchValidator' => 'propel/validator/NotMatchValidator.php',
220         'RequiredValidator' => 'propel/validator/RequiredValidator.php',
221         'UniqueValidator' => 'propel/validator/UniqueValidator.php',
222         'ValidValuesValidator' => 'propel/validator/ValidValuesValidator.php',
223         'ValidationFailed' => 'propel/validator/ValidationFailed.php',
224     );
225
226     /**
227      * Initializes Propel
228      *
229      * @throws     PropelException Any exceptions caught during processing will be
230      *                             rethrown wrapped into a PropelException.
231      */
232     public static function initialize()
233     {
234         if (self::$configuration === null) {
235             throw new PropelException("Propel cannot be initialized without "
236             . "a valid configuration. Please check the log files "
237             . "for further details.");
238         }
239
240         self::configureLogging();
241
242         // Support having the configuration stored within a 'propel' sub-section or at the top-level
243         if (isset(self::$configuration['propel']) && is_array(self::$configuration['propel'])) {
244             self::$configuration = self::$configuration['propel'];
245         }
246
247         // reset the connection map (this should enable runtime changes of connection params)
248         self::$connectionMap = array();
249
250         foreach (self::$configuration['datasources'] as $key => $datasource) {
251             if ($key != 'default' && isset($datasource['classes'])) {
252                 // merge the classes to the autoload map
253                 self::$autoloadMap = array_merge($datasource['classes'], self::$autoloadMap);
254             }
255         }
256
257         self::$isInit = true;
258     }
259
260     /**
261      * Configure Propel using an INI or PHP (array) config file.
262      *
263      * @param      string Path (absolute or relative to include_path) to config file.
264      *
265      * @throws     PropelException If configuration file cannot be opened.
266      *                             (E_WARNING probably will also be raised by PHP)
267      */
268     public static function configure($configFile)
269     {
270         self::$configuration = include($configFile);
271         if (self::$configuration === false) {
272             throw new PropelException("Unable to open configuration file: " . var_export($configFile, true));
273         }
274     }
275
276     /**
277      * Configure the logging system, if config is specified in the runtime configuration.
278      */
279     protected static function configureLogging()
280     {
281         if (self::$logger === null) {
282             if (isset(self::$configuration['log']) && is_array(self::$configuration['log']) && count(self::$configuration['log'])) {
283                 include_once 'Log.php'; // PEAR Log class
284                 $c = self::$configuration['log'];
285                 $type = isset($c['type']) ? $c['type'] : 'file';
286                 $name = isset($c['name']) ? $c['name'] : './propel.log';
287                 $ident = isset($c['ident']) ? $c['ident'] : 'propel';
288                 $conf = isset($c['conf']) ? $c['conf'] : array();
289                 $level = isset($c['level']) ? $c['level'] : PEAR_LOG_DEBUG;
290                 self::$logger = Log::singleton($type, $name, $ident, $conf, $level);
291             } // if isset()
292         }
293     }
294
295     /**
296      * Initialization of Propel with an INI or PHP (array) configuration file.
297      *
298      * @param      string $c The Propel configuration file path.
299      *
300      * @throws     PropelException Any exceptions caught during processing will be
301      *                             rethrown wrapped into a PropelException.
302      */
303     public static function init($c)
304     {
305         self::configure($c);
306         self::initialize();
307     }
308
309     /**
310      * Determine whether Propel has already been initialized.
311      *
312      * @return     bool True if Propel is already initialized.
313      */
314     public static function isInit()
315     {
316         return self::$isInit;
317     }
318
319     /**
320      * Sets the configuration for Propel and all dependencies.
321      *
322      * @param      array The Configuration
323      */
324     public static function setConfiguration($c)
325     {
326         self::$configuration = $c;
327     }
328
329     /**
330      * Get the configuration for this component.
331      *
332      * @return     array The Configuration
333      */
334     public static function getConfiguration()
335     {
336         return self::$configuration;
337     }
338
339     /**
340      * Override the configured logger.
341      *
342      * This is primarily for things like unit tests / debugging where
343      * you want to change the logger without altering the configuration file.
344      *
345      * You can use any logger class that implements the propel.logger.BasicLogger
346      * interface.  This interface is based on PEAR::Log, so you can also simply pass
347      * a PEAR::Log object to this method.
348      *
349      * @param      object The new logger to use. ([PEAR] Log or BasicLogger)
350      */
351     public static function setLogger($logger)
352     {
353         self::$logger = $logger;
354     }
355
356     /**
357      * Returns true if a logger, for example PEAR::Log, has been configured,
358      * otherwise false.
359      *
360      * @return     bool True if Propel uses logging
361      */
362     public static function hasLogger()
363     {
364         return (self::$logger !== null);
365     }
366
367     /**
368      * Get the configured logger.
369      *
370      * @return     object Configured log class ([PEAR] Log or BasicLogger).
371      */
372     public static function logger()
373     {
374         return self::$logger;
375     }
376
377     /**
378      * Logs a message
379      * If a logger has been configured, the logger will be used, otherwrise the
380      * logging message will be discarded without any further action
381      *
382      * @param      string The message that will be logged.
383      * @param      string The logging level.
384      *
385      * @return     bool True if the message was logged successfully or no logger was used.
386      */
387     public static function log($message, $level = self::LOG_DEBUG)
388     {
389         if (self::hasLogger()) {
390             $logger = self::logger();
391             switch ($level) {
392                 case self::LOG_EMERG:
393                     return $logger->log($message, $level);
394                 case self::LOG_ALERT:
395                     return $logger->alert($message);
396                 case self::LOG_CRIT:
397                     return $logger->crit($message);
398                 case self::LOG_ERR:
399                     return $logger->err($message);
400                 case self::LOG_WARNING:
401                     return $logger->warning($message);
402                 case self::LOG_NOTICE:
403                     return $logger->notice($message);
404                 case self::LOG_INFO:
405                     return $logger->info($message);
406                 default:
407                     return $logger->debug($message);
408             }
409         }
410         return true;
411     }
412
413     /**
414      * Returns the database map information. Name relates to the name
415      * of the connection pool to associate with the map.
416      *
417      * The database maps are "registered" by the generated map builder classes.
418      *
419      * @param      string The name of the database corresponding to the DatabaseMap to retrieve.
420      *
421      * @return     DatabaseMap The named <code>DatabaseMap</code>.
422      *
423      * @throws     PropelException - if database map is null or propel was not initialized properly.
424      */
425     public static function getDatabaseMap($name = null)
426     {
427         if ($name === null) {
428             $name = self::getDefaultDB();
429             if ($name === null) {
430                 throw new PropelException("DatabaseMap name was null!");
431             }
432         }
433
434         if (!isset(self::$dbMaps[$name])) {
435             $clazz = self::$databaseMapClass;
436             self::$dbMaps[$name] = new $clazz($name);
437         }
438
439         return self::$dbMaps[$name];
440     }
441
442     /**
443      * Sets the database map object to use for specified datasource.
444      *
445      * @param      string $name The datasource name.
446      * @param      DatabaseMap $map The database map object to use for specified datasource.
447      */
448     public static function setDatabaseMap($name, DatabaseMap $map)
449     {
450         if ($name === null) {
451             $name = self::getDefaultDB();
452         }
453         self::$dbMaps[$name] = $map;
454     }
455
456     /**
457      * For replication, set whether to always force the use of a master connection.
458      *
459      * @param      boolean $bit True or False
460      */
461     public static function setForceMasterConnection($bit)
462     {
463         self::$forceMasterConnection = (bool) $bit;
464     }
465
466     /**
467      * For replication, whether to always force the use of a master connection.
468      *
469      * @return     boolean
470      */
471     public static function getForceMasterConnection()
472     {
473         return self::$forceMasterConnection;
474     }
475
476     /**
477      * Sets a Connection for specified datasource name.
478      *
479      * @param      string $name The datasource name for the connection being set.
480      * @param      PropelPDO $con The PDO connection.
481      * @param      string $mode Whether this is a READ or WRITE connection (Propel::CONNECTION_READ, Propel::CONNECTION_WRITE)
482      */
483     public static function setConnection($name, PropelPDO $con, $mode = Propel::CONNECTION_WRITE)
484     {
485         if ($name === null) {
486             $name = self::getDefaultDB();
487         }
488         if ($mode == Propel::CONNECTION_READ) {
489             self::$connectionMap[$name]['slave'] = $con;
490         } else {
491             self::$connectionMap[$name]['master'] = $con;
492         }
493     }
494
495     /**
496      * Gets an already-opened PDO connection or opens a new one for passed-in db name.
497      *
498      * @param      string $name The datasource name that is used to look up the DSN from the runtime configuation file.
499      * @param      string $mode The connection mode (this applies to replication systems).
500      *
501      * @return     PDO A database connection
502      *
503      * @throws     PropelException - if connection cannot be configured or initialized.
504      */
505     public static function getConnection($name = null, $mode = Propel::CONNECTION_WRITE)
506     {
507         if ($name === null) {
508             $name = self::getDefaultDB();
509         }
510
511         // IF a WRITE-mode connection was requested
512         // or Propel is configured to always use the master connection
513         // or the slave for this connection has already been set to FALSE (indicating no slave)
514         // THEN return the master connection.
515         if ($mode != Propel::CONNECTION_READ || self::$forceMasterConnection || (isset(self::$connectionMap[$name]['slave']) && self::$connectionMap[$name]['slave'] === false)) {
516             if (!isset(self::$connectionMap[$name]['master'])) {
517                 // load connection parameter for master connection
518                 $conparams = isset(self::$configuration['datasources'][$name]['connection']) ? self::$configuration['datasources'][$name]['connection'] : null;
519                 if (empty($conparams)) {
520                     throw new PropelException('No connection information in your runtime configuration file for datasource ['.$name.']');
521                 }
522                 // initialize master connection
523                 $con = Propel::initConnection($conparams, $name);
524                 self::$connectionMap[$name]['master'] = $con;
525             }
526
527             return self::$connectionMap[$name]['master'];
528
529         } else {
530
531             if (!isset(self::$connectionMap[$name]['slave'])) {
532
533                 // we've already ensured that the configuration exists, in previous if-statement
534                 $slaveconfigs = isset(self::$configuration['datasources'][$name]['slaves']) ? self::$configuration['datasources'][$name]['slaves'] : null;
535
536                 if (empty($slaveconfigs)) { // no slaves configured for this datasource
537                     self::$connectionMap[$name]['slave'] = false;
538                     return self::getConnection($name, Propel::CONNECTION_WRITE); // Recurse to get the WRITE connection
539                 } else { // Initialize a new slave
540                     if (isset($slaveconfigs['connection']['dsn'])) { // only one slave connection configured
541                         $conparams = $slaveconfigs['connection'];
542                     } else {
543                         $randkey = array_rand($slaveconfigs['connection']);
544                         $conparams = $slaveconfigs['connection'][$randkey];
545                         if (empty($conparams)) {
546                             throw new PropelException('No connection information in your runtime configuration file for SLAVE ['.$randkey.'] to datasource ['.$name.']');
547                         }
548                     }
549
550                     // initialize master connection
551                     $con = Propel::initConnection($conparams, $name);
552                     self::$connectionMap[$name]['slave'] = $con;
553                 }
554
555             } // if datasource slave not set
556
557             return self::$connectionMap[$name]['slave'];
558
559         } // if mode == CONNECTION_WRITE
560
561     } // getConnection()
562
563     /**
564      * Opens a new PDO connection for passed-in db name.
565      *
566      * @param      array $conparams Connection paramters.
567      * @param      string $name Datasource name.
568      * @param      string $defaultClass The PDO subclass to instantiate if there is no explicit classname
569      *                                     specified in the connection params (default is Propel::CLASS_PROPEL_PDO)
570      *
571      * @return     PDO A database connection of the given class (PDO, PropelPDO, SlavePDO or user-defined)
572      *
573      * @throws     PropelException - if lower-level exception caught when trying to connect.
574      */
575     public static function initConnection($conparams, $name, $defaultClass = Propel::CLASS_PROPEL_PDO)
576     {
577
578         $dsn = $conparams['dsn'];
579         if ($dsn