From aa5aa8eb9792c179e3a47f6d483e8ef229430c71 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Wed, 23 Apr 2014 15:53:16 -0400 Subject: [PATCH] Docblock fixes and more dependency injection --- core/abstract/abstract_driver.php | 37 +++++++++-- core/abstract/abstract_sql.php | 92 +++++++++++++-------------- core/connection_manager.php | 7 +- core/interfaces/driver_interface.php | 15 ++++- core/query_builder.php | 58 ++++++++--------- core/query_parser.php | 9 ++- drivers/firebird/firebird_result.php | 3 +- drivers/firebird/firebird_sql.php | 2 +- drivers/firebird/firebird_util.php | 2 +- tests/core/db_qb_test.php | 7 -- tests/core/db_test.php | 7 ++ tests/db_files/FB_TEST_DB.FDB | Bin 860160 -> 897024 bytes 12 files changed, 141 insertions(+), 98 deletions(-) diff --git a/core/abstract/abstract_driver.php b/core/abstract/abstract_driver.php index 8cee9f9..4d87f4e 100644 --- a/core/abstract/abstract_driver.php +++ b/core/abstract/abstract_driver.php @@ -36,7 +36,7 @@ abstract class Abstract_Driver extends \PDO implements Driver_Interface { protected $statement; /** - * Character to escape indentifiers + * Character to escape identifiers * @var string */ protected $escape_char = '"'; @@ -100,6 +100,7 @@ abstract class Abstract_Driver extends \PDO implements Driver_Interface { /** * Allow invoke to work on table object * + * @codeCoverageIgnore * @param string $name * @param array $args * @return mixed @@ -120,6 +121,30 @@ abstract class Abstract_Driver extends \PDO implements Driver_Interface { // ! Concrete functions that can be overridden in child classes // -------------------------------------------------------------------------- + /** + * Get the SQL class for the current driver + * + * @return SQL_Interface + */ + public function get_sql() + { + return $this->sql; + } + + // -------------------------------------------------------------------------- + + /** + * Get the Util class for the current driver + * + * @return Abstract_Util + */ + public function get_util() + { + return $this->util; + } + + // -------------------------------------------------------------------------- + /** * Simplifies prepared statements for database queries * @@ -193,7 +218,7 @@ abstract class Abstract_Driver extends \PDO implements Driver_Interface { // before quoting it if ( ! empty($this->table_prefix)) { - // Split indentifier by period, will split into: + // Split identifier by period, will split into: // database.schema.table OR // schema.table OR // database.table OR @@ -436,7 +461,7 @@ abstract class Abstract_Driver extends \PDO implements Driver_Interface { */ public function get_columns($table) { - return $this->driver_query($this->sql->column_list($this->prefix_table($table)), FALSE); + return $this->driver_query($this->get_sql()->column_list($this->prefix_table($table)), FALSE); } // -------------------------------------------------------------------------- @@ -449,7 +474,7 @@ abstract class Abstract_Driver extends \PDO implements Driver_Interface { */ public function get_fks($table) { - return $this->driver_query($this->sql->fk_list($table), FALSE); + return $this->driver_query($this->get_sql()->fk_list($table), FALSE); } // -------------------------------------------------------------------------- @@ -462,7 +487,7 @@ abstract class Abstract_Driver extends \PDO implements Driver_Interface { */ public function get_indexes($table) { - return $this->driver_query($this->sql->index_list($this->prefix_table($table)), FALSE); + return $this->driver_query($this->get_sql()->index_list($this->prefix_table($table)), FALSE); } // -------------------------------------------------------------------------- @@ -491,7 +516,7 @@ abstract class Abstract_Driver extends \PDO implements Driver_Interface { // Call the appropriate method, if it exists if (is_string($query) && method_exists($this->sql, $query)) { - $query = $this->sql->$query(); + $query = $this->get_sql()->$query(); } // Return if the values are returned instead of a query, diff --git a/core/abstract/abstract_sql.php b/core/abstract/abstract_sql.php index a33332a..07f261f 100644 --- a/core/abstract/abstract_sql.php +++ b/core/abstract/abstract_sql.php @@ -1,46 +1,46 @@ -table_prefix = $params->prefix; } - // Create the Query Builder object - $conn = new Query_Builder($db); + // Create Query Builder object + $conn = new Query_Builder($db, new Query_Parser($db)); + // Save it for later if (isset($params->alias)) diff --git a/core/interfaces/driver_interface.php b/core/interfaces/driver_interface.php index df5aef9..21b447b 100644 --- a/core/interfaces/driver_interface.php +++ b/core/interfaces/driver_interface.php @@ -30,7 +30,6 @@ interface Driver_Interface { * @param string $username * @param string $password * @param array $driver_options - * @return void */ public function __construct($dsn, $username=NULL, $password=NULL, array $driver_options = array()); @@ -140,5 +139,19 @@ interface Driver_Interface { * @return \PDOStatement */ public function prepare_execute($sql, $params); + + /** + * Get the SQL class for the current driver + * + * @return SQL_Interface + */ + public function get_sql(); + + /** + * Get the Util class for the current driver + * + * @return Abstract_Util + */ + public function get_util(); } // End of driver_interface.php diff --git a/core/query_builder.php b/core/query_builder.php index 2ae8506..1e2fba8 100644 --- a/core/query_builder.php +++ b/core/query_builder.php @@ -100,7 +100,7 @@ class Query_Builder implements Query_Builder_Interface { /** * Value for limit string - * @var type string + * @var string */ protected $limit; @@ -162,14 +162,14 @@ class Query_Builder implements Query_Builder_Interface { protected $parser; /** - * Alias to $this->db->util - * @var DB_Util + * Alias to driver util class + * @var \Query\Driver\Abstract_Util */ public $util; /** - * Alias to $this->db->sql - * @var SQL_Interface + * Alias to driver sql class + * @var \Query\Driver\SQL_Interface */ public $sql; @@ -181,19 +181,19 @@ class Query_Builder implements Query_Builder_Interface { * Constructor * * @param \Query\Driver\Driver_Interface $db + * @param Query_Parser $parser */ - public function __construct(Driver_Interface $db) + public function __construct(Driver_Interface $db, Query_Parser $parser) { + // Inject driver and parser $this->db = $db; - - // Instantiate the Query Parser - $this->parser = new Query_Parser($this); + $this->parser = $parser; $this->queries['total_time'] = 0; - // Make things just slightly shorter - $this->sql = $this->db->sql; - $this->util = $this->db->util; + // Alias driver sql and util classes + $this->sql = $this->db->get_sql(); + $this->util = $this->db->get_util(); } // -------------------------------------------------------------------------- @@ -215,7 +215,7 @@ class Query_Builder implements Query_Builder_Interface { * Method to simplify select_ methods * * @param string $field - * @param string $as + * @param string|bool $as * @return string */ protected function _select($field, $as = FALSE) @@ -295,7 +295,7 @@ class Query_Builder implements Query_Builder_Interface { * Selects the minimum value of a field from a query * * @param string $field - * @param string $as + * @param string|bool $as * @return Query_Builder */ public function select_min($field, $as=FALSE) @@ -311,7 +311,7 @@ class Query_Builder implements Query_Builder_Interface { * Selects the average value of a field from a query * * @param string $field - * @param string $as + * @param string|bool $as * @return Query_Builder */ public function select_avg($field, $as=FALSE) @@ -327,7 +327,7 @@ class Query_Builder implements Query_Builder_Interface { * Selects the sum of a field from a query * * @param string $field - * @param string $as + * @param string|bool $as * @return Query_Builder */ public function select_sum($field, $as=FALSE) @@ -975,7 +975,7 @@ class Query_Builder implements Query_Builder_Interface { * @param $table * @param int|bool $limit * @param int|bool $offset - * @return PDOStatement + * @return \PDOStatement */ public function get($table='', $limit=FALSE, $offset=FALSE) { @@ -986,7 +986,7 @@ class Query_Builder implements Query_Builder_Interface { } // Set the limit, if it exists - if ($limit !== FALSE) + if (is_int($limit)) { $this->limit($limit, $offset); } @@ -997,13 +997,13 @@ class Query_Builder implements Query_Builder_Interface { // -------------------------------------------------------------------------- /** - * Convience method for get() with a where clause + * Convenience method for get() with a where clause * * @param string $table * @param array $where - * @param int $limit - * @param int $offset - * @return PDOStatement + * @param int|bool $limit + * @param int|bool $offset + * @return \PDOStatement */ public function get_where($table, $where=array(), $limit=FALSE, $offset=FALSE) { @@ -1061,7 +1061,7 @@ class Query_Builder implements Query_Builder_Interface { * * @param string $table * @param mixed $data - * @return PDOStatement + * @return \PDOStatement */ public function insert($table, $data=array()) { @@ -1081,7 +1081,7 @@ class Query_Builder implements Query_Builder_Interface { * * @param string $table * @param array $data - * @return PDOStatement + * @return \PDOStatement */ public function insert_batch($table, $data=array()) { @@ -1100,7 +1100,7 @@ class Query_Builder implements Query_Builder_Interface { * * @param string $table * @param mixed $data - * @return PDOStatement + * @return \PDOStatement */ public function update($table, $data=array()) { @@ -1120,7 +1120,7 @@ class Query_Builder implements Query_Builder_Interface { * * @param string $table * @param mixed $where - * @return PDOStatement + * @return \PDOStatement */ public function delete($table, $where='') { @@ -1143,7 +1143,7 @@ class Query_Builder implements Query_Builder_Interface { * @param string $type * @param string $table * @param bool $reset - * @resturn string + * @return string */ protected function _get_compile($type, $table, $reset) { @@ -1271,7 +1271,7 @@ class Query_Builder implements Query_Builder_Interface { * @param string $table * @param string $sql * @param array|null $vals - * @return PDOStatement + * @return \PDOStatement */ protected function _run($type, $table, $sql=NULL, $vals=NULL) { @@ -1311,7 +1311,7 @@ class Query_Builder implements Query_Builder_Interface { * @param string $name * @param array $params * @return mixed - * @throws BadMethodCallException + * @throws \BadMethodCallException */ public function __call($name, $params) { diff --git a/core/query_parser.php b/core/query_parser.php index 07fc54f..d2b7cfc 100644 --- a/core/query_parser.php +++ b/core/query_parser.php @@ -56,9 +56,9 @@ class Query_Parser { /** * Constructor/entry point into parser * - * @param \Query\Query_Builder $db + * @param Query_Builder $db */ - public function __construct(\Query\Query_Builder $db) + public function __construct(\Query\Driver\Driver_Interface $db) { $this->db = $db; } @@ -66,9 +66,10 @@ class Query_Parser { // -------------------------------------------------------------------------- /** - * Public parser method for seting the parse string + * Public parser method for setting the parse string * * @param string $sql + * @return array */ protected function parse_join($sql) { @@ -112,6 +113,8 @@ class Query_Parser { return implode('', $parts['combined']); } + // -------------------------------------------------------------------------- + /** * Returns a more useful match array * diff --git a/drivers/firebird/firebird_result.php b/drivers/firebird/firebird_result.php index 54189a0..28fe22a 100644 --- a/drivers/firebird/firebird_result.php +++ b/drivers/firebird/firebird_result.php @@ -57,7 +57,8 @@ class Firebird_Result extends \PDOStatement { * the query * * @param resource $link - * @param [\Query\Driver\Firebird] $db + * @param \Query\Driver\Firebird|null $db + * @return void */ public function __construct($link, Driver_Interface $db = NULL) { diff --git a/drivers/firebird/firebird_sql.php b/drivers/firebird/firebird_sql.php index a169b5d..0a4b205 100644 --- a/drivers/firebird/firebird_sql.php +++ b/drivers/firebird/firebird_sql.php @@ -28,7 +28,7 @@ class Firebird_SQL extends Abstract_SQL { * * @param string $sql * @param int $limit - * @param int $offset + * @param int|bool $offset * @return string */ public function limit($sql, $limit, $offset=FALSE) diff --git a/drivers/firebird/firebird_util.php b/drivers/firebird/firebird_util.php index b9b4ba0..6a513f2 100644 --- a/drivers/firebird/firebird_util.php +++ b/drivers/firebird/firebird_util.php @@ -28,7 +28,7 @@ namespace Query\Driver; class Firebird_Util extends Abstract_Util { /** - * Convienience public function to generate sql for creating a db table + * Convenience public function to generate sql for creating a db table * * @deprecated Use the table builder class instead * @param string $name diff --git a/tests/core/db_qb_test.php b/tests/core/db_qb_test.php index caf3832..465c1ff 100644 --- a/tests/core/db_qb_test.php +++ b/tests/core/db_qb_test.php @@ -153,13 +153,6 @@ abstract class QBTest extends Query_TestCase { $this->assertIsA($query, 'PDOStatement'); } - // -------------------------------------------------------------------------- - - public function testGetViews() - { - $this->assertTrue(is_array($this->db->get_views())); - } - // -------------------------------------------------------------------------- // ! Select tests // -------------------------------------------------------------------------- diff --git a/tests/core/db_test.php b/tests/core/db_test.php index f858652..221ced8 100644 --- a/tests/core/db_test.php +++ b/tests/core/db_test.php @@ -94,5 +94,12 @@ abstract class DBTest extends Query_TestCase { $this->assertTrue(is_array($keys)); } + // -------------------------------------------------------------------------- + + public function testGetViews() + { + $this->assertTrue(is_array($this->db->get_views())); + } + } // End of db_test.php \ No newline at end of file diff --git a/tests/db_files/FB_TEST_DB.FDB b/tests/db_files/FB_TEST_DB.FDB index f2e29a27c0738f082da12384b57f5c33576592cf..edd89efbda97b2cb1cd604cffd358eef3f9d4d19 100755 GIT binary patch delta 15859 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 KmgxDn#{U5M!^49B delta 9097 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