2006-08-22 22:03:16 +04:00
|
|
|
<?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>.
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Doctrine_Connection
|
|
|
|
*
|
2006-11-14 21:21:36 +03:00
|
|
|
* @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>
|
2006-11-15 18:19:55 +03:00
|
|
|
* @author Lukas Smith <smith@pooteeweet.org> (MDB2 library)
|
2006-11-14 21:21:36 +03:00
|
|
|
*/
|
2006-08-22 22:03:16 +04:00
|
|
|
abstract class Doctrine_Connection extends Doctrine_Configurable implements Countable, IteratorAggregate {
|
|
|
|
/**
|
2006-09-20 13:46:52 +04:00
|
|
|
* @var $dbh the database handler
|
2006-08-22 22:03:16 +04:00
|
|
|
*/
|
2006-12-02 17:40:47 +03:00
|
|
|
protected $dbh;
|
2006-09-20 13:46:52 +04:00
|
|
|
/**
|
|
|
|
* @var array $tables an array containing all the initialized Doctrine_Table objects
|
|
|
|
* keys representing Doctrine_Table component names and values as Doctrine_Table objects
|
2006-08-22 22:03:16 +04:00
|
|
|
*/
|
|
|
|
protected $tables = array();
|
2006-10-31 02:27:26 +03:00
|
|
|
/**
|
|
|
|
* @var string $driverName the name of this connection driver
|
|
|
|
*/
|
|
|
|
protected $driverName;
|
|
|
|
/**
|
|
|
|
* @var array $supported an array containing all features this driver supports,
|
|
|
|
* keys representing feature names and values as
|
|
|
|
* one of the following (true, false, 'emulated')
|
|
|
|
*/
|
|
|
|
protected $supported = array();
|
2006-09-26 21:20:22 +04:00
|
|
|
/**
|
2006-11-16 16:19:45 +03:00
|
|
|
* @var array $modules an array containing all modules
|
|
|
|
* transaction Doctrine_Transaction driver, handles savepoint and transaction isolation abstraction
|
|
|
|
*
|
|
|
|
* expression Doctrine_Expression driver, handles expression abstraction
|
|
|
|
*
|
|
|
|
* dataDict Doctrine_DataDict driver, handles datatype abstraction
|
|
|
|
*
|
|
|
|
* export Doctrine_Export driver, handles db structure modification abstraction (contains
|
|
|
|
* methods such as alterTable, createConstraint etc.)
|
|
|
|
*
|
|
|
|
* @see Doctrine_Connection::__get()
|
|
|
|
* @see Doctrine_DataDict
|
|
|
|
* @see Doctrine_Expression
|
|
|
|
* @see Doctrine_Export
|
|
|
|
* @see Doctrine_Transaction
|
2006-09-26 21:20:22 +04:00
|
|
|
*/
|
2006-11-16 16:19:45 +03:00
|
|
|
private $modules = array('transaction' => false,
|
|
|
|
'expression' => false,
|
|
|
|
'dataDict' => false,
|
|
|
|
'export' => false,
|
|
|
|
'unitOfWork' => false,
|
2006-11-16 15:46:23 +03:00
|
|
|
);
|
2006-11-28 01:22:31 +03:00
|
|
|
/**
|
|
|
|
* @var array $properties an array of connection properties
|
|
|
|
*/
|
2006-12-02 17:40:47 +03:00
|
|
|
protected $properties = array('sql_comments' => array(array('start' => '--', 'end' => "\n", 'escape' => false),
|
|
|
|
array('start' => '/*', 'end' => '*/', 'escape' => false)
|
|
|
|
),
|
|
|
|
'identifier_quoting' => array('start' => '"',
|
|
|
|
'end' => '"',
|
|
|
|
'escape' => '"'
|
|
|
|
),
|
|
|
|
'string_quoting' => array('start' => "'",
|
|
|
|
'end' => "'",
|
|
|
|
'escape' => false,
|
|
|
|
'escape_pattern' => false
|
|
|
|
),
|
|
|
|
'wildcards' => array('%', '_')
|
|
|
|
);
|
2006-11-16 15:46:23 +03:00
|
|
|
/**
|
|
|
|
* @var array $availibleDrivers an array containing all availible drivers
|
|
|
|
*/
|
2006-10-31 02:27:26 +03:00
|
|
|
private static $availibleDrivers = array(
|
|
|
|
'Mysql',
|
|
|
|
'Pgsql',
|
|
|
|
'Oracle',
|
|
|
|
'Informix',
|
|
|
|
'Mssql',
|
|
|
|
'Sqlite',
|
|
|
|
'Firebird'
|
2006-10-31 02:00:09 +03:00
|
|
|
);
|
|
|
|
|
2006-08-22 22:03:16 +04:00
|
|
|
/**
|
|
|
|
* the constructor
|
|
|
|
*
|
2006-11-08 13:18:15 +03:00
|
|
|
* @param Doctrine_Manager $manager the manager object
|
|
|
|
* @param PDO|Doctrine_Adapter_Interface $adapter database driver
|
2006-08-22 22:03:16 +04:00
|
|
|
*/
|
2006-11-08 13:18:15 +03:00
|
|
|
public function __construct(Doctrine_Manager $manager, $adapter) {
|
|
|
|
if( ! ($adapter instanceof PDO) && ! in_array('Doctrine_Adapter_Interface', class_implements($adapter)))
|
|
|
|
throw new Doctrine_Connection_Exception("First argument should be an instance of PDO or implement Doctrine_Adapter_Interface");
|
|
|
|
|
|
|
|
$this->dbh = $adapter;
|
2006-09-20 13:35:29 +04:00
|
|
|
|
2006-11-30 00:09:02 +03:00
|
|
|
//$this->modules['transaction'] = new Doctrine_Connection_Transaction($this);
|
2006-11-16 16:19:45 +03:00
|
|
|
$this->modules['unitOfWork'] = new Doctrine_Connection_UnitOfWork($this);
|
2006-08-22 22:03:16 +04:00
|
|
|
|
|
|
|
$this->setParent($manager);
|
|
|
|
|
|
|
|
$this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL);
|
|
|
|
$this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
|
|
|
|
|
|
$this->getAttribute(Doctrine::ATTR_LISTENER)->onOpen($this);
|
|
|
|
}
|
2006-10-31 02:27:26 +03:00
|
|
|
/**
|
|
|
|
* getName
|
2006-11-16 15:46:23 +03:00
|
|
|
* returns the name of this driver
|
2006-10-31 02:27:26 +03:00
|
|
|
*
|
2006-11-16 15:46:23 +03:00
|
|
|
* @return string the name of this driver
|
2006-10-31 02:27:26 +03:00
|
|
|
*/
|
|
|
|
public function getName() {
|
|
|
|
return $this->driverName;
|
|
|
|
}
|
2006-11-16 15:46:23 +03:00
|
|
|
/**
|
|
|
|
* __get
|
|
|
|
* lazy loads given module and returns it
|
|
|
|
*
|
2006-11-16 16:19:45 +03:00
|
|
|
* @see Doctrine_DataDict
|
|
|
|
* @see Doctrine_Expression
|
|
|
|
* @see Doctrine_Export
|
|
|
|
* @see Doctrine_Transaction
|
2006-11-17 00:38:59 +03:00
|
|
|
* @see Doctrine_Connection::$modules all availible modules
|
2006-11-16 15:46:23 +03:00
|
|
|
* @param string $name the name of the module to get
|
2006-11-16 16:19:45 +03:00
|
|
|
* @throws Doctrine_Connection_Exception if trying to get an unknown module
|
2006-11-16 15:46:23 +03:00
|
|
|
* @return Doctrine_Connection_Module connection module
|
|
|
|
*/
|
|
|
|
public function __get($name) {
|
|
|
|
if( ! isset($this->modules[$name]))
|
|
|
|
throw new Doctrine_Connection_Exception('Unknown module ' . $name);
|
2006-11-16 16:19:45 +03:00
|
|
|
|
2006-11-16 15:46:23 +03:00
|
|
|
if($this->modules[$name] === false) {
|
|
|
|
switch($name) {
|
2006-11-16 16:19:45 +03:00
|
|
|
case 'unitOfWork':
|
2006-11-16 15:46:23 +03:00
|
|
|
$this->modules[$name] = new Doctrine_Connection_UnitOfWork($this);
|
|
|
|
break;
|
|
|
|
default:
|
2006-11-16 16:19:45 +03:00
|
|
|
$class = 'Doctrine_' . ucwords($name) . '_' . $this->getName();
|
2006-11-16 15:46:23 +03:00
|
|
|
$this->modules[$name] = new $class($this);
|
|
|
|
}
|
2006-11-28 01:22:31 +03:00
|
|
|
}
|
|
|
|
if(isset($this->properties[$name]))
|
|
|
|
return $this->properties[$name];
|
|
|
|
|
2006-10-31 02:27:26 +03:00
|
|
|
|
2006-11-16 15:46:23 +03:00
|
|
|
return $this->modules[$name];
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Quotes pattern (% and _) characters in a string)
|
|
|
|
*
|
|
|
|
* EXPERIMENTAL
|
|
|
|
*
|
|
|
|
* WARNING: this function is experimental and may change signature at
|
|
|
|
* any time until labelled as non-experimental
|
|
|
|
*
|
|
|
|
* @param string the input string to quote
|
|
|
|
*
|
|
|
|
* @return string quoted string
|
|
|
|
*/
|
|
|
|
public function escapePattern($text) {
|
|
|
|
if ($this->string_quoting['escape_pattern']) {
|
|
|
|
$text = str_replace($this->string_quoting['escape_pattern'], $this->string_quoting['escape_pattern'] . $this->string_quoting['escape_pattern'], $text);
|
|
|
|
foreach ($this->wildcards as $wildcard) {
|
|
|
|
$text = str_replace($wildcard, $this->string_quoting['escape_pattern'] . $wildcard, $text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $text;
|
|
|
|
}
|
2006-10-26 19:32:09 +04:00
|
|
|
/**
|
2006-11-16 15:46:23 +03:00
|
|
|
* Quote a string so it can be safely used as a table or column name
|
|
|
|
*
|
|
|
|
* Delimiting style depends on which database driver is being used.
|
|
|
|
*
|
|
|
|
* NOTE: just because you CAN use delimited identifiers doesn't mean
|
|
|
|
* you SHOULD use them. In general, they end up causing way more
|
|
|
|
* problems than they solve.
|
|
|
|
*
|
|
|
|
* Portability is broken by using the following characters inside
|
|
|
|
* delimited identifiers:
|
|
|
|
* + backtick (<kbd>`</kbd>) -- due to MySQL
|
|
|
|
* + double quote (<kbd>"</kbd>) -- due to Oracle
|
|
|
|
* + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
|
|
|
|
*
|
|
|
|
* Delimited identifiers are known to generally work correctly under
|
|
|
|
* the following drivers:
|
|
|
|
* + mssql
|
|
|
|
* + mysql
|
|
|
|
* + mysqli
|
|
|
|
* + oci8
|
|
|
|
* + pgsql
|
|
|
|
* + sqlite
|
2006-10-26 19:32:09 +04:00
|
|
|
*
|
2006-11-16 15:46:23 +03:00
|
|
|
* InterBase doesn't seem to be able to use delimited identifiers
|
|
|
|
* via PHP 4. They work fine under PHP 5.
|
|
|
|
*
|
|
|
|
* @param string $str identifier name to be quoted
|
|
|
|
* @param bool $checkOption check the 'quote_identifier' option
|
|
|
|
*
|
|
|
|
* @return string quoted identifier string
|
2006-10-26 19:32:09 +04:00
|
|
|
*/
|
2006-11-16 15:46:23 +03:00
|
|
|
public function quoteIdentifier($str, $checkOption = true) {
|
2006-11-24 01:54:10 +03:00
|
|
|
if ($checkOption && ! $this->getAttribute(Doctrine::ATTR_QUOTE_IDENTIFIER)) {
|
2006-11-16 15:46:23 +03:00
|
|
|
return $str;
|
2006-12-28 00:35:32 +03:00
|
|
|
}
|
|
|
|
$str = str_replace($this->properties['identifier_quoting']['end'],
|
2006-11-28 01:22:31 +03:00
|
|
|
$this->properties['identifier_quoting']['escape'] .
|
|
|
|
$this->properties['identifier_quoting']['end'], $str);
|
|
|
|
|
|
|
|
return $this->properties['identifier_quoting']['start']
|
|
|
|
. $str . $this->properties['identifier_quoting']['end'];
|
2006-10-26 19:32:09 +04:00
|
|
|
}
|
2006-08-22 22:03:16 +04:00
|
|
|
/**
|
|
|
|
* returns the manager that created this connection
|
|
|
|
*
|
|
|
|
* @return Doctrine_Manager
|
|
|
|
*/
|
|
|
|
public function getManager() {
|
|
|
|
return $this->getParent();
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* returns the database handler of which this connection uses
|
|
|
|
*
|
2006-09-26 01:08:02 +04:00
|
|
|
* @return PDO the database handler
|
2006-08-22 22:03:16 +04:00
|
|
|
*/
|
2006-11-08 13:18:15 +03:00
|
|
|
public function getDbh() {
|
2006-08-22 22:03:16 +04:00
|
|
|
return $this->dbh;
|
|
|
|
}
|
2006-10-31 02:00:09 +03:00
|
|
|
/**
|
|
|
|
* converts given driver name
|
|
|
|
*
|
|
|
|
* @param
|
|
|
|
*/
|
|
|
|
public function driverName($name) {
|
|
|
|
}
|
2006-11-01 18:07:04 +03:00
|
|
|
/**
|
|
|
|
* supports
|
|
|
|
*
|
|
|
|
* @param string $feature the name of the feature
|
|
|
|
* @return boolean whether or not this drivers supports given feature
|
|
|
|
*/
|
|
|
|
public function supports($feature) {
|
|
|
|
return (isset($this->supported[$feature]) &&
|
|
|
|
$this->supported[$feature] === 'emulated' ||
|
|
|
|
$this->supported[$feature]);
|
|
|
|
}
|
2006-11-29 02:26:44 +03:00
|
|
|
/**
|
|
|
|
* quote
|
2006-12-02 17:40:47 +03:00
|
|
|
* quotes given input parameter
|
2006-11-29 02:26:44 +03:00
|
|
|
*
|
2006-12-02 17:40:47 +03:00
|
|
|
* @param mixed $input parameter to be quoted
|
2006-11-29 02:26:44 +03:00
|
|
|
* @param string $type
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function quote($input, $type = null) {
|
2006-12-02 17:40:47 +03:00
|
|
|
if($type == null) {
|
|
|
|
$type = gettype($input);
|
|
|
|
}
|
2006-11-29 02:26:44 +03:00
|
|
|
switch($type) {
|
|
|
|
case 'integer':
|
2006-12-02 17:40:47 +03:00
|
|
|
case 'enum':
|
|
|
|
case 'boolean':
|
2006-11-29 02:26:44 +03:00
|
|
|
return $input;
|
2006-12-02 17:40:47 +03:00
|
|
|
case 'array':
|
|
|
|
case 'object':
|
|
|
|
$input = serialize($input);
|
2006-11-29 02:26:44 +03:00
|
|
|
case 'string':
|
|
|
|
case 'char':
|
|
|
|
case 'varchar':
|
|
|
|
case 'text':
|
|
|
|
case 'gzip':
|
|
|
|
case 'blob':
|
|
|
|
case 'clob':
|
|
|
|
return $this->dbh->quote($input);
|
|
|
|
}
|
|
|
|
}
|
2006-11-15 18:19:55 +03:00
|
|
|
/**
|
|
|
|
* Removes any formatting in an sequence name using the 'seqname_format' option
|
|
|
|
*
|
|
|
|
* @param string $sqn string that containts name of a potential sequence
|
|
|
|
* @return string name of the sequence with possible formatting removed
|
|
|
|
*/
|
|
|
|
public function fixSequenceName($sqn) {
|
2006-11-16 16:19:45 +03:00
|
|
|
$seqPattern = '/^'.preg_replace('/%s/', '([a-z0-9_]+)', $this->getAttribute(Doctrine::ATTR_SEQNAME_FORMAT)).'$/i';
|
|
|
|
$seqName = preg_replace($seqPattern, '\\1', $sqn);
|
|
|
|
|
|
|
|
if ($seqName && ! strcasecmp($sqn, $db->getSequenceName($seqName))) {
|
|
|
|
return $seqName;
|
2006-11-15 18:19:55 +03:00
|
|
|
}
|
|
|
|
return $sqn;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Removes any formatting in an index name using the 'idxname_format' option
|
|
|
|
*
|
|
|
|
* @param string $idx string that containts name of anl index
|
|
|
|
* @return string name of the index with possible formatting removed
|
|
|
|
*/
|
|
|
|
public function fixIndexName($idx) {
|
2006-11-16 16:19:45 +03:00
|
|
|
$indexPattern = '/^'.preg_replace('/%s/', '([a-z0-9_]+)', $this->getAttribute(Doctrine::ATTR_IDXNAME_FORMAT)).'$/i';
|
|
|
|
$indexName = preg_replace($indexPattern, '\\1', $idx);
|
|
|
|
if ($indexName && ! strcasecmp($idx, $this->getIndexName($indexName))) {
|
|
|
|
return $indexName;
|
2006-11-15 18:19:55 +03:00
|
|
|
}
|
|
|
|
return $idx;
|
|
|
|
}
|
2006-11-24 23:51:40 +03:00
|
|
|
/**
|
|
|
|
* adds sequence name formatting to a sequence name
|
|
|
|
*
|
|
|
|
* @param string name of the sequence
|
|
|
|
* @return string formatted sequence name
|
|
|
|
*/
|
2006-11-24 01:54:10 +03:00
|
|
|
public function getSequenceName($sqn) {
|
2006-11-24 23:51:40 +03:00
|
|
|
return sprintf($this->getAttribute(Doctrine::ATTR_SEQNAME_FORMAT),
|
|
|
|
preg_replace('/[^a-z0-9_\$.]/i', '_', $sqn));
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* adds index name formatting to a index name
|
|
|
|
*
|
|
|
|
* @param string name of the index
|
|
|
|
* @return string formatted index name
|
|
|
|
*/
|
|
|
|
public function getIndexName($idx) {
|
|
|
|
return sprintf($this->getAttribute(Doctrine::ATTR_IDXNAME_FORMAT),
|
2006-11-28 01:22:31 +03:00
|
|
|
preg_replace('/[^a-z0-9_\$]/i', '_', $idx));
|
2006-11-24 01:54:10 +03:00
|
|
|
}
|
2006-10-31 02:27:26 +03:00
|
|
|
|
2006-10-31 13:16:36 +03:00
|
|
|
/**
|
|
|
|
* Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
|
|
|
|
* query, except that if there is already a row in the table with the same
|
|
|
|
* key field values, the REPLACE query just updates its values instead of
|
|
|
|
* inserting a new row.
|
|
|
|
*
|
|
|
|
* The REPLACE type of query does not make part of the SQL standards. Since
|
|
|
|
* practically only MySQL and SQLIte implement it natively, this type of
|
|
|
|
* query isemulated through this method for other DBMS using standard types
|
|
|
|
* of queries inside a transaction to assure the atomicity of the operation.
|
|
|
|
*
|
|
|
|
* @param string name of the table on which the REPLACE query will
|
|
|
|
* be executed.
|
|
|
|
*
|
|
|
|
* @param array an associative array that describes the fields and the
|
|
|
|
* values that will be inserted or updated in the specified table. The
|
|
|
|
* indexes of the array are the names of all the fields of the table.
|
|
|
|
*
|
|
|
|
* The values of the array are values to be assigned to the specified field.
|
|
|
|
*
|
|
|
|
* @param array $keys an array containing all key fields (primary key fields
|
|
|
|
* or unique index fields) for this table
|
|
|
|
*
|
|
|
|
* the uniqueness of a row will be determined according to
|
|
|
|
* the provided key fields
|
|
|
|
*
|
|
|
|
* this method will fail if no key fields are specified
|
|
|
|
*
|
|
|
|
* @throws Doctrine_Connection_Exception if this driver doesn't support replace
|
|
|
|
* @throws Doctrine_Connection_Exception if some of the key values was null
|
|
|
|
* @throws Doctrine_Connection_Exception if there were no key fields
|
|
|
|
* @throws PDOException if something fails at PDO level
|
2006-12-01 02:51:44 +03:00
|
|
|
* @return integer number of rows affected
|
2006-10-31 13:16:36 +03:00
|
|
|
*/
|
|
|
|
public function replace($table, array $fields, array $keys) {
|
2006-12-01 02:51:44 +03:00
|
|
|
//if( ! $this->supports('replace'))
|
|
|
|
// throw new Doctrine_Connection_Exception('replace query is not supported');
|
2006-10-31 13:16:36 +03:00
|
|
|
|
|
|
|
|
|
|
|
if(empty($keys))
|
|
|
|
throw new Doctrine_Connection_Exception('Not specified which fields are keys');
|
|
|
|
|
|
|
|
$condition = $values = array();
|
|
|
|
|
|
|
|
foreach($fields as $name => $value) {
|
|
|
|
$values[$name] = $value;
|
|
|
|
|
|
|
|
if(in_array($name, $keys)) {
|
|
|
|
if($value === null)
|
|
|
|
throw new Doctrine_Connection_Exception('key value '.$name.' may not be null');
|
|
|
|
|
|
|
|
$condition[] = $name . ' = ?';
|
|
|
|
$conditionValues[] = $value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$query = 'DELETE FROM '. $table . ' WHERE ' . implode(' AND ', $condition);
|
|
|
|
$affectedRows = $this->dbh->exec($query);
|
|
|
|
|
2006-10-31 13:42:46 +03:00
|
|
|
$this->insert($table, $values);
|
2006-10-31 13:16:36 +03:00
|
|
|
|
|
|
|
$affectedRows++;
|
|
|
|
|
|
|
|
|
|
|
|
return $affectedRows;
|
|
|
|
}
|
2006-10-31 13:42:46 +03:00
|
|
|
/**
|
|
|
|
* Inserts a table row with specified data.
|
|
|
|
*
|
|
|
|
* @param string $table The table to insert data into.
|
|
|
|
* @param array $values An associateve array containing column-value pairs.
|
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
public function insert($table, array $values = array()) {
|
|
|
|
if(empty($values))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// column names are specified as array keys
|
|
|
|
$cols = array_keys($values);
|
|
|
|
|
|
|
|
// build the statement
|
|
|
|
$query = "INSERT INTO $table "
|
|
|
|
. '(' . implode(', ', $cols) . ') '
|
|
|
|
. 'VALUES (' . substr(str_repeat('?, ', count($values)), 0, -2) . ')';
|
|
|
|
|
|
|
|
// prepare and execute the statement
|
|
|
|
$stmt = $this->dbh->prepare($query);
|
|
|
|
$stmt->execute(array_values($values));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2006-10-23 00:50:27 +04:00
|
|
|
/**
|
2006-10-31 12:14:54 +03:00
|
|
|
* returns the next value in the given sequence
|
|
|
|
*
|
|
|
|
* @param string $sequence
|
|
|
|
* @throws PDOException if something went wrong at database level
|
|
|
|
* @return integer
|
|
|
|
*/
|
|
|
|
public function nextId($sequence) {
|
|
|
|
throw new Doctrine_Connection_Exception('NextId() for sequences not supported by this driver.');
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Set the charset on the current connection
|
|
|
|
*
|
|
|
|
* @param string charset
|
|
|
|
*
|
2006-12-06 09:58:24 +03:00
|
|
|
* @return void
|
2006-10-31 12:14:54 +03:00
|
|
|
*/
|
|
|
|
public function setCharset($charset) {
|
|
|
|
}
|
|
|
|
/**
|
2006-11-14 21:21:36 +03:00
|
|
|
* fetchAll
|
2006-10-23 00:50:27 +04:00
|
|
|
*
|
2006-11-14 21:21:36 +03:00
|
|
|
* @param string $statement sql query to be executed
|
|
|
|
* @param array $params prepared statement params
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function fetchAll($statement, array $params = array()) {
|
|
|
|
return $this->query($statement, $params)->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* fetchOne
|
2006-10-31 12:14:54 +03:00
|
|
|
*
|
2006-11-14 21:21:36 +03:00
|
|
|
* @param string $statement sql query to be executed
|
|
|
|
* @param array $params prepared statement params
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function fetchOne($statement, array $params = array()) {
|
2006-11-24 01:54:10 +03:00
|
|
|
return current($this->execute($statement, $params)->fetch(PDO::FETCH_NUM));
|
2006-11-14 21:21:36 +03:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* fetchRow
|
2006-10-31 12:14:54 +03:00
|
|
|
*
|
2006-11-14 21:21:36 +03:00
|
|
|
* @param string $statement sql query to be executed
|
|
|
|
* @param array $params prepared statement params
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function fetchRow($statement, array $params = array()) {
|
|
|
|
return $this->query($statement, $params)->fetch(PDO::FETCH_ASSOC);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* fetchArray
|
2006-10-26 19:32:09 +04:00
|
|
|
*
|
2006-11-14 21:21:36 +03:00
|
|
|
* @param string $statement sql query to be executed
|
|
|
|
* @param array $params prepared statement params
|
|
|
|
* @return array
|
2006-10-23 00:50:27 +04:00
|
|
|
*/
|
2006-11-14 21:21:36 +03:00
|
|
|
public function fetchArray($statement, array $params = array()) {
|
|
|
|
return $this->query($statement, $params)->fetch(PDO::FETCH_NUM);
|
2006-10-23 00:50:27 +04:00
|
|
|
}
|
2006-11-14 21:21:36 +03:00
|
|
|
/**
|
|
|
|
* fetchColumn
|
|
|
|
*
|
|
|
|
* @param string $statement sql query to be executed
|
|
|
|
* @param array $params prepared statement params
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function fetchColumn($statement, array $params = array()) {
|
|
|
|
$result = $this->query($statement, $params)->fetchAll(PDO::FETCH_COLUMN);
|
2006-11-16 15:46:23 +03:00
|
|
|
|
2006-11-14 21:21:36 +03:00
|
|
|
if($this->options['portability'] & Doctrine::PORTABILITY_FIX_CASE)
|
|
|
|
$result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
|
2006-10-31 02:27:26 +03:00
|
|
|
|
2006-11-14 21:21:36 +03:00
|
|
|
return $result;
|
|
|
|
}
|
2006-10-26 19:32:09 +04:00
|
|
|
/**
|
2006-11-14 21:21:36 +03:00
|
|
|
* fetchAssoc
|
|
|
|
*
|
|
|
|
* @param string $statement sql query to be executed
|
|
|
|
* @param array $params prepared statement params
|
|
|
|
* @return array
|
2006-10-26 19:32:09 +04:00
|
|
|
*/
|
2006-11-14 21:21:36 +03:00
|
|
|
public function fetchAssoc($statement, array $params = array()) {
|
|
|
|
return $this->query($statement, $params)->fetchAll(PDO::FETCH_ASSOC);
|
2006-10-26 19:32:09 +04:00
|
|
|
}
|
2006-11-14 21:21:36 +03:00
|
|
|
/**
|
|
|
|
* fetchBoth
|
|
|
|
*
|
|
|
|
* @param string $statement sql query to be executed
|
|
|
|
* @param array $params prepared statement params
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function fetchBoth($statement, array $params = array()) {
|
|
|
|
return $this->query($statement, $params)->fetchAll(PDO::FETCH_BOTH);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-08-22 22:03:16 +04:00
|
|
|
/**
|
|
|
|
* query
|
2006-10-31 12:14:54 +03:00
|
|
|
* queries the database using Doctrine Query Language
|
2006-11-01 18:07:04 +03:00
|
|
|
* returns a collection of Doctrine_Record objects
|
2006-10-31 12:14:54 +03:00
|
|
|
*
|
|
|
|
* <code>
|
|
|
|
* $users = $conn->query('SELECT u.* FROM User u');
|
|
|
|
*
|
|
|
|
* $users = $conn->query('SELECT u.* FROM User u WHERE u.name LIKE ?', array('someone'));
|
|
|
|
* </code>
|
2006-08-22 22:03:16 +04:00
|
|
|
*
|
2006-10-31 12:14:54 +03:00
|
|
|
* @param string $query DQL query
|
|
|
|
* @param array $params query parameters
|
|
|
|
* @see Doctrine_Query
|
|
|
|
* @return Doctrine_Collection Collection of Doctrine_Record objects
|
2006-08-22 22:03:16 +04:00
|
|
|
*/
|
2006-10-31 12:14:54 +03:00
|
|
|
public function query($query, array $params = array()) {
|
2006-08-22 22:03:16 +04:00
|
|
|
$parser = new Doctrine_Query($this);
|
|
|
|
|
|
|
|
return $parser->query($query, $params);
|
|
|
|
}
|
2006-11-01 18:07:04 +03:00
|
|
|
/**
|
|
|
|
* query
|
|
|
|
* queries the database using Doctrine Query Language and returns
|
|
|
|
* the first record found
|
|
|
|
*
|
|
|
|
* <code>
|
|
|
|
* $user = $conn->queryOne('SELECT u.* FROM User u WHERE u.id = ?', array(1));
|
|
|
|
*
|
|
|
|
* $user = $conn->queryOne('SELECT u.* FROM User u WHERE u.name LIKE ? AND u.password = ?',
|
|
|
|
* array('someone', 'password')
|
|
|
|
* );
|
|
|
|
* </code>
|
|
|
|
*
|
|
|
|
* @param string $query DQL query
|
|
|
|
* @param array $params query parameters
|
|
|
|
* @see Doctrine_Query
|
|
|
|
* @return Doctrine_Record|false Doctrine_Record object on success,
|
|
|
|
* boolean false on failure
|
|
|
|
*/
|
|
|
|
public function queryOne($query, array $params = array()) {
|
|
|
|
$parser = new Doctrine_Query($this);
|
|
|
|
|
|
|
|
$coll = $parser->query($query, $params);
|
|
|
|
if( ! $coll->contains(0))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return $coll[0];
|
|
|
|
}
|
2006-08-22 22:03:16 +04:00
|
|
|
/**
|
|
|
|
* queries the database with limit and offset
|
|
|
|
* added to the query and returns a PDOStatement object
|
|
|
|
*
|
|
|
|
* @param string $query
|
|
|
|
* @param integer $limit
|
|
|
|
* @param integer $offset
|
|
|
|
* @return PDOStatement
|
|
|
|
*/
|
|
|
|
public function select($query,$limit = 0,$offset = 0) {
|
|
|
|
if($limit > 0 || $offset > 0)
|
2006-10-31 02:00:09 +03:00
|
|
|
$query = $this->modifyLimitQuery($query, $limit, $offset);
|
2006-08-22 22:03:16 +04:00
|
|
|
|
|
|
|
return $this->dbh->query($query);
|
|
|
|
}
|
|
|
|
/**
|
2006-12-22 01:06:08 +03:00
|
|
|
* execute
|
2006-08-22 22:03:16 +04:00
|
|
|
* @param string $query sql query
|
|
|
|
* @param array $params query parameters
|
|
|
|
*
|
2006-12-22 01:06:08 +03:00
|
|
|
* @return PDOStatement|Doctrine_Adapter_Statement
|
2006-08-22 22:03:16 +04:00
|
|
|
*/
|
|
|
|
public function execute($query, array $params = array()) {
|
2006-12-22 01:06:08 +03:00
|
|
|
try {
|
|
|
|
if( ! empty($params)) {
|
|
|
|
$stmt = $this->dbh->prepare($query);
|
|
|
|
$stmt->execute($params);
|
|
|
|
return $stmt;
|
|
|
|
} else {
|
|
|
|
return $this->dbh->query($query);
|
|
|
|
}
|
|
|
|
} catch(Doctrine_Adapter_Exception $e) {
|
2006-12-28 00:35:32 +03:00
|
|
|
} catch(PDOException $e) { }
|
|
|
|
|
|
|
|
$this->rethrowException($e);
|
2006-08-22 22:03:16 +04:00
|
|
|
}
|
2006-12-22 01:06:08 +03:00
|
|
|
/**
|
|
|
|
* exec
|
|
|
|
* @param string $query sql query
|
|
|
|
* @param array $params query parameters
|
|
|
|
*
|
|
|
|
* @return PDOStatement|Doctrine_Adapter_Statement
|
|
|
|
*/
|
|
|
|
public function exec($query, array $params = array()) {
|
|
|
|
try {
|
|
|
|
if( ! empty($params)) {
|
|
|
|
$stmt = $this->dbh->prepare($query);
|
|
|
|
$stmt->execute($params);
|
|
|
|
return $stmt;
|
|
|
|
} else {
|
|
|
|
return $this->dbh->exec($query);
|
|
|
|
}
|
|
|
|
} catch(Doctrine_Adapter_Exception $e) {
|
|
|
|
} catch(PDOException $e) { }
|
|
|
|
|
|
|
|
$this->rethrowException($e);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* rethrowException
|
|
|
|
*
|
|
|
|
* @throws Doctrine_Connection_Exception
|
|
|
|
*/
|
|
|
|
private function rethrowException(Exception $e) {
|
|
|
|
$name = 'Doctrine_Connection_' . $this->driverName . '_Exception';
|
|
|
|
|
|
|
|
$exc = new $name($e->getMessage(), (int) $e->getCode());
|
|
|
|
if( ! is_array($e->errorInfo))
|
2006-12-28 00:35:32 +03:00
|
|
|
$e->errorInfo = array(null, null, null, null);
|
2006-12-22 01:06:08 +03:00
|
|
|
|
2006-12-28 00:35:32 +03:00
|
|
|
$exc->processErrorInfo($e->errorInfo);
|
2006-12-22 01:06:08 +03:00
|
|
|
|
|
|
|
throw $exc;
|
|
|
|
}
|
2006-08-22 22:03:16 +04:00
|
|
|
/**
|
2006-10-31 02:27:26 +03:00
|
|
|
* hasTable
|
2006-08-22 22:03:16 +04:00
|
|
|
* whether or not this connection has table $name initialized
|
|
|
|
*
|
2006-10-31 02:27:26 +03:00
|
|
|
* @param mixed $name
|
2006-08-22 22:03:16 +04:00
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
public function hasTable($name) {
|
|
|
|
return isset($this->tables[$name]);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* returns a table object for given component name
|
|
|
|
*
|
|
|
|
* @param string $name component name
|
|
|
|
* @return object Doctrine_Table
|
|
|
|
*/
|
|
|
|
public function getTable($name) {
|
|
|
|
if(isset($this->tables[$name]))
|
|
|
|
return $this->tables[$name];
|
|
|
|
|
|
|
|
$class = $name."Table";
|
|
|
|
|
|
|
|
if(class_exists($class) && in_array("Doctrine_Table", class_parents($class))) {
|
2006-09-22 23:23:57 +04:00
|
|
|
return new $class($name, $this);
|
2006-08-22 22:03:16 +04:00
|
|
|
} else {
|
|
|
|
|
2006-09-22 23:23:57 +04:00
|
|
|
return new Doctrine_Table($name, $this);
|
2006-08-22 22:03:16 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* returns an array of all initialized tables
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getTables() {
|
|
|
|
return $this->tables;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* returns an iterator that iterators through all
|
|
|
|
* initialized table objects
|
|
|
|
*
|
2006-11-05 22:24:28 +03:00
|
|
|
* <code>
|
|
|
|
* foreach($conn as $index => $table) {
|
|
|
|
* print $table; // get a string representation of each table object
|
|
|
|
* }
|
|
|
|
* </code>
|
|
|
|
*
|
2006-12-28 00:35:32 +03:00
|
|
|
* @return ArrayIterator SPL ArrayIterator object
|
2006-08-22 22:03:16 +04:00
|
|
|
*/
|
|
|
|
public function getIterator() {
|
|
|
|
return new ArrayIterator($this->tables);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* returns the count of initialized table objects
|
|
|
|
*
|
|
|
|
* @return integer
|
|
|
|
*/
|
|
|
|
public function count() {
|
|
|
|
return count($this->tables);
|
|
|
|
}
|
|
|
|
/**
|
2006-09-20 15:29:35 +04:00
|
|
|
* addTable
|
|
|
|
* adds a Doctrine_Table object into connection registry
|
|
|
|
*
|
2006-08-22 22:03:16 +04:00
|
|
|
* @param $objTable a Doctrine_Table object to be added into registry
|
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
public function addTable(Doctrine_Table $objTable) {
|
|
|
|
$name = $objTable->getComponentName();
|
|
|
|
|
|
|
|
if(isset($this->tables[$name]))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
$this->tables[$name] = $objTable;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/**
|
2006-09-20 15:29:35 +04:00
|
|
|
* create
|
2006-08-22 22:03:16 +04:00
|
|
|
* creates a record
|
|
|
|
*
|
|
|
|
* create creates a record
|
|
|
|
* @param string $name component name
|
|
|
|
* @return Doctrine_Record Doctrine_Record object
|
|
|
|
*/
|
|
|
|
public function create($name) {
|
|
|
|
return $this->getTable($name)->create();
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* flush
|
|
|
|
* saves all the records from all tables
|
|
|
|
* this operation is isolated using a transaction
|
|
|
|
*
|
2006-10-31 13:42:46 +03:00
|
|
|
* @throws PDOException if something went wrong at database level
|
2006-08-22 22:03:16 +04:00
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function flush() {
|
|
|
|
$this->beginTransaction();
|
2006-10-31 13:42:46 +03:00
|
|
|
$this->unitOfWork->saveAll();
|
2006-08-22 22:03:16 +04:00
|
|
|
$this->commit();
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* clear
|
|
|
|
* clears all repositories
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function clear() {
|
|
|
|
foreach($this->tables as $k => $table) {
|
|
|
|
$table->getRepository()->evictAll();
|
|
|
|
$table->clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
2006-09-20 15:29:35 +04:00
|
|
|
* evictTables
|
|
|
|
* evicts all tables
|
|
|
|
*
|
2006-08-22 22:03:16 +04:00
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function evictTables() {
|
|
|
|
$this->tables = array();
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* close
|
|
|
|
* closes the connection
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function close() {
|
|
|
|
$this->getAttribute(Doctrine::ATTR_LISTENER)->onPreClose($this);
|
|
|
|
|
|
|
|
$this->clear();
|
2006-11-16 15:46:23 +03:00
|
|
|
|
2006-08-22 22:03:16 +04:00
|
|
|
$this->getAttribute(Doctrine::ATTR_LISTENER)->onClose($this);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* get the current transaction nesting level
|
|
|
|
*
|
|
|
|
* @return integer
|
|
|
|
*/
|
|
|
|
public function getTransactionLevel() {
|
2006-09-20 13:46:52 +04:00
|
|
|
return $this->transaction->getTransactionLevel();
|
2006-08-22 22:03:16 +04:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* beginTransaction
|
|
|
|
* starts a new transaction
|
2006-10-31 13:42:46 +03:00
|
|
|
*
|
|
|
|
* this method can be listened by onPreBeginTransaction and onBeginTransaction
|
|
|
|
* listener methods
|
|
|
|
*
|
2006-08-22 22:03:16 +04:00
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function beginTransaction() {
|
2006-09-20 13:46:52 +04:00
|
|
|
$this->transaction->beginTransaction();
|
2006-08-22 22:03:16 +04:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* commits the current transaction
|
|
|
|
* if lockmode is optimistic this method starts a transaction
|
|
|
|
* and commits it instantly
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function commit() {
|
2006-09-20 13:46:52 +04:00
|
|
|
$this->transaction->commit();
|
2006-08-22 22:03:16 +04:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* rollback
|
|
|
|
* rolls back all transactions
|
|
|
|
*
|
|
|
|
* this method also listens to onPreTransactionRollback and onTransactionRollback
|
|
|
|
* eventlisteners
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function rollback() {
|
2006-09-20 13:46:52 +04:00
|
|
|
$this->transaction->rollback();
|
2006-08-22 22:03:16 +04:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* saves the given record
|
|
|
|
*
|
|
|
|
* @param Doctrine_Record $record
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function save(Doctrine_Record $record) {
|
2006-09-30 16:36:03 +04:00
|
|
|
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreSave($record);
|
|
|
|
|
|
|
|
|
2006-08-22 22:03:16 +04:00
|
|
|
switch($record->getState()):
|
|
|
|
case Doctrine_Record::STATE_TDIRTY:
|
2006-11-30 00:09:02 +03:00
|
|
|
$this->unitOfWork->insert($record);
|
2006-08-22 22:03:16 +04:00
|
|
|
break;
|
|
|
|
case Doctrine_Record::STATE_DIRTY:
|
|
|
|
case Doctrine_Record::STATE_PROXY:
|
2006-11-30 00:09:02 +03:00
|
|
|
$this->unitOfWork->update($record);
|
2006-08-22 22:03:16 +04:00
|
|
|
break;
|
|
|
|
case Doctrine_Record::STATE_CLEAN:
|
|
|
|
case Doctrine_Record::STATE_TCLEAN:
|
|
|
|
// do nothing
|
|
|
|
break;
|
|
|
|
endswitch;
|
2006-09-30 16:36:03 +04:00
|
|
|
|
|
|
|
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record);
|
2006-08-22 22:03:16 +04:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* deletes this data access object and all the related composites
|
|
|
|
* this operation is isolated by a transaction
|
|
|
|
*
|
|
|
|
* this event can be listened by the onPreDelete and onDelete listeners
|
|
|
|
*
|
|
|
|
* @return boolean true on success, false on failure
|
|
|
|
*/
|
2006-10-31 02:00:09 +03:00
|
|
|
public function delete(Doctrine_Record $record) {
|
2006-09-20 15:29:35 +04:00
|
|
|
if( ! $record->exists())
|
|
|
|
return false;
|
2006-09-30 16:36:03 +04:00
|
|
|
|
2006-09-20 15:29:35 +04:00
|
|
|
$this->beginTransaction();
|
2006-08-22 22:03:16 +04:00
|
|
|
|
2006-09-30 16:36:03 +04:00
|
|
|
$record->getTable()->getListener()->onPreDelete($record);
|
|
|
|
|
2006-10-31 02:00:09 +03:00
|
|
|
$this->unitOfWork->deleteComposites($record);
|
2006-09-30 16:36:03 +04:00
|
|
|
|
2006-09-20 15:29:35 +04:00
|
|
|
$this->transaction->addDelete($record);
|
2006-08-22 22:03:16 +04:00
|
|
|
|
2006-09-30 16:36:03 +04:00
|
|
|
$record->getTable()->getListener()->onDelete($record);
|
|
|
|
|
2006-09-20 15:29:35 +04:00
|
|
|
$this->commit();
|
2006-11-01 18:07:04 +03:00
|
|
|
|
2006-09-20 15:29:35 +04:00
|
|
|
return true;
|
2006-08-22 22:03:16 +04:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* returns a string representation of this object
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function __toString() {
|
|
|
|
return Doctrine_Lib::getConnectionAsString($this);
|
|
|
|
}
|
|
|
|
}
|
2006-09-04 02:46:30 +04:00
|
|
|
|