/scar.php
PHP | 337 lines | 182 code | 61 blank | 94 comment | 24 complexity | f62a65dd5ccccfc26000af2d06282143 MD5 | raw file
Possible License(s): JSON
- <?php
- define('SCAR_BASE', dirname(__FILE__).DIRECTORY_SEPARATOR);
- define('SCAR_LIB', SCAR_BASE.'lib'.DIRECTORY_SEPARATOR);
- define('SCAR_DB', SCAR_BASE.'db'.DIRECTORY_SEPARATOR);
- include SCAR_LIB . 'exceptions.php';
- include SCAR_LIB . 'inflector.php';
- include SCAR_LIB . 'constants.php';
- include SCAR_LIB . 'sql.php';
- include SCAR_LIB . 'datastore.php';
- include SCAR_LIB . 'repository.php';
- include SCAR_LIB . 'scargeneric.php';
- /**
- * SCAR Factory and Bridge Class
- * The SCAR Object is a static object that:
- * - creates new SCAR_Generic Objects
- * - runs queries for SCAR_Generic Objects
- * - can retrieve tables and columns for databases
- * - can escape strings
- * - serves as a bridge for multiple DB types
- *
- * It should be used as the start of any SCAR retrieval chain, using the format
- * SCAR::get('tables') and from that point forward, you will be working with
- * SCAR_Generic objects. To trigger an insert, use the format SCAR::make('object')
- * to get a SCAR_Generic object geared towards inserting data.
- **/
- class SCAR {
- /**
- * contains an array of timings for events as they happen
- * useful for benchmarking
- **/
- public static $timings = array();
-
- /**
- * Specify a config path for the application or retrieve the config path
- * @param $cfg string a string to set the config path to
- * @return string
- **/
- public static function config($cfg = null) {
- static $config;
- if (!isset($config)) {
- $config = null;
- }
-
- if ($cfg === null) {
- return $config;
- }
- $config = $cfg;
-
- return $config;
- }
-
- /**
- * Record a timestamp for benchmarking
- * if $finish is provided, then it will be the time lapsed since
- * mark was called last for $method without $finish.
- * @param string $method the token we are timing
- * @param boolean $finish if true, the time lapse will be recorded to self::$timings
- **/
- public static function mark($method, $finish = false) {
- static $timings;
- if (!isset($timings)) {
- $timings = array();
- }
-
- $time = microtime(true);
-
- $uuid = 't'.$method;
- if (!$finish) {
- $timings[$uuid] = $time;
- return;
- }
-
- $end_time = floatval($time - $timings[$uuid]);
- SCAR::$timings[] = array('m' => $method, 't' => $end_time);
- }
- /**
- * Create a SCAR_Generic object from a class name
- * this function is useful when you know the class name
- * and want to create an object (for insert).
- * @param $name string a class name, ie Author
- * @return SCAR_Generic
- **/
- public static function make($name) {
- return SCAR::get(Inflector::tableize($name));
- }
-
- /**
- * Get a collection of objects, via a SCAR object from a table
- * this function creates a SCAR object based on the table
- * @param $name string a name of a table to create an object for
- * @param $columns string [optional] a list of columns to limit to
- * @return SCAR_Generic
- * @throws SCAR_Object_Not_Found_Exception
- **/
- public static function get($name, $columns = null) {
- static $repository;
-
- $datastore = SCAR::datastore();
-
- $klass = Inflector::classify($name);
- if (class_exists($klass)) {
- $k = new $klass();
- $k->columns($columns);
- $k->datastore($datastore);
- return $k;
- }
-
- // class does not exist, check the repository
- if (!isset($repository)) {
- self::mark('repository');
- $repository = new SCAR_Repository(SCAR::config());
- self::mark('repository', true);
- }
-
- $k = $repository->get($klass);
- if ($k === null) {
- throw new SCAR_Object_Not_Found_Exception(Inflector::camelize($name));
- }
- $k->columns($columns);
- $k->datastore($datastore);
- return $k;
- }
-
- /**
- * Loads a Sub-Scheme object.
- * This isolates DB specific methods into static classes which SCAR
- * can then call. This serves as a factory method, attempting to load
- * schemes out of the db/ directory
- * @param $scheme string The scheme type to load, ie 'mysql'
- * @throws SCAR_Scheme_Not_Found_Exception
- **/
- public static function load($scheme) {
- $klass = 'SCAR_'.$scheme;
- if (!class_exists($klass)) {
- $file = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'db' . DIRECTORY_SEPARATOR . strtolower($scheme) . '.php';
- if (!file_exists($file)) {
- throw new SCAR_Scheme_Not_Found_Exception($scheme);
- }
- include $file;
- if (!class_exists($klass)) {
- throw new SCAR_Scheme_Not_Found_Exception($scheme);
- }
- }
- }
-
- /**
- * Makes the SCAR Datastore a Singleton to the SCAR class
- * This is a retrieval point for the SCAR_Datastore object,
- * ensuring one persistent copy exists within a deployment of
- * SCAR.
- * @return SCAR_Datastore
- **/
- public static function datastore() {
- static $datastore;
- if (!isset($datastore)) {
- self::mark('datastore');
- $datastore = new SCAR_Datastore();
- self::mark('datastore', true);
- }
- return $datastore;
- }
-
- /**
- * Opens a connection to a database via a subscheme
- * @param $dsn string a scheme://user:pass@host/db formatted string
- * @return a connection object reference for that db combination
- * @throws SCAR_Connection_Exception
- **/
- public static function connect($dsn) {
- static $connectors;
- if (!isset($connectors)) {
- $connectors = array();
- }
-
- $uri = parse_url($dsn);
- if (!isset($uri['port'])) {
- $uri['port'] = null;
- }
-
- SCAR::load($uri['scheme']);
-
- $hash = md5(strtolower(serialize(ksort($uri))));
-
- if (!isset($connectors[$hash])) {
- self::mark('connect');
- $connector = call_user_func_array(array('SCAR_'.$uri['scheme'], 'connect'), array($uri));
- if (!$connector) {
- throw new SCAR_Connection_Exception($dsn);
- }
- $connectors[$hash] = $connector;
- self::mark('connect', true);
- }
- return $connectors[$hash];
- }
-
- /**
- * Calls a scheme specific escape function
- * @param $dsn string a formatted scheme://user:pass@host/db string
- * @param $string string what we want to escape for SQL
- * @return string
- **/
- public static function escape($dsn, $string) {
- $uri = parse_url($dsn);
- SCAR::load($uri['scheme']);
-
- return call_user_func_array(array('SCAR_'.$uri['scheme'], 'escape'), array($string));
- }
-
- /**
- * Runs a scheme specific query and normalizes the results for SCAR objects
- * @param $dsn string a scheme://user:pass@host/db formatted string
- * @param $sql string the SQL to run
- * @param $pk_list array the primary key list for the object
- * @return array
- * @throws SCAR_Query_Exception
- **/
- public static function query($dsn, $sql, $pk_list) {
- // var_dump($sql);
- self::mark($sql);
-
- $i_am = substr($sql, 0, 6);
- $is_update = ($i_am == 'UPDATE');
- $is_select = ($i_am == 'SELECT');
- $is_insert = ($i_am == 'INSERT');
-
- $db = SCAR::connect($dsn);
- // var_dump('QUERY: '.$sql);
- $uri = parse_url($dsn);
-
- SCAR::load($uri['scheme']);
-
- $rs = call_user_func_array(array('SCAR_'.$uri['scheme'], 'query'), array($db, $sql));
- if (!$rs) { throw new SCAR_Query_Exception($dsn, $sql); }
-
- $results = array();
- $keys = array();
-
- if ($is_select) {
- while ($row = call_user_func_array(array('SCAR_'.$uri['scheme'], 'row'), array($rs))) {
- $store_pk = implode(',', $pk_list);
- $pk_val = '';
- foreach ($pk_list as $pk) {
- $pk_val .= $row[$pk].',';
- }
- $pk_val = trim($pk_val, ',');
- $keys[$store_pk][] = $pk_val;
- $results[$pk_val] = $row;
- }
- }
-
- self::mark($sql, true);
-
- return array(
- 'keys' => $keys,
- 'data' => $results,
- 'rows' => ($is_select) ? call_user_func_array(array('SCAR_'.$uri['scheme'], 'count'), array($rs)) : false,
- 'next' => ($is_insert) ? call_user_func_array(array('SCAR_'.$uri['scheme'], 'nextid'), array($db)) : false,
- 'affected' => ($is_update || $is_insert) ? call_user_func_array(array('SCAR_'.$uri['scheme'], 'affected'), array($db)) : false,
- );
- }
-
- /**
- * Calls a scheme specific show tables command
- * @param $dsn string a scheme://user:pass@host/db formatted string
- * @return array
- **/
- public static function showTables($dsn) {
- $db = SCAR::connect($dsn);
- $uri = parse_url($dsn);
-
- SCAR::load($uri['scheme']);
-
- self::mark('showtables: '.$dsn);
-
- $sql = call_user_func_array(array('SCAR_'.$uri['scheme'], 'showTables'), array());
- $rs = call_user_func_array(array('SCAR_'.$uri['scheme'], 'query'), array($db, $sql));
-
- $tables = array();
- while ($row = call_user_func_array(array('SCAR_'.$uri['scheme'], 'row'), array($rs))) {
- $tables[] = $row;
- }
-
- self::mark('showtables: '.$dsn, true);
- self::mark('parsetables: '.$dsn);
- $results = call_user_func_array(array('SCAR_'.$uri['scheme'], 'parseShowTables'), array($tables));
-
- self::mark('parsetables: '.$dsn, true);
- return $results;
- }
-
- /**
- * Calls a scheme specific version of showing the columns in a database
- * @param $dsn string a scheme://user:pass@host/db formatted string
- * @param $table string a table name to get the columns for
- * @return array
- **/
- public static function showColumns($dsn, $table) {
- $db = SCAR::connect($dsn);
- $uri = parse_url($dsn);
-
- SCAR::load($uri['scheme']);
-
- self::mark('showcolumns: '.$dsn.' tbl: '.$table);
-
- $sql = call_user_func_array(array('SCAR_'.$uri['scheme'], 'showColumns'), array($table));
- $rs = call_user_func_array(array('SCAR_'.$uri['scheme'], 'query'), array($db, $sql));
- $columns = array();
- while ($row = call_user_func_array(array('SCAR_'.$uri['scheme'], 'row'), array($rs))) {
- $columns[] = $row;
- }
-
- self::mark('showcolumns: '.$dsn.' tbl: '.$table, true);
- self::mark('parsecolumns: '.$dsn.' tbl: '.$table);
-
- $results = call_user_func_array(array('SCAR_'.$uri['scheme'], 'parseShowColumns'), array($columns));
-
- self::mark('parsecolumns: '.$dsn.' tbl: '.$table, true);
- return $results;
- }
- }