PageRenderTime 62ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/site/application/third_party/php-activerecord/lib/Connection.php

https://bitbucket.org/thiscode/thiscode-shop
PHP | 523 lines | 267 code | 64 blank | 192 comment | 33 complexity | b32e31f19d47b885e369943910cf35e0 MD5 | raw file
  1. <?php
  2. /**
  3. * @package ActiveRecord
  4. */
  5. namespace ActiveRecord;
  6. require 'Column.php';
  7. use PDO;
  8. use PDOException;
  9. use Closure;
  10. /**
  11. * The base class for database connection adapters.
  12. *
  13. * @package ActiveRecord
  14. */
  15. abstract class Connection
  16. {
  17. /**
  18. * The PDO connection object.
  19. * @var mixed
  20. */
  21. public $connection;
  22. /**
  23. * The last query run.
  24. * @var string
  25. */
  26. public $last_query;
  27. /**
  28. * Switch for logging.
  29. *
  30. * @var bool
  31. */
  32. private $logging = false;
  33. /**
  34. * Contains a Logger object that must impelement a log() method.
  35. *
  36. * @var object
  37. */
  38. private $logger;
  39. /**
  40. * The name of the protocol that is used.
  41. * @var string
  42. */
  43. public $protocol;
  44. /**
  45. * Default PDO options to set for each connection.
  46. * @var array
  47. */
  48. static $PDO_OPTIONS = array(
  49. PDO::ATTR_CASE => PDO::CASE_LOWER,
  50. PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  51. PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
  52. PDO::ATTR_STRINGIFY_FETCHES => false);
  53. /**
  54. * The quote character for stuff like column and field names.
  55. * @var string
  56. */
  57. static $QUOTE_CHARACTER = '`';
  58. /**
  59. * Default port.
  60. * @var int
  61. */
  62. static $DEFAULT_PORT = 0;
  63. /**
  64. * Retrieve a database connection.
  65. *
  66. * @param string $connection_string_or_connection_name A database connection string (ex. mysql://user:pass@host[:port]/dbname)
  67. * Everything after the protocol:// part is specific to the connection adapter.
  68. * OR
  69. * A connection name that is set in ActiveRecord\Config
  70. * If null it will use the default connection specified by ActiveRecord\Config->set_default_connection
  71. * @return Connection
  72. * @see parse_connection_url
  73. */
  74. public static function instance($connection_string_or_connection_name=null)
  75. {
  76. $config = Config::instance();
  77. if (strpos($connection_string_or_connection_name, '://') === false)
  78. {
  79. $connection_string = $connection_string_or_connection_name ?
  80. $config->get_connection($connection_string_or_connection_name) :
  81. $config->get_default_connection_string();
  82. }
  83. else
  84. $connection_string = $connection_string_or_connection_name;
  85. if (!$connection_string)
  86. throw new DatabaseException("Empty connection string");
  87. $info = static::parse_connection_url($connection_string);
  88. $fqclass = static::load_adapter_class($info->protocol);
  89. try {
  90. $connection = new $fqclass($info);
  91. $connection->protocol = $info->protocol;
  92. $connection->logging = $config->get_logging();
  93. $connection->logger = $connection->logging ? $config->get_logger() : null;
  94. if (isset($info->charset))
  95. $connection->set_encoding($info->charset);
  96. } catch (PDOException $e) {
  97. throw new DatabaseException($e);
  98. }
  99. return $connection;
  100. }
  101. /**
  102. * Loads the specified class for an adapter.
  103. *
  104. * @param string $adapter Name of the adapter.
  105. * @return string The full name of the class including namespace.
  106. */
  107. private static function load_adapter_class($adapter)
  108. {
  109. $class = ucwords($adapter) . 'Adapter';
  110. $fqclass = 'ActiveRecord\\' . $class;
  111. $source = __DIR__ . "/adapters/$class.php";
  112. if (!file_exists($source))
  113. throw new DatabaseException("$fqclass not found!");
  114. require_once($source);
  115. return $fqclass;
  116. }
  117. /**
  118. * Use this for any adapters that can take connection info in the form below
  119. * to set the adapters connection info.
  120. *
  121. * <code>
  122. * protocol://username:password@host[:port]/dbname
  123. * protocol://urlencoded%20username:urlencoded%20password@host[:port]/dbname?decode=true
  124. * protocol://username:password@unix(/some/file/path)/dbname
  125. * </code>
  126. *
  127. * Sqlite has a special syntax, as it does not need a database name or user authentication:
  128. *
  129. * <code>
  130. * sqlite://file.db
  131. * sqlite://../relative/path/to/file.db
  132. * sqlite://unix(/absolute/path/to/file.db)
  133. * sqlite://windows(c%2A/absolute/path/to/file.db)
  134. * </code>
  135. *
  136. * @param string $connection_url A connection URL
  137. * @return object the parsed URL as an object.
  138. */
  139. public static function parse_connection_url($connection_url)
  140. {
  141. $url = @parse_url($connection_url);
  142. if (!isset($url['host']))
  143. throw new DatabaseException('Database host must be specified in the connection string. If you want to specify an absolute filename, use e.g. sqlite://unix(/path/to/file)');
  144. $info = new \stdClass();
  145. $info->protocol = $url['scheme'];
  146. $info->host = $url['host'];
  147. $info->db = isset($url['path']) ? substr($url['path'], 1) : null;
  148. $info->user = isset($url['user']) ? $url['user'] : null;
  149. $info->pass = isset($url['pass']) ? $url['pass'] : null;
  150. $allow_blank_db = ($info->protocol == 'sqlite');
  151. if ($info->host == 'unix(')
  152. {
  153. $socket_database = $info->host . '/' . $info->db;
  154. if ($allow_blank_db)
  155. $unix_regex = '/^unix\((.+)\)\/?().*$/';
  156. else
  157. $unix_regex = '/^unix\((.+)\)\/(.+)$/';
  158. if (preg_match_all($unix_regex, $socket_database, $matches) > 0)
  159. {
  160. $info->host = $matches[1][0];
  161. $info->db = $matches[2][0];
  162. }
  163. } elseif (substr($info->host, 0, 8) == 'windows(')
  164. {
  165. $info->host = urldecode(substr($info->host, 8) . '/' . substr($info->db, 0, -1));
  166. $info->db = null;
  167. }
  168. if ($allow_blank_db && $info->db)
  169. $info->host .= '/' . $info->db;
  170. if (isset($url['port']))
  171. $info->port = $url['port'];
  172. if (strpos($connection_url, 'decode=true') !== false)
  173. {
  174. if ($info->user)
  175. $info->user = urldecode($info->user);
  176. if ($info->pass)
  177. $info->pass = urldecode($info->pass);
  178. }
  179. if (isset($url['query']))
  180. {
  181. foreach (explode('/&/', $url['query']) as $pair) {
  182. list($name, $value) = explode('=', $pair);
  183. if ($name == 'charset')
  184. $info->charset = $value;
  185. }
  186. }
  187. return $info;
  188. }
  189. /**
  190. * Class Connection is a singleton. Access it via instance().
  191. *
  192. * @param array $info Array containing URL parts
  193. * @return Connection
  194. */
  195. protected function __construct($info)
  196. {
  197. try {
  198. // unix sockets start with a /
  199. if ($info->host[0] != '/')
  200. {
  201. $host = "host=$info->host";
  202. if (isset($info->port))
  203. $host .= ";port=$info->port";
  204. }
  205. else
  206. $host = "unix_socket=$info->host";
  207. $this->connection = new PDO("$info->protocol:$host;dbname=$info->db", $info->user, $info->pass, static::$PDO_OPTIONS);
  208. } catch (PDOException $e) {
  209. throw new DatabaseException($e);
  210. }
  211. }
  212. /**
  213. * Retrieves column meta data for the specified table.
  214. *
  215. * @param string $table Name of a table
  216. * @return array An array of {@link Column} objects.
  217. */
  218. public function columns($table)
  219. {
  220. $columns = array();
  221. $sth = $this->query_column_info($table);
  222. while (($row = $sth->fetch())) {
  223. $c = $this->create_column($row);
  224. $columns[$c->name] = $c;
  225. }
  226. return $columns;
  227. }
  228. /**
  229. * Escapes quotes in a string.
  230. *
  231. * @param string $string The string to be quoted.
  232. * @return string The string with any quotes in it properly escaped.
  233. */
  234. public function escape($string)
  235. {
  236. return $this->connection->quote($string);
  237. }
  238. /**
  239. * Retrieve the insert id of the last model saved.
  240. *
  241. * @param string $sequence Optional name of a sequence to use
  242. * @return int
  243. */
  244. public function insert_id($sequence=null)
  245. {
  246. return $this->connection->lastInsertId($sequence);
  247. }
  248. /**
  249. * Execute a raw SQL query on the database.
  250. *
  251. * @param string $sql Raw SQL string to execute.
  252. * @param array &$values Optional array of bind values
  253. * @return mixed A result set object
  254. */
  255. public function query($sql, &$values=array())
  256. {
  257. if ($this->logging)
  258. $this->logger->log($sql);
  259. $this->last_query = $sql;
  260. try {
  261. if (!($sth = $this->connection->prepare($sql)))
  262. throw new DatabaseException($this);
  263. } catch (PDOException $e) {
  264. throw new DatabaseException($this);
  265. }
  266. $sth->setFetchMode(PDO::FETCH_ASSOC);
  267. try {
  268. if (!$sth->execute($values))
  269. throw new DatabaseException($this);
  270. } catch (PDOException $e) {
  271. throw new DatabaseException($sth);
  272. }
  273. return $sth;
  274. }
  275. /**
  276. * Execute a query that returns maximum of one row with one field and return it.
  277. *
  278. * @param string $sql Raw SQL string to execute.
  279. * @param array &$values Optional array of values to bind to the query.
  280. * @return string
  281. */
  282. public function query_and_fetch_one($sql, &$values=array())
  283. {
  284. $sth = $this->query($sql, $values);
  285. $row = $sth->fetch(PDO::FETCH_NUM);
  286. return $row[0];
  287. }
  288. /**
  289. * Execute a raw SQL query and fetch the results.
  290. *
  291. * @param string $sql Raw SQL string to execute.
  292. * @param Closure $handler Closure that will be passed the fetched results.
  293. */
  294. public function query_and_fetch($sql, Closure $handler)
  295. {
  296. $sth = $this->query($sql);
  297. while (($row = $sth->fetch(PDO::FETCH_ASSOC)))
  298. $handler($row);
  299. }
  300. /**
  301. * Returns all tables for the current database.
  302. *
  303. * @return array Array containing table names.
  304. */
  305. public function tables()
  306. {
  307. $tables = array();
  308. $sth = $this->query_for_tables();
  309. while (($row = $sth->fetch(PDO::FETCH_NUM)))
  310. $tables[] = $row[0];
  311. return $tables;
  312. }
  313. /**
  314. * Starts a transaction.
  315. */
  316. public function transaction()
  317. {
  318. if (!$this->connection->beginTransaction())
  319. throw new DatabaseException($this);
  320. }
  321. /**
  322. * Commits the current transaction.
  323. */
  324. public function commit()
  325. {
  326. if (!$this->connection->commit())
  327. throw new DatabaseException($this);
  328. }
  329. /**
  330. * Rollback a transaction.
  331. */
  332. public function rollback()
  333. {
  334. if (!$this->connection->rollback())
  335. throw new DatabaseException($this);
  336. }
  337. /**
  338. * Tells you if this adapter supports sequences or not.
  339. *
  340. * @return boolean
  341. */
  342. function supports_sequences()
  343. {
  344. return false;
  345. }
  346. /**
  347. * Return a default sequence name for the specified table.
  348. *
  349. * @param string $table Name of a table
  350. * @param string $column_name Name of column sequence is for
  351. * @return string sequence name or null if not supported.
  352. */
  353. public function get_sequence_name($table, $column_name)
  354. {
  355. return "{$table}_seq";
  356. }
  357. /**
  358. * Return SQL for getting the next value in a sequence.
  359. *
  360. * @param string $sequence_name Name of the sequence
  361. * @return string
  362. */
  363. public function next_sequence_value($sequence_name)
  364. {
  365. return null;
  366. }
  367. /**
  368. * Quote a name like table names and field names.
  369. *
  370. * @param string $string String to quote.
  371. * @return string
  372. */
  373. public function quote_name($string)
  374. {
  375. return $string[0] === static::$QUOTE_CHARACTER || $string[strlen($string) - 1] === static::$QUOTE_CHARACTER ?
  376. $string : static::$QUOTE_CHARACTER . $string . static::$QUOTE_CHARACTER;
  377. }
  378. /**
  379. * Return a date time formatted into the database's date format.
  380. *
  381. * @param DateTime $datetime The DateTime object
  382. * @return string
  383. */
  384. public function date_to_string($datetime)
  385. {
  386. return $datetime->format('Y-m-d');
  387. }
  388. /**
  389. * Return a date time formatted into the database's datetime format.
  390. *
  391. * @param DateTime $datetime The DateTime object
  392. * @return string
  393. */
  394. public function datetime_to_string($datetime)
  395. {
  396. return $datetime->format('Y-m-d H:i:s T');
  397. }
  398. /**
  399. * Converts a string representation of a datetime into a DateTime object.
  400. *
  401. * @param string $string A datetime in the form accepted by date_create()
  402. * @return DateTime
  403. */
  404. public function string_to_datetime($string)
  405. {
  406. $date = date_create($string);
  407. $errors = \DateTime::getLastErrors();
  408. if ($errors['warning_count'] > 0 || $errors['error_count'] > 0)
  409. return null;
  410. return new DateTime($date->format('Y-m-d H:i:s T'));
  411. }
  412. /**
  413. * Adds a limit clause to the SQL query.
  414. *
  415. * @param string $sql The SQL statement.
  416. * @param int $offset Row offset to start at.
  417. * @param int $limit Maximum number of rows to return.
  418. * @return string The SQL query that will limit results to specified parameters
  419. */
  420. abstract function limit($sql, $offset, $limit);
  421. /**
  422. * Query for column meta info and return statement handle.
  423. *
  424. * @param string $table Name of a table
  425. * @return PDOStatement
  426. */
  427. abstract public function query_column_info($table);
  428. /**
  429. * Query for all tables in the current database. The result must only
  430. * contain one column which has the name of the table.
  431. *
  432. * @return PDOStatement
  433. */
  434. abstract function query_for_tables();
  435. /**
  436. * Executes query to specify the character set for this connection.
  437. */
  438. abstract function set_encoding($charset);
  439. /*
  440. * Returns an array mapping of native database types
  441. */
  442. abstract public function native_database_types();
  443. /**
  444. * Specifies whether or not adapter can use LIMIT/ORDER clauses with DELETE & UPDATE operations
  445. *
  446. * @internal
  447. * @returns boolean (FALSE by default)
  448. */
  449. public function accepts_limit_and_order_for_update_and_delete()
  450. {
  451. return false;
  452. }
  453. }
  454. ;
  455. ?>