Manager.php 16.1 KB
Newer Older
lsmith's avatar
lsmith committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
<?php
/*
 *  $Id$
 *
 * 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, see
 * <http://www.phpdoctrine.com>.
 */
/**
 *
lsmith's avatar
lsmith committed
23
 * Doctrine_Manager is the base component of all doctrine based projects.
lsmith's avatar
lsmith committed
24 25 26 27 28 29 30 31 32 33
 * It opens and keeps track of all connections (database connections).
 *
 * @package     Doctrine
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
 * @category    Object Relational Mapping
 * @link        www.phpdoctrine.com
 * @since       1.0
 * @version     $Revision$
 * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
 */
lsmith's avatar
lsmith committed
34 35
class Doctrine_Manager extends Doctrine_Configurable implements Countable, IteratorAggregate
{
lsmith's avatar
lsmith committed
36 37 38
    /**
     * @var array $connections      an array containing all the opened connections
     */
39
    protected $_connections   = array();
lsmith's avatar
lsmith committed
40 41 42
    /**
     * @var array $bound            an array containing all components that have a bound connection
     */
43
    protected $_bound          = array();
lsmith's avatar
lsmith committed
44 45 46
    /**
     * @var integer $index          the incremented index
     */
47
    protected $_index      = 0;
lsmith's avatar
lsmith committed
48 49 50
    /**
     * @var integer $currIndex      the current connection index
     */
51
    protected $_currIndex  = 0;
lsmith's avatar
lsmith committed
52 53 54
    /**
     * @var string $root            root directory
     */
55
    protected $_root;
lsmith's avatar
lsmith committed
56 57 58
    /**
     * @var Doctrine_Null $null     Doctrine_Null object, used for extremely fast null value checking
     */
59
    protected $_null;
zYne's avatar
zYne committed
60 61 62
    /**
     * @var array $driverMap
     */
63 64 65 66 67
    protected $_driverMap        = array('oracle'     => 'oci8',
                                         'postgres'   => 'pgsql',
                                         'oci'        => 'oci8',
                                         'sqlite2'    => 'sqlite',
                                         'sqlite3'    => 'sqlite');
68 69 70
                                         

    protected $_integrityActionsMap = array();
lsmith's avatar
lsmith committed
71 72 73 74 75
    /**
     * constructor
     *
     * this is private constructor (use getInstance to get an instance of this class)
     */
lsmith's avatar
lsmith committed
76 77
    private function __construct()
    {
78
        $this->_root = dirname(__FILE__);
zYne's avatar
zYne committed
79
        $this->_null = new Doctrine_Null; 
lsmith's avatar
lsmith committed
80

81 82
        Doctrine_Record_Iterator::initNullObject($this->_null);
        Doctrine_Validator::initNullObject($this->_null);
zYne's avatar
zYne committed
83
        Doctrine_Object::initNullObject($this->_null);
lsmith's avatar
lsmith committed
84
    }
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
    public function addDeleteAction($componentName, $foreignComponent, $action)
    {
        $this->_integrityActions[$componentName]['onDelete'][$foreignComponent] = $action;
    }
    public function addUpdateAction($componentName, $foreignComponent, $action)
    {
        $this->_integrityActions[$componentName]['onUpdate'][$foreignComponent] = $action;
    }
    public function getDeleteActions($componentName)
    {
        if ( ! isset($this->_integrityActions[$componentName]['onDelete'])) {
            return null;
        }
        
        return $this->_integrityActions[$componentName]['onDelete'];
    }
    public function getUpdateActions($componentName)
    {
        if ( ! isset($this->_integrityActions[$componentName]['onUpdate'])) {
            return null;
        }
        
        return $this->_integrityActions[$componentName]['onUpdate'];
    }
lsmith's avatar
lsmith committed
109 110 111
    /**
     * @return Doctrine_Null
     */
lsmith's avatar
lsmith committed
112 113
    final public function getNullObject()
    {
114
        return $this->_null;
lsmith's avatar
lsmith committed
115 116 117 118 119 120 121
    }
    /**
     * setDefaultAttributes
     * sets default attributes
     *
     * @return boolean
     */
122
    public function setDefaultAttributes()
lsmith's avatar
lsmith committed
123
    {
lsmith's avatar
lsmith committed
124 125 126 127
        static $init = false;
        if ( ! $init) {
            $init = true;
            $attributes = array(
zYne's avatar
zYne committed
128 129 130
                        Doctrine::ATTR_DQL_PARSER_CACHE => null,
                        Doctrine::ATTR_DQL_CACHE        => null,
                        Doctrine::ATTR_SQL_CACHE        => null,
131
                        Doctrine::ATTR_LOAD_REFERENCES  => true,
lsmith's avatar
lsmith committed
132 133 134 135 136 137 138 139 140 141 142
                        Doctrine::ATTR_LISTENER         => new Doctrine_EventListener(),
                        Doctrine::ATTR_LOCKMODE         => 1,
                        Doctrine::ATTR_VLD              => false,
                        Doctrine::ATTR_AUTO_LENGTH_VLD  => true,
                        Doctrine::ATTR_AUTO_TYPE_VLD    => true,
                        Doctrine::ATTR_QUERY_LIMIT      => Doctrine::LIMIT_RECORDS,
                        Doctrine::ATTR_IDXNAME_FORMAT   => "%s_idx",
                        Doctrine::ATTR_SEQNAME_FORMAT   => "%s_seq",
                        Doctrine::ATTR_QUOTE_IDENTIFIER => false,
                        Doctrine::ATTR_SEQCOL_NAME      => 'id',
                        Doctrine::ATTR_PORTABILITY      => Doctrine::PORTABILITY_ALL,
zYne's avatar
zYne committed
143
                        Doctrine::ATTR_EXPORT           => Doctrine::EXPORT_NONE,
144
                        Doctrine::ATTR_DECIMAL_PLACES   => 2,
lsmith's avatar
lsmith committed
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
                        );
            foreach ($attributes as $attribute => $value) {
                $old = $this->getAttribute($attribute);
                if ($old === null) {
                    $this->setAttribute($attribute,$value);
                }
            }
            return true;
        }
        return false;
    }
    /**
     * returns the root directory of Doctrine
     *
     * @return string
     */
lsmith's avatar
lsmith committed
161 162
    final public function getRoot()
    {
163
        return $this->_root;
lsmith's avatar
lsmith committed
164 165 166 167 168 169 170 171
    }
    /**
     * getInstance
     * returns an instance of this class
     * (this class uses the singleton pattern)
     *
     * @return Doctrine_Manager
     */
lsmith's avatar
lsmith committed
172 173
    public static function getInstance()
    {
lsmith's avatar
lsmith committed
174 175 176 177 178 179 180 181
        static $instance;
        if ( ! isset($instance)) {
            $instance = new self();
        }
        return $instance;
    }
    /**
     * connection
lsmith's avatar
lsmith committed
182
     *
lsmith's avatar
lsmith committed
183 184 185
     * if the adapter parameter is set this method acts as
     * a short cut for Doctrine_Manager::getInstance()->openConnection($adapter, $name);
     *
lsmith's avatar
lsmith committed
186
     * if the adapter paramater is not set this method acts as
lsmith's avatar
lsmith committed
187 188 189 190 191 192 193
     * a short cut for Doctrine_Manager::getInstance()->getCurrentConnection()
     *
     * @param PDO|Doctrine_Adapter_Interface $adapter   database driver
     * @param string $name                              name of the connection, if empty numeric key is used
     * @throws Doctrine_Manager_Exception               if trying to bind a connection with an existing name
     * @return Doctrine_Connection
     */
lsmith's avatar
lsmith committed
194 195
    public static function connection($adapter = null, $name = null)
    {
lsmith's avatar
lsmith committed
196 197 198 199 200
        if ($adapter == null) {
            return Doctrine_Manager::getInstance()->getCurrentConnection();
        } else {
            return Doctrine_Manager::getInstance()->openConnection($adapter, $name);
        }
lsmith's avatar
lsmith committed
201
    }
lsmith's avatar
lsmith committed
202 203 204 205 206 207 208
    /**
     * openConnection
     * opens a new connection and saves it to Doctrine_Manager->connections
     *
     * @param PDO|Doctrine_Adapter_Interface $adapter   database driver
     * @param string $name                              name of the connection, if empty numeric key is used
     * @throws Doctrine_Manager_Exception               if trying to bind a connection with an existing name
zYne's avatar
zYne committed
209
     * @throws Doctrine_Manager_Exception               if trying to open connection for unknown driver
lsmith's avatar
lsmith committed
210 211
     * @return Doctrine_Connection
     */
212
    public function openConnection($adapter, $name = null, $setCurrent = true)
lsmith's avatar
lsmith committed
213
    {
lsmith's avatar
lsmith committed
214 215 216 217
        if ( ! ($adapter instanceof PDO) && ! in_array('Doctrine_Adapter_Interface', class_implements($adapter))) {
            throw new Doctrine_Manager_Exception("First argument should be an instance of PDO or implement Doctrine_Adapter_Interface");
        }

218 219 220 221
        if ($adapter instanceof Doctrine_Db) {
            $adapter->setName($name);
        }

lsmith's avatar
lsmith committed
222 223 224 225 226
        // initialize the default attributes
        $this->setDefaultAttributes();

        if ($name !== null) {
            $name = (string) $name;
227 228
            if (isset($this->_connections[$name])) {
                return $this->_connections[$name];
lsmith's avatar
lsmith committed
229 230
            }
        } else {
231 232
            $name = $this->_index;
            $this->_index++;
lsmith's avatar
lsmith committed
233
        }
zYne's avatar
zYne committed
234

zYne's avatar
zYne committed
235
        switch ($adapter->getAttribute(Doctrine::ATTR_DRIVER_NAME)) {
236
            case 'mysql':
237
                $this->_connections[$name] = new Doctrine_Connection_Mysql($this, $adapter);
238 239
                break;
            case 'sqlite':
240
                $this->_connections[$name] = new Doctrine_Connection_Sqlite($this, $adapter);
241 242
                break;
            case 'pgsql':
243
                $this->_connections[$name] = new Doctrine_Connection_Pgsql($this, $adapter);
244 245
                break;
            case 'oci':
zYne's avatar
zYne committed
246
            case 'oci8':
247
            case 'oracle':
248
                $this->_connections[$name] = new Doctrine_Connection_Oracle($this, $adapter);
249 250
                break;
            case 'mssql':
zYne's avatar
zYne committed
251
            case 'dblib':
252
                $this->_connections[$name] = new Doctrine_Connection_Mssql($this, $adapter);
253 254
                break;
            case 'firebird':
255
                $this->_connections[$name] = new Doctrine_Connection_Firebird($this, $adapter);
256 257
                break;
            case 'informix':
258
                $this->_connections[$name] = new Doctrine_Connection_Informix($this, $adapter);
259 260
                break;
            case 'mock':
261
                $this->_connections[$name] = new Doctrine_Connection_Mock($this, $adapter);
262 263
                break;
            default:
264
                throw new Doctrine_Manager_Exception('Unknown connection driver '. $adapter->getAttribute(Doctrine::ATTR_DRIVER_NAME));
lsmith's avatar
lsmith committed
265
        };
266

267
        if ($setCurrent) {
268
            $this->_currIndex = $name;
269
        }
270
        return $this->_connections[$name];
lsmith's avatar
lsmith committed
271 272 273 274 275 276 277
    }
    /**
     * getConnection
     * @param integer $index
     * @return object Doctrine_Connection
     * @throws Doctrine_Manager_Exception   if trying to get a non-existent connection
     */
lsmith's avatar
lsmith committed
278 279
    public function getConnection($name)
    {
280
        if ( ! isset($this->_connections[$name])) {
lsmith's avatar
lsmith committed
281 282
            throw new Doctrine_Manager_Exception('Unknown connection: ' . $name);
        }
zYne's avatar
zYne committed
283

284
        return $this->_connections[$name];
lsmith's avatar
lsmith committed
285
    }
286
    /**
287
     * getComponentAlias
288
     * retrieves the alias for given component name
289 290 291 292 293 294 295 296 297 298 299
     * if the alias couldn't be found, this method returns the given
     * component name
     *
     * @param string $componentName
     * @return string                   the component alias
     */
    public function getComponentAlias($componentName)
    {
        if (isset($this->componentAliases[$componentName])) {
            return $this->componentAliases[$componentName];
        }
300

301 302 303 304 305 306 307 308 309 310 311
        return $componentName;
    }
    /**
     * sets an alias for given component name
     * very useful when building a large framework with a possibility
     * to override any given class
     *
     * @param string $componentName         the name of the component
     * @param string $alias
     * @return Doctrine_Manager
     */
312
    public function setComponentAlias($componentName, $alias)
313 314
    {
        $this->componentAliases[$componentName] = $alias;
315

316 317
        return $this;
    }
zYne's avatar
zYne committed
318 319 320
    /**
     * getConnectionName
     *
zYne's avatar
zYne committed
321 322
     * @param Doctrine_Connection $conn     connection object to be searched for
     * @return string                       the name of the connection
zYne's avatar
zYne committed
323
     */
324
    public function getConnectionName(Doctrine_Connection $conn)
zYne's avatar
zYne committed
325
    {
326
        return array_search($conn, $this->_connections, true);
zYne's avatar
zYne committed
327
    }
lsmith's avatar
lsmith committed
328 329 330 331 332 333 334 335 336 337
    /**
     * bindComponent
     * binds given component to given connection
     * this means that when ever the given component uses a connection
     * it will be using the bound connection instead of the current connection
     *
     * @param string $componentName
     * @param string $connectionName
     * @return boolean
     */
lsmith's avatar
lsmith committed
338 339
    public function bindComponent($componentName, $connectionName)
    {
340
        $this->_bound[$componentName] = $connectionName;
lsmith's avatar
lsmith committed
341 342 343 344 345 346 347
    }
    /**
     * getConnectionForComponent
     *
     * @param string $componentName
     * @return Doctrine_Connection
     */
lsmith's avatar
lsmith committed
348 349
    public function getConnectionForComponent($componentName = null)
    {
350 351
        if (isset($this->_bound[$componentName])) {
            return $this->getConnection($this->_bound[$componentName]);
lsmith's avatar
lsmith committed
352 353 354
        }
        return $this->getCurrentConnection();
    }
lsmith's avatar
lsmith committed
355
    /**
lsmith's avatar
lsmith committed
356 357 358 359 360 361 362 363
     * getTable
     * this is the same as Doctrine_Connection::getTable() except
     * that it works seamlessly in multi-server/connection environment
     *
     * @see Doctrine_Connection::getTable()
     * @param string $componentName
     * @return Doctrine_Table
     */
lsmith's avatar
lsmith committed
364 365
    public function getTable($componentName)
    {
lsmith's avatar
lsmith committed
366 367 368 369 370 371 372 373
        return $this->getConnectionForComponent($componentName)->getTable($componentName);
    }
    /**
     * closes the connection
     *
     * @param Doctrine_Connection $connection
     * @return void
     */
lsmith's avatar
lsmith committed
374 375
    public function closeConnection(Doctrine_Connection $connection)
    {
lsmith's avatar
lsmith committed
376
        $connection->close();
377

378
        $key = array_search($connection, $this->_connections, true);
zYne's avatar
zYne committed
379 380

        if ($key !== false) {
381
            unset($this->_connections[$key]);
zYne's avatar
zYne committed
382
        }
383
        $this->_currIndex = key($this->_connections);
zYne's avatar
zYne committed
384

lsmith's avatar
lsmith committed
385 386 387 388 389 390 391 392
        unset($connection);
    }
    /**
     * getConnections
     * returns all opened connections
     *
     * @return array
     */
lsmith's avatar
lsmith committed
393 394
    public function getConnections()
    {
395
        return $this->_connections;
lsmith's avatar
lsmith committed
396 397 398 399 400 401 402 403 404
    }
    /**
     * setCurrentConnection
     * sets the current connection to $key
     *
     * @param mixed $key                        the connection key
     * @throws InvalidKeyException
     * @return void
     */
lsmith's avatar
lsmith committed
405 406
    public function setCurrentConnection($key)
    {
lsmith's avatar
lsmith committed
407
        $key = (string) $key;
408
        if ( ! isset($this->_connections[$key])) {
lsmith's avatar
lsmith committed
409 410
            throw new InvalidKeyException();
        }
411
        $this->_currIndex = $key;
lsmith's avatar
lsmith committed
412
    }
zYne's avatar
zYne committed
413 414 415 416 417 418 419
    /**
     * contains
     * whether or not the manager contains specified connection
     *
     * @param mixed $key                        the connection key
     * @return boolean
     */
420
    public function contains($key)
zYne's avatar
zYne committed
421
    {
422
        return isset($this->_connections[$key]);
zYne's avatar
zYne committed
423
    }
lsmith's avatar
lsmith committed
424 425 426 427 428 429
    /**
     * count
     * returns the number of opened connections
     *
     * @return integer
     */
lsmith's avatar
lsmith committed
430 431
    public function count()
    {
432
        return count($this->_connections);
lsmith's avatar
lsmith committed
433 434 435 436 437 438 439
    }
    /**
     * getIterator
     * returns an ArrayIterator that iterates through all connections
     *
     * @return ArrayIterator
     */
lsmith's avatar
lsmith committed
440 441
    public function getIterator()
    {
442
        return new ArrayIterator($this->_connections);
lsmith's avatar
lsmith committed
443 444 445 446 447 448 449 450
    }
    /**
     * getCurrentConnection
     * returns the current connection
     *
     * @throws Doctrine_Connection_Exception       if there are no open connections
     * @return Doctrine_Connection
     */
lsmith's avatar
lsmith committed
451 452
    public function getCurrentConnection()
    {
453 454
        $i = $this->_currIndex;
        if ( ! isset($this->_connections[$i])) {
lsmith's avatar
lsmith committed
455 456
            throw new Doctrine_Connection_Exception();
        }
457
        return $this->_connections[$i];
lsmith's avatar
lsmith committed
458 459 460 461 462 463 464
    }
    /**
     * __toString
     * returns a string representation of this object
     *
     * @return string
     */
lsmith's avatar
lsmith committed
465 466
    public function __toString()
    {
lsmith's avatar
lsmith committed
467 468
        $r[] = "<pre>";
        $r[] = "Doctrine_Manager";
469
        $r[] = "Connections : ".count($this->_connections);
lsmith's avatar
lsmith committed
470 471 472 473
        $r[] = "</pre>";
        return implode("\n",$r);
    }
}