646 lines
22 KiB
PHP
646 lines
22 KiB
PHP
<?php
|
||
|
||
/*
|
||
* $Id: Propel.php 601 2007-03-07 13:23:12Z hans $
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
*
|
||
* This software consists of voluntary contributions made by many individuals
|
||
* and is licensed under the LGPL. For more information please see
|
||
* <http://propel.phpdb.org>.
|
||
*/
|
||
|
||
include_once 'propel/PropelException.php';
|
||
include_once 'adapter/DBAdapter.php';
|
||
|
||
/**
|
||
* Propel's main resource pool and initialization & configuration class.
|
||
*
|
||
* This static class is used to handle Propel initialization and to maintain all of the
|
||
* open database connections and instantiated database maps.
|
||
*
|
||
* @author Hans Lellelid <hans@xmpl.rg> (Propel)
|
||
* @author Daniel Rall <dlr@finemaltcoding.com> (Torque)
|
||
* @author Magn<67>s <20><>r Torfason <magnus@handtolvur.is> (Torque)
|
||
* @author Jason van Zyl <jvanzyl@apache.org> (Torque)
|
||
* @author Rafal Krzewski <Rafal.Krzewski@e-point.pl> (Torque)
|
||
* @author Martin Poeschl <mpoeschl@marmot.at> (Torque)
|
||
* @author Henning P. Schmiedehausen <hps@intermeta.de> (Torque)
|
||
* @author Kurt Schrader <kschrader@karmalab.org> (Torque)
|
||
* @version $Revision: 601 $
|
||
* @package propel
|
||
*/
|
||
class Propel
|
||
{
|
||
|
||
/**
|
||
* A constant for <code>default</code>.
|
||
*/
|
||
const DEFAULT_NAME = "default";
|
||
|
||
/**
|
||
* A constant defining 'System is unusuable' logging level
|
||
*/
|
||
const LOG_EMERG = 0;
|
||
|
||
/**
|
||
* A constant defining 'Immediate action required' logging level
|
||
*/
|
||
const LOG_ALERT = 1;
|
||
|
||
/**
|
||
* A constant defining 'Critical conditions' logging level
|
||
*/
|
||
const LOG_CRIT = 2;
|
||
|
||
/**
|
||
* A constant defining 'Error conditions' logging level
|
||
*/
|
||
const LOG_ERR = 3;
|
||
|
||
/**
|
||
* A constant defining 'Warning conditions' logging level
|
||
*/
|
||
const LOG_WARNING = 4;
|
||
|
||
/**
|
||
* A constant defining 'Normal but significant' logging level
|
||
*/
|
||
const LOG_NOTICE = 5;
|
||
|
||
/**
|
||
* A constant defining 'Informational' logging level
|
||
*/
|
||
const LOG_INFO = 6;
|
||
|
||
/**
|
||
* A constant defining 'Debug-level messages' logging level
|
||
*/
|
||
const LOG_DEBUG = 7;
|
||
|
||
/**
|
||
* The Propel version.
|
||
*/
|
||
const VERSION = '1.2.1';
|
||
|
||
/**
|
||
* The db name that is specified as the default in the property file
|
||
*/
|
||
private static $defaultDBName;
|
||
|
||
/**
|
||
* The global cache of database maps
|
||
*/
|
||
private static $dbMaps = array();
|
||
|
||
/**
|
||
* The cache of DB adapter keys
|
||
*/
|
||
private static $adapterMap;
|
||
|
||
/**
|
||
* The logging category.
|
||
*/
|
||
private static $category;
|
||
|
||
/**
|
||
* Propel-specific configuration.
|
||
*/
|
||
private static $configuration;
|
||
|
||
/**
|
||
* flag to set to true once this class has been initialized
|
||
*/
|
||
private static $isInit = false;
|
||
|
||
/**
|
||
* @var Log
|
||
*/
|
||
private static $logger = null;
|
||
|
||
/**
|
||
* Store mapbuilder classnames for peers that have been referenced prior
|
||
* to Propel being initialized. This can happen if the OM Peer classes are
|
||
* included before the Propel::init() method has been called.
|
||
*/
|
||
private static $mapBuilders = array();
|
||
|
||
/**
|
||
* Cache of established connections (to eliminate overhead).
|
||
* @var array
|
||
*/
|
||
private static $connectionMap = array();
|
||
|
||
/**
|
||
* initialize Propel
|
||
* @return void
|
||
* @throws PropelException Any exceptions caught during processing will be
|
||
* rethrown wrapped into a PropelException.
|
||
*/
|
||
public static function initialize()
|
||
{
|
||
if (self::$configuration === null) {
|
||
throw new PropelException("Propel cannot be initialized without "
|
||
. "a valid configuration. Please check the log files "
|
||
. "for further details.");
|
||
}
|
||
|
||
self::configureLogging();
|
||
// Now that we have dealt with processing the log properties
|
||
// that may be contained in the configuration we will make the
|
||
// configuration consist only of the remaining propel-specific
|
||
// properties that are contained in the configuration. First
|
||
// look for properties that are in the "propel" namespace.
|
||
|
||
|
||
$originalConf = self::$configuration;
|
||
self::$configuration = isset(self::$configuration['propel']) ? self::$configuration['propel'] : null;
|
||
|
||
if (empty(self::$configuration)) {
|
||
// Assume the original configuration already had any
|
||
// prefixes stripped.
|
||
self::$configuration = $originalConf;
|
||
}
|
||
|
||
// reset the connection map (this should enable runtime changes of connection params)
|
||
self::$connectionMap = array();
|
||
self::initAdapters(self::$configuration);
|
||
|
||
self::$isInit = true;
|
||
|
||
// map builders may be registered w/ Propel before Propel has
|
||
// been initialized; in this case they are stored in a static
|
||
// var of this class & now can be propertly initialized.
|
||
foreach (self::$mapBuilders as $mbClass) {
|
||
BasePeer::getMapBuilder($mbClass);
|
||
}
|
||
|
||
// now that the pre-loaded map builders have been propertly initialized
|
||
// empty the array.
|
||
// any further mapBuilders will be build by the generated MapBuilder classes.
|
||
self::$mapBuilders = array();
|
||
}
|
||
|
||
/**
|
||
* Setup the adapters needed. An adapter must be defined for each database connection.
|
||
* Generally the adapter will be the same as the PEAR phpname; e.g. for MySQL, use the
|
||
* 'mysql' adapter.
|
||
* @param array $configuration the Configuration representing the properties file
|
||
* @throws PropelException Any exceptions caught during processing will be
|
||
* rethrown wrapped into a PropelException.
|
||
*/
|
||
private static function initAdapters($configuration)
|
||
{
|
||
self::$adapterMap = array();
|
||
|
||
$c = isset($configuration['datasources']) ? $configuration['datasources'] : null;
|
||
if (!empty($c)) {
|
||
try {
|
||
foreach ($c as $handle => $properties) {
|
||
if (is_array($properties) && isset($properties['adapter'])) {
|
||
$db = DBAdapter::factory($properties['adapter']);
|
||
// register the adapter for this name
|
||
self::$adapterMap[$handle] = $db;
|
||
}
|
||
}
|
||
} catch (Exception $e) {
|
||
throw new PropelException("Unable to initialize adapters.", $e);
|
||
}
|
||
} else {
|
||
self::log("There were no adapters in the configuration.", self::LOG_WARNING);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* configure propel
|
||
*
|
||
* @param string $config Path (absolute or relative to include_path) to config file.
|
||
* @return void
|
||
* @throws PropelException If configuration file cannot be opened. (E_WARNING probably will also be raised in PHP)
|
||
*/
|
||
public static function krumo()
|
||
{
|
||
krumo(self::$configuration);
|
||
}
|
||
|
||
|
||
public static function configure($configFile)
|
||
{
|
||
self::$configuration = include($configFile);
|
||
if (self::$configuration === false) {
|
||
throw new PropelException("Unable to open configuration file: " . var_export($configFile, true));
|
||
}
|
||
//include($configFile );
|
||
}
|
||
|
||
/**
|
||
* Initialization of Propel with a properties file.
|
||
*
|
||
* @param string $c The Propel configuration file path.
|
||
* @return void
|
||
* @throws PropelException Any exceptions caught during processing will be
|
||
* rethrown wrapped into a PropelException.
|
||
*/
|
||
public static function init($c)
|
||
{
|
||
self::configure($c);
|
||
self::initialize();
|
||
}
|
||
|
||
public static function initConfiguration($configuration)
|
||
{
|
||
self::$configuration = $configuration;
|
||
self::initialize();
|
||
}
|
||
|
||
/**
|
||
* Determine whether Propel has already been initialized.
|
||
*
|
||
* @return boolean True if Propel is already initialized.
|
||
*/
|
||
public static function isInit()
|
||
{
|
||
return self::$isInit;
|
||
}
|
||
|
||
/**
|
||
* Sets the configuration for Propel and all dependencies.
|
||
*
|
||
* @param array $c the Configuration
|
||
* @return void
|
||
*/
|
||
public static function setConfiguration($c)
|
||
{
|
||
self::$configuration = $c;
|
||
}
|
||
|
||
/**
|
||
* Get the configuration for this component.
|
||
*
|
||
* @return the Configuration
|
||
*/
|
||
public static function getConfiguration()
|
||
{
|
||
return self::$configuration;
|
||
}
|
||
|
||
/**
|
||
* Configure the logging for this subsystem.
|
||
* The logging system is only configured if there is a 'log'
|
||
* section in the passed-in runtime configuration.
|
||
* @return void
|
||
*/
|
||
protected static function configureLogging()
|
||
{
|
||
if (self::$logger === null) {
|
||
if (isset(self::$configuration['log']) && is_array(self::$configuration['log']) && count(self::$configuration['log'])) {
|
||
include_once 'Log.php'; // PEAR Log class
|
||
$c = self::$configuration['log'];
|
||
// array casting handles bug in PHP5b2 where the isset() checks
|
||
// below may return true if $c is not an array (e.g. is a string)
|
||
|
||
$type = isset($c['type']) ? $c['type'] : 'file';
|
||
$name = isset($c['name']) ? $c['name'] : './propel.log';
|
||
$ident = isset($c['ident']) ? $c['ident'] : 'propel';
|
||
$conf = isset($c['conf']) ? $c['conf'] : array();
|
||
$level = isset($c['level']) ? $c['level'] : PEAR_LOG_DEBUG;
|
||
|
||
self::$logger = Log::singleton($type, $name, $ident, $conf, $level);
|
||
} // if isset()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Override the configured logger.
|
||
*
|
||
* This is primarily for things like unit tests / debugging where
|
||
* you want to change the logger without altering the configuration file.
|
||
*
|
||
* You can use any logger class that implements the propel.logger.BasicLogger
|
||
* interface. This interface is based on PEAR::Log, so you can also simply pass
|
||
* a PEAR::Log object to this method.
|
||
*
|
||
* @param object $logger The new logger to use. ([PEAR] Log or BasicLogger)
|
||
* @return void
|
||
*/
|
||
public static function setLogger($logger)
|
||
{
|
||
self::$logger = $logger;
|
||
}
|
||
|
||
/**
|
||
* Returns true if a logger, for example PEAR::Log, has been configured,
|
||
* otherwise false.
|
||
*
|
||
* @return boolean True if Propel uses logging
|
||
*/
|
||
public static function hasLogger()
|
||
{
|
||
return self::$logger !== null;
|
||
}
|
||
|
||
/**
|
||
* Get the configured logger.
|
||
* @return object Configured log class ([PEAR] Log or BasicLogger).
|
||
*/
|
||
public static function logger()
|
||
{
|
||
return self::$logger;
|
||
}
|
||
|
||
/**
|
||
* Logs a message
|
||
* If a logger has been configured, the logger will be used, otherwrise the
|
||
* logging message will be discarded without any further action
|
||
*
|
||
* @param string $message The message that will be logged.
|
||
* @param string $level The logging level.
|
||
* @return boolean True if the message was logged successfully or no logger was used.
|
||
*/
|
||
public static function log($message, $level = self::LOG_DEBUG)
|
||
{
|
||
if (self::hasLogger()) {
|
||
$logger = self::logger();
|
||
switch ($level) {
|
||
case self::LOG_EMERG:
|
||
return $logger->log($message, $level);
|
||
case self::LOG_ALERT:
|
||
return $logger->alert($message);
|
||
case self::LOG_CRIT:
|
||
return $logger->crit($message);
|
||
case self::LOG_ERR:
|
||
return $logger->err($message);
|
||
case self::LOG_WARNING:
|
||
return $logger->warning($message);
|
||
case self::LOG_NOTICE:
|
||
return $logger->notice($message);
|
||
case self::LOG_INFO:
|
||
return $logger->info($message);
|
||
default:
|
||
return $logger->debug($message);
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Returns the database map information. Name relates to the name
|
||
* of the connection pool to associate with the map.
|
||
*
|
||
* The database maps are "registered" by the generated map builder classes.
|
||
*
|
||
* @param string $name The name of the database corresponding to the DatabaseMapto retrieve.
|
||
* @return DatabaseMap The named <code>DatabaseMap</code>.
|
||
* @throws PropelException - if database map is null or propel was not initialized properly.
|
||
*/
|
||
public static function getDatabaseMap($name = null)
|
||
{
|
||
if ($name === null) {
|
||
$name = self::getDefaultDB();
|
||
if ($name === null) {
|
||
throw new PropelException("DatabaseMap name was null!");
|
||
}
|
||
}
|
||
|
||
// CACHEHOOK - this would be a good place
|
||
// to add shared memory caching options (database
|
||
// maps should be a pretty safe candidate for shared mem caching)
|
||
|
||
if (isset(self::$dbMaps[$name])) {
|
||
$map = self::$dbMaps[$name];
|
||
} else {
|
||
$map = self::initDatabaseMap($name);
|
||
}
|
||
|
||
return $map;
|
||
}
|
||
|
||
/**
|
||
* Creates and initializes the mape for the named database.
|
||
*
|
||
* The database maps are "registered" by the generated map builder classes
|
||
* by calling this method and then adding the tables, etc. to teh DatabaseMap
|
||
* object returned from this method.
|
||
*
|
||
* @param string $name The name of the database to map.
|
||
* @return DatabaseMap The desired map.
|
||
* @throws PropelException Any exceptions caught during processing will be
|
||
* rethrown wrapped into a PropelException.
|
||
*/
|
||
private static function initDatabaseMap($name)
|
||
{
|
||
$map = new DatabaseMap($name);
|
||
self::$dbMaps[$name] = $map;
|
||
return $map;
|
||
}
|
||
|
||
/**
|
||
* Register a MapBuilder
|
||
*
|
||
* @param string $className the MapBuilder
|
||
*/
|
||
public static function registerMapBuilder($className)
|
||
{
|
||
self::$mapBuilders[] = $className;
|
||
}
|
||
|
||
/**
|
||
* Returns the specified property of the given database, or the empty
|
||
* string if no value is set for the property.
|
||
*
|
||
* @param string $db The name of the database whose property to get.
|
||
* @param string $prop The name of the property to get.
|
||
* @return mixed The property's value.
|
||
*/
|
||
private static function getDatabaseProperty($db, $prop)
|
||
{
|
||
return isset(self::$configuration['datasources'][$db][$prop]) ? self::$configuration['datasources'][$db][$prop] : null;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param string $name The database name.
|
||
* @return Connection A database connection
|
||
* @throws PropelException - if no conneciton params, or SQLException caught when trying to connect.
|
||
*/
|
||
public static function getConnection($name = null)
|
||
{
|
||
if ($name === null) {
|
||
$name = self::getDefaultDB();
|
||
}
|
||
$con = isset(self::$connectionMap[$name]) ? self::$connectionMap[$name] : null;
|
||
if ($con === null || $name === 'dbarray') {
|
||
$dsn = isset(self::$configuration['datasources'][$name]['connection']) ? self::$configuration['datasources'][$name]['connection'] : null;
|
||
if ($dsn === null) {
|
||
if (isset($_SESSION['PROCESS'])) {
|
||
/***********************************************************************
|
||
* @date: 27-05-08 11:48 *
|
||
* @Description: this was added for the additional database connections *
|
||
***********************************************************************/
|
||
DbConnections::loadAdditionalConnections();
|
||
//Method "DbConnections::loadAdditionalConnections()" does not work
|
||
//well in a multi-threaded environment, its restructuring is quite
|
||
//expensive and should be done in another improvement. Forcing the
|
||
//load ensures that additional connections are obtained.
|
||
if (empty(self::$configuration['datasources'][$name])) {
|
||
DbConnections::loadAdditionalConnections(true);
|
||
}
|
||
$dsn = isset(self::$configuration['datasources'][$name]['connection']) ? self::$configuration['datasources'][$name]['connection'] : null;
|
||
} else {
|
||
throw new PropelException("No connection params set for " . $name);
|
||
}
|
||
}
|
||
// if specified, use custom driver
|
||
if (isset(self::$configuration['datasources'][$name]['driver'])) {
|
||
Creole::registerDriver($dsn['phptype'], self::$configuration['datasources'][$name]['driver']);
|
||
}
|
||
|
||
try {
|
||
// set Connection Creole default mysqli
|
||
$dsn = str_replace('mysql:', 'mysqli:', $dsn);
|
||
$con = Creole::getConnection($dsn);
|
||
} catch (SQLException $e) {
|
||
throw new PropelException($e);
|
||
}
|
||
self::$connectionMap[$name] = $con;
|
||
}
|
||
return $con;
|
||
}
|
||
|
||
/**
|
||
* Returns database adapter for a specific connection pool.
|
||
*
|
||
* @param string $name A database name.
|
||
* @return DBAdapter The corresponding database adapter.
|
||
* @throws PropelException - if unable to find DBdapter for specified db.
|
||
*/
|
||
public static function getDB($name = null)
|
||
{
|
||
if ($name === null) {
|
||
$name = self::getDefaultDB();
|
||
}
|
||
if (!isset(self::$adapterMap[$name])) {
|
||
//throw new PropelException("Unable to load DBAdapter for database '" . var_export($name, true) . "' (check your runtime properties file!)");
|
||
throw new PropelException("Unable to load DBAdapter for database '' (check your runtime properties file!)");
|
||
}
|
||
return self::$adapterMap[$name];
|
||
}
|
||
|
||
/**
|
||
* Returns the name of the default database.
|
||
*
|
||
* @return string Name of the default DB
|
||
*/
|
||
public static function getDefaultDB()
|
||
{
|
||
if (self::$configuration === null) {
|
||
return self::DEFAULT_NAME;
|
||
} elseif (self::$defaultDBName === null) {
|
||
// Determine default database name.
|
||
self::$defaultDBName = isset(self::$configuration['datasources']['default']) ? self::$configuration['datasources']['default'] : self::DEFAULT_NAME;
|
||
}
|
||
return self::$defaultDBName;
|
||
}
|
||
|
||
/**
|
||
* Include once a file specified in DOT notation and reutrn unqualified clasname.
|
||
*
|
||
* Package notation is expected to be relative to a location
|
||
* on the PHP include_path. The dot-path classes are used as a way
|
||
* to represent both classname and filesystem location; there is
|
||
* an inherent assumption about filenaming. To get around these
|
||
* naming requirements you can include the class yourself
|
||
* and then just use the classname instead of dot-path.
|
||
*
|
||
* @param string $class dot-path to clas (e.g. path.to.my.ClassName).
|
||
* @return string unqualified classname
|
||
*/
|
||
public static function import($path)
|
||
{
|
||
|
||
// extract classname
|
||
if (($pos = strrpos($path, '.')) === false) {
|
||
$class = $path;
|
||
} else {
|
||
$class = substr($path, $pos + 1);
|
||
}
|
||
|
||
// check if class exists
|
||
if (class_exists($class, false)) {
|
||
return $class;
|
||
}
|
||
|
||
// turn to filesystem path
|
||
$path = strtr($path, '.', DIRECTORY_SEPARATOR) . '.php';
|
||
|
||
// include class
|
||
$ret = include_once($path);
|
||
if ($ret === false) {
|
||
throw new PropelException("Unable to import class: " . $class . " from " . $path);
|
||
}
|
||
|
||
// return qualified name
|
||
return $class;
|
||
}
|
||
|
||
/**
|
||
* Closes any associated resource handles.
|
||
*
|
||
* This method frees any database connection handles that have been
|
||
* opened by the getConnection() method.
|
||
*
|
||
* @return void
|
||
*/
|
||
public static function close()
|
||
{
|
||
$lastQuery = "";
|
||
|
||
foreach (self::$connectionMap as $cnn) {
|
||
if (get_class($cnn) != "DBArrayConnection") {
|
||
if (isset($cnn->lastQuery)) {
|
||
$existsIsConnectedMethod = method_exists($cnn, "isConnected");
|
||
$existsCloseMethod = method_exists($cnn, "close");
|
||
if ($existsIsConnectedMethod && $existsCloseMethod && $cnn->isConnected() && $cnn->lastQuery != $lastQuery) {
|
||
$cnn->close();
|
||
}
|
||
$lastQuery = $cnn->lastQuery;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @param $name string The connection name
|
||
* @return Connection A database connection
|
||
* @throws PropelException
|
||
*/
|
||
public static function getDbConnection($name)
|
||
{
|
||
if (!empty(self::$configuration['datasources'][$name]['connection'])) {
|
||
return self::getConnection($name);
|
||
}
|
||
|
||
// the connection names always should be have a underscore like: workflow_ro, rbac_rw
|
||
// on fallback, we will try found a connection named: "workflow" if "workflow_ro" does not exist.
|
||
// the name without the "_ro" part.
|
||
|
||
$defaultDbName = substr($name, 0, strrpos($name, '_'));
|
||
|
||
if (!empty(self::$configuration['datasources'][$defaultDbName]['connection'])) {
|
||
return self::getConnection($defaultDbName);
|
||
}
|
||
|
||
throw new PropelException('Error, database connection named "' . $name . '" is not defined for Propel.');
|
||
}
|
||
}
|