[ Index ] |
|
Code source de Symfony 1.0.0 |
1 <?php 2 /* 3 * $Id: Creole.php,v 1.14 2006/01/17 20:06:31 hlellelid Exp $ 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://creole.phpdb.org>. 20 */ 21 22 include_once 'creole/SQLException.php'; 23 include_once 'creole/Connection.php'; 24 25 // static: 26 // track errors is used by drivers to get better error messages 27 // make sure it's set. 28 29 @ini_set('track_errors', true); 30 31 /** 32 * This is the class that manages the database drivers. 33 * 34 * There are a number of default drivers (at the time of writing this comment: MySQL, MSSQL, SQLite, PgSQL, Oracle) 35 * that are "shipped" with Creole. You may wish to either add a new driver or swap out one of the existing drivers 36 * for your own custom driver. To do this you simply need to register your driver using the registerDriver() method. 37 * 38 * Note that you register your Connection class because the Connection class is responsible for calling the other 39 * driver classes (e.g. ResultSet, PreparedStatement, etc.). 40 * 41 * 42 * @author Hans Lellelid <hans@xmpl.org> 43 * @version $Revision: 1.14 $ 44 * @package creole 45 */ 46 class Creole { 47 48 /** 49 * Constant that indicates a connection object should be used. 50 */ 51 const PERSISTENT = 1; 52 53 /** 54 * Flag to pass to the connection to indicate that no case conversions 55 * should be performed by ResultSet on keys of fetched rows. 56 * @deprecated use COMPAT_ASSOC_LOWER 57 */ 58 const NO_ASSOC_LOWER = 16; 59 60 /** 61 * Flag to pass to the connection to indicate that a to-lower case conversion 62 * should be performed by ResultSet on keys of fetched rows. 63 */ 64 const COMPAT_ASSOC_LOWER = 32; 65 66 /** 67 * Flag to pass to the connection to indicate that an rtrim() should be performed 68 * on strings (using ResultSet->getString(), etc.). 69 */ 70 const COMPAT_RTRIM_STRING = 64; 71 72 /** 73 * Flag to indicate that all compatibility flags should be set. 74 */ 75 const COMPAT_ALL = 96; 76 77 /** 78 * Map of built-in drivers. 79 * Change or add your own using registerDriver() 80 * @see registerDriver() 81 * @var array Hash mapping phptype => driver class (in dot-path notation, e.g. 'mysql' => 'creole.drivers.mysql.MySQLConnection'). 82 */ 83 private static $driverMap = array( 'mysql' => 'creole.drivers.mysql.MySQLConnection', 84 'mysqli' => 'creole.drivers.mysqli.MySQLiConnection', 85 'pgsql' => 'creole.drivers.pgsql.PgSQLConnection', 86 'sqlite' => 'creole.drivers.sqlite.SQLiteConnection', 87 'oracle' => 'creole.drivers.oracle.OCI8Connection', 88 'mssql' => 'creole.drivers.mssql.MSSQLConnection', 89 'odbc' => 'creole.drivers.odbc.ODBCConnection' 90 ); 91 92 /** 93 * Map of already established connections 94 * @see getConnection() 95 * @var array Hash mapping connection DSN => Connection instance 96 */ 97 private static $connectionMap = array(); 98 99 /** 100 * Register your own RDBMS driver class. 101 * 102 * You can use this to specify your own class that replaces a default driver or 103 * adds support for a new driver. Register your own class by specifying the 104 * 'phptype' (e.g. mysql) and a dot-path notation to where your Connection class is 105 * relative to any location on the include path. You can also specify '*' as the phptype 106 * if you want to register a driver that will handle any native type (e.g. if creating 107 * a set of decorator classes that log SQL before calling native driver methods). YOU CAN 108 * ONLY REGISTER ONE CATCHALL ('*') DRIVER. 109 * <p> 110 * Note: the class you need to register is your Connection class because this is the 111 * class that's responsible for instantiating the other classes that are part of your 112 * driver. It is possible to mix & match drivers -- i.e. to write a custom driver where 113 * the Connection object just instantiates stock classes for ResultSet and PreparedStatement. 114 * Note that if you wanted to "override" only the ResultSet class you would also have to override 115 * the Connection and PreparedStatement classes so that they would return the correct ResultSet 116 * class. In the future we may implement a more "packaged" approach to drivers; for now we 117 * want to keep it simple. 118 * 119 * @param string $phptype The phptype (mysql, mssql, etc.). This is first part of DSN URL (e.g. mysql://localhost/...). 120 * You may also specify '*' to register a driver that will "wrap" the any native drivers. 121 * @param string $dotpath A dot-path locating your class. For example 'creole.drivers.mssql.MSSQLConnection' 122 * will be included like: include 'creole/drivers/mssql/MSSQLConnection.php' and the 123 * classname will be assumed to be 'MSSQLConnection'. 124 * @return void 125 */ 126 public static function registerDriver($phptype, $dotpath) 127 { 128 self::$driverMap[$phptype] = $dotpath; 129 } 130 131 /** 132 * Removes the driver for a PHP type. Note that this will remove user-registered 133 * drivers _and_ the default drivers. 134 * @param string $phptype The PHP type for driver to de-register. 135 * @see registerDriver() 136 */ 137 public static function deregisterDriver($phptype) 138 { 139 unset(self::$driverMap[$phptype]); 140 } 141 142 /** 143 * Returns the class path to the driver registered for specified type. 144 * @param string $phptype The phptype handled by driver (e.g. 'mysql', 'mssql', '*'). 145 * @return string The driver class in dot-path notation (e.g. creole.drivers.mssql.MSSQLConnection) 146 * or NULL if no registered driver found. 147 */ 148 public static function getDriver($phptype) 149 { 150 if (isset(self::$driverMap[$phptype])) { 151 return self::$driverMap[$phptype]; 152 } else { 153 return null; 154 } 155 } 156 157 /** 158 * Create a new DB connection object and connect to the specified 159 * database 160 * 161 * @param mixed $dsn "data source name", see the self::parseDSN 162 * method for a description of the dsn format. Can also be 163 * specified as an array of the format returned by DB::parseDSN(). 164 165 * @param int $flags Connection flags (e.g. PERSISTENT). 166 * 167 * @return Connection Newly created DB connection object 168 * @throws SQLException 169 * @see self::parseDSN() 170 */ 171 public static function getConnection($dsn, $flags = 0) 172 { 173 if (is_array($dsn)) { 174 $dsninfo = $dsn; 175 } else { 176 $dsninfo = self::parseDSN($dsn); 177 } 178 179 // gather any flags from the DSN 180 if ( isset ( $dsninfo['persistent'] ) && ! empty ( $dsninfo['persistent'] ) ) 181 $flags |= Creole::PERSISTENT; 182 if ( isset ( $dsninfo['compat_assoc_lower'] ) && ! empty ( $dsninfo['compat_assoc_lower'] ) ) 183 $flags |= Creole::COMPAT_ASSOC_LOWER; 184 if ( isset ( $dsninfo['compat_rtrim_string'] ) && ! empty ( $dsninfo['compat_rtrim_string'] ) ) 185 $flags |= Creole::COMPAT_RTRIM_STRING; 186 if ( isset ( $dsninfo['compat_all'] ) && ! empty ( $dsninfo['compat_all'] ) ) 187 $flags |= Creole::COMPAT_ALL; 188 189 if ($flags & Creole::NO_ASSOC_LOWER) { 190 trigger_error("The Creole::NO_ASSOC_LOWER flag has been deprecated, and is now the default behavior. Use Creole::COMPAT_ASSOC_LOWER to lowercase resulset keys.", E_USER_WARNING); 191 } 192 193 // sort $dsninfo by keys so the serialized result is always the same 194 // for identical connection parameters, no matter what their order is 195 ksort($dsninfo); 196 $connectionMapKey = crc32(serialize($dsninfo + array('compat_flags' => ($flags & Creole::COMPAT_ALL)))); 197 198 // see if we already have a connection with these parameters cached 199 if(isset(self::$connectionMap[$connectionMapKey])) 200 { 201 // persistent connections will be used if a non-persistent one was requested and is available 202 // but a persistent connection will be created if a non-persistent one is present 203 204 // TODO: impliment auto close of non persistent and replacing the 205 // non persistent with the persistent object so as we dont have 206 // both links open for no reason 207 208 if( isset(self::$connectionMap[$connectionMapKey][1]) ) { // is persistent 209 // a persistent connection with these parameters is already there, 210 // so we return it, no matter what was specified as persistent flag 211 $con = self::$connectionMap[$connectionMapKey][1]; 212 } else { 213 // we don't have a persistent connection, and since the persistent 214 // flag wasn't set either, we just return the non-persistent connection 215 $con = self::$connectionMap[$connectionMapKey][0]; 216 } 217 218 // if we're here, a non-persistent connection was already there, but 219 // the user wants a persistent one, so it will be created 220 221 if ($con->isConnected()) 222 return $con; 223 } 224 225 // support "catchall" drivers which will themselves handle the details of connecting 226 // using the proper RDBMS driver. 227 if (isset(self::$driverMap['*'])) { 228 $type = '*'; 229 } else { 230 $type = $dsninfo['phptype']; 231 if (!isset(self::$driverMap[$type])) { 232 throw new SQLException("No driver has been registered to handle connection type: $type"); 233 } 234 } 235 236 // may need to make this more complex if we add support 237 // for 'dbsyntax' 238 $clazz = self::import(self::$driverMap[$type]); 239 $obj = new $clazz(); 240 241 if (!($obj instanceof Connection)) { 242 throw new SQLException("Class does not implement creole.Connection interface: $clazz"); 243 } 244 245 try { 246 $obj->connect($dsninfo, $flags); 247 } catch(SQLException $sqle) { 248 $sqle->setUserInfo($dsninfo); 249 throw $sqle; 250 } 251 $persistent = ($flags & Creole::PERSISTENT) === Creole::PERSISTENT; 252 return self::$connectionMap[$connectionMapKey][(int)$persistent] = $obj; 253 } 254 255 /** 256 * Parse a data source name. 257 * 258 * This isn't quite as powerful as DB::parseDSN(); it's also a lot simpler, a lot faster, 259 * and many fewer lines of code. 260 * 261 * A array with the following keys will be returned: 262 * phptype: Database backend used in PHP (mysql, odbc etc.) 263 * protocol: Communication protocol to use (tcp, unix etc.) 264 * hostspec: Host specification (hostname[:port]) 265 * database: Database to use on the DBMS server 266 * username: User name for login 267 * password: Password for login 268 * 269 * The format of the supplied DSN is in its fullest form: 270 * 271 * phptype://username:password@protocol+hostspec/database 272 * 273 * Most variations are allowed: 274 * 275 * phptype://username:password@protocol+hostspec:110//usr/db_file.db 276 * phptype://username:password@hostspec/database_name 277 * phptype://username:password@hostspec 278 * phptype://username@hostspec 279 * phptype://hostspec/database 280 * phptype://hostspec 281 * phptype 282 * 283 * @param string $dsn Data Source Name to be parsed 284 * @return array An associative array 285 */ 286 public static function parseDSN($dsn) 287 { 288 if (is_array($dsn)) { 289 return $dsn; 290 } 291 292 $parsed = array( 293 'phptype' => null, 294 'username' => null, 295 'password' => null, 296 'protocol' => null, 297 'hostspec' => null, 298 'port' => null, 299 'socket' => null, 300 'database' => null 301 ); 302 303 $info = parse_url($dsn); 304 305 if (count($info) === 1) { // if there's only one element in result, then it must be the phptype 306 $parsed['phptype'] = array_pop($info); 307 return $parsed; 308 } 309 310 // some values can be copied directly 311 $parsed['phptype'] = @$info['scheme']; 312 $parsed['username'] = @$info['user']; 313 $parsed['password'] = @$info['pass']; 314 $parsed['port'] = @$info['port']; 315 316 $host = @$info['host']; 317 if (false !== ($pluspos = strpos($host, '+'))) { 318 $parsed['protocol'] = substr($host,0,$pluspos); 319 if ($parsed['protocol'] === 'unix') { 320 $parsed['socket'] = substr($host,$pluspos+1); 321 } else { 322 $parsed['hostspec'] = substr($host,$pluspos+1); 323 } 324 } else { 325 $parsed['hostspec'] = $host; 326 } 327 328 if (isset($info['path'])) { 329 $parsed['database'] = substr($info['path'], 1); // remove first char, which is '/' 330 } 331 332 if (isset($info['query'])) { 333 $opts = explode('&', $info['query']); 334 foreach ($opts as $opt) { 335 list($key, $value) = explode('=', $opt); 336 if (!isset($parsed[$key])) { // don't allow params overwrite 337 $parsed[$key] = urldecode($value); 338 } 339 } 340 } 341 342 return $parsed; 343 } 344 345 /** 346 * Include once a file specified in DOT notation. 347 * Package notation is expected to be relative to a location 348 * on the PHP include_path. 349 * @param string $class 350 * @return string unqualified classname 351 * @throws SQLException - if class does not exist and cannot load file 352 * - if after loading file class still does not exist 353 */ 354 public static function import($class) { 355 $pos = strrpos($class, '.'); 356 // get just classname ('path.to.ClassName' -> 'ClassName') 357 if ($pos !== false) { 358 $classname = substr($class, $pos + 1); 359 } 360 else 361 { 362 $classname = $class; 363 } 364 if (!class_exists($classname, false)) { 365 $path = strtr($class, '.', DIRECTORY_SEPARATOR) . '.php'; 366 $ret = include_once($path); 367 if ($ret === false) { 368 throw new SQLException("Unable to load driver class: " . $class); 369 } 370 if (!class_exists($classname)) { 371 throw new SQLException("Unable to find loaded class: $classname (Hint: make sure classname matches filename)"); 372 } 373 } 374 return $classname; 375 } 376 377 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Fri Mar 16 22:42:14 2007 | par Balluche grâce à PHPXref 0.7 |