diff --git a/.gitignore b/.gitignore index 8d6ad33..4e19e79 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ Thumbs.db # skip base files/folders +_temp hooks packages/vendor packages/node_modules diff --git a/models/Model.php b/models/Model.php index 6d4165c..8f68009 100644 --- a/models/Model.php +++ b/models/Model.php @@ -9,11 +9,13 @@ */ use \Contracts\Policies\DBAccessInterface as DBInterface; -use \Providers\Core\QueryBuilder as Builder; - class Model implements DBInterface { + public static $class = NULL; + + protected static $instance = NULL; + protected $table = 'NULL'; protected $primaryKey = 'NULL'; @@ -25,54 +27,292 @@ class Model implements DBInterface { ); public function __construct(){ + + static::setInstance($this); + + } - $envfile = $env['app.path.base'] . '.env'; - - if(file_exists($envfile)){ + public function __destruct(){ - $app->setDBConnection($envfile); + static::unsetInstance(); + } - }else{ + protected static function unsetInstance(){ - throw new \Exception("Cannot create Model Instance >> Database Settings Not Found"); + if(static::$instance){ - } + static::$instance = NULL; + } } - protected function setBuilder(Builder $builder){ + protected static function setInstance(Model $m){ + + /* + * A trick to instantiate an class without calling the constructor + * > credits: PHPUnit Framework Project - GitHub + * + * -- This trick has been modified for use in Jollof + */ + + if(!isset(static::$instance)){ + + $m->builder = $app->getBuilder($m->getAttributes()); + + static::$instance = unserialize( + sprintf('O:%d:"%s":0:{}', strlen(get_called_class()), get_class($m)) + ); + + static::$class = get_class(static::$instance); + + static::$instance->builder = $m->builder; + + }else{ + + $m->builder = static::$instance->builder; + + } + + } - $this->builder = $builder; - } + + /** + * + * + * + * + * @param + * @return + */ protected function rawGet(array $columns = array(), array $clauseProps = array(), $conjunction = 'and'){ ; } + + /** + * + * + * + * + * @param array $columns + * @param array $clauseProps + * @param string $conjunction + * @return Providers\Core\QueryExtender + */ + protected function get(array $columns = array(), array $clauseProps = array(), $conjunction = 'and'){ return $this->builder->select($columns, $clauseProps, $conjunction); } + + /** + * + * + * + * + * @param array $values + * @param array $clauseProps + * @return Providers\Core\QueryExtender + */ + protected function set(array $values = array(), array $clauseProps = array()){ return $this->builder->insert($values, $clauseProps); } + + /** + * + * + * + * + * @param array $columnValues + * @param array $clauseProps + * @param string $conjunction + * @return Providers\Core\QueryExtender + */ + protected function let(array $columnValues = array(), $clauseProps = array(), $conjunction = 'and'){ return $this->builder->update($columnValues, $clauseProps, $conjunction); } + + /** + * + * + * + * + * @param array $columns + * @param array $clauseProps + * @return \Providers\Core\QueryExtender + */ + protected function del(array $columns = array(), $clauseProps = array()){ return $this->builder->delete($columns, $clauseProps); } + + /** + * + * + * + * + * @param void + * @return array + */ + public function getAttributes(){ return array('table' => $this->table, 'key' => $this->primaryKey, 'relations' => $this->relations); } + + /** + * + * + * + * + * @param void + * @return void + */ + + public function bindSchema(){ + + ; + } + + /** + * Retrieves one or more tuples/rows from a Model table + * based on conditions. + * + * + * @param void + * @return void + */ + + public static function whereBy(array $clause){ + + return static::$instance->get(array('*'), $clause)->exec(); + } + + /** + * + * + * + * + * @param + * @return + */ + + public static function fetchAllWith($modelName, array $clause){ + + return static::$instance->get(array('*'), $clause)->with($modelName)->exec(); + } + + /** + * Updates a tuple/row from the Model table using the + * value of the tables' primary key {$id} + * + * + * @param string $id + * @return array + * @api + */ + + public static function updateById($id = ''){ + + $attr = static::$instance->getAttributes(); + $clause = array(); + $clause[$attr['key']] = $id; + + return static::$instance->let(array('*'), $clause)->exec(0); + } + + /** + * Deletes a tuple/row from the Model table using the + * value of the tables' primary key {$id} + * + * + * @param string $id + * @return array + * @api + */ + + public static function removeById($id = ''){ + + $attr = static::$instance->getAttributes(); + $clause = array(); + $clause[$attr['key']] = $id; + + return static::$instance->del(array('*'), $clause)->exec(0); + } + + /** + * Retrieves a tuple/row from the Model table using the + * value of the tables' primary key {$id} + * + * + * @param string $id + * @return array + * @api + */ + + public static function findById($id = ''){ + + $attr = static::$instance->getAttributes(); + $clause = array(); + $clause[$attr['key']] = $id; + + return static::$instance->get(array('*'), $clause)->exec(); + } + + /** + * Retrieves all tuples/rows from the Model table + * + * + * + * @param integer $limit + * @param integer $offset + * @return array + * @api + */ + + public static function fetchAll($limit = -1, $offset = -1){ + + return static::$instance->get(array('*'))->exec($limit, $offset); + } + + /** + * Inserts OR Updates a tuple/row in the Model table + * + * + * + * @param array $tuple + * @return array + */ + + public static function createOrUpdate(array $tuple, array $clause){ + + return static::$instance->set($tuple, $clause)->exec(0); + } + + /** + * Inserts a tuple/row into the Model table + * + * + * + * @param array $tuple + * @return array + */ + + public static function create(array $tuple){ + + return static::$instance->set($tuple)->exec(0); + } + } diff --git a/system/base/components/Router.php b/system/base/components/Router.php index 93df65a..7ed0960 100644 --- a/system/base/components/Router.php +++ b/system/base/components/Router.php @@ -299,7 +299,7 @@ public function getRouteSettings($requestMethod, System $instance, Auth $auth){ // build out models and return models array foreach ($settings['models'] as $modelClass) { if(class_exists($modelClass)){ - $models[$modelClass] = new $modelClass(); + $models[$modelClass] = new $modelClass(); }else{ $models[$modelClass] = NULL; } diff --git a/system/base/components/System.php b/system/base/components/System.php index 49bdc70..1acd734 100644 --- a/system/base/components/System.php +++ b/system/base/components/System.php @@ -143,6 +143,11 @@ public static function on($eventName, callable $eventHandler){ static::$instance->setCustomEvent($eventName, $eventHandler); } + public static function getRequestingDeviceType(){ + + ; + } + public function getFaultedMiddlewares(){ return $this->faultedMiddlewares; diff --git a/system/base/providers/Core/App.php b/system/base/providers/Core/App.php index d26ef9b..4a19998 100644 --- a/system/base/providers/Core/App.php +++ b/system/base/providers/Core/App.php @@ -193,7 +193,7 @@ public function installENVService(array $ENVCONFIG){ public function getOS(){ - return $this->os; + return $this->os; } /** @@ -206,22 +206,17 @@ public function getOS(){ public function getHost($appendChar = ''){ - return $this->apphost . $appendChar; + return $this->apphost . $appendChar; } /** * Sets up the connection for the database. * * - * @param string $env_path - * @return void + * @param void + * @return array */ - public function setDBConnection($env_path){ - - $this->dbservice->connect($env_path); - } - public function getCookieQueue(){ return $this->cookieQueue; @@ -257,9 +252,9 @@ public function initHTTPResolver(){ $auth = $this->getInstance('Auth'); - $this->resolver->draftRouteHandler($router->getMethod()); + $this->resolver->draftRouteHandler($router->getMethod()); - return $this->resolver->handleCurrentRoute($router, $system, $auth); + return $this->resolver->handleCurrentRoute($router, $system, $auth); } @@ -275,7 +270,7 @@ public function cacheModelInstances(array $models){ if($this->hasCachedModels === FALSE){ - $this->dbservice->setModelsToBuilder($models); + $this->dbservice->bindSchema($models); $this->hasCachedModels = TRUE; @@ -297,6 +292,19 @@ public function exposeEnvironment($root){ } + /** + * + * + * + * @param + * @return + */ + + public function getBuilder(array $attribs){ + + return $this->dbservice->getBuilder($attribs); + } + /** * Retrives the error reporter. * @@ -331,20 +339,24 @@ public function getRemoteErrorReporter(){ } /** - * Registers all core components for the application. - * + * Registers all core components and ancillary + * services for the application. * * @param void * @return void */ public function registerCoreComponents(){ + + $this->dbservice->connect($GLOBALS['env']['app.path.base'] . '.env'); + + $this->jheaders->installConfig($this->envservice->getConfig("app_security")); + /* * Setup all Singletons for the application */ // @TODO: later, try to do the below in a loop! it probably will be a much cleaner code - $this->jheaders->installConfig($this->envservice->getConfig("app_security")); $this->instances['Logger'] = Logger::createInstance(); $this->instances['System'] = System::createInstance(); diff --git a/system/base/providers/Core/HTTPResolver.php b/system/base/providers/Core/HTTPResolver.php index 778a960..35e7739 100644 --- a/system/base/providers/Core/HTTPResolver.php +++ b/system/base/providers/Core/HTTPResolver.php @@ -1,5 +1,12 @@ currentController = NULL; @@ -22,12 +45,30 @@ public function __construct(){ } + /** + * + * + * + * + * @param string $method + * @return void + */ + public function draftRouteHandler($method){ $this->resolverMethod = $method; } + /** + * + * + * + * + * @param string $url + * @return array + */ + private function getResolverURIParts($url){ $pathname = preg_replace('/^\/|\/$/', '', $url); @@ -36,18 +77,47 @@ private function getResolverURIParts($url){ } + /** + * + * + * + * + * @param void + * @return string $resolverMethod + */ + public function getResolverMethod(){ return $this->resolverMethod; } + /** + * + * + * + * + * @param Router $router + * @param System $sys + * @param Auth $auth + * @return mixed + */ + public function handleCurrentRoute(Router $router, System $sys, Auth $auth){ $uri = Request::uri(); if(preg_match('/\.(?:[a-z]{1,5})$/i', $uri)){ + + /* if the requested file (or route) is a real file on disk, + * then serve it using PHP (Apache/Nginx not involved) + */ + if(file_exists(realpath($uri))){ + /* + * This serves back the file from disk to the client + * -- can be very slow though + */ return $router->fromDisk($uri); } } diff --git a/system/base/providers/Core/InputManager.php b/system/base/providers/Core/InputManager.php index bc3f2e0..5718127 100644 --- a/system/base/providers/Core/InputManager.php +++ b/system/base/providers/Core/InputManager.php @@ -1,18 +1,36 @@ array(), 'files'=>NULL); + /** + * Constructor + * + * + * @param void + * + */ + public function __construct(array $httpInput = array(), array $uploadConfig = array()){ $this->maxUploadSize = $uploadConfig['max_upload_size']; @@ -68,6 +110,16 @@ public function __construct(array $httpInput = array(), array $uploadConfig = ar } + /** + * + * + * + * + * @param array $upload_path_map + * @param array $errors + * @return array $results + */ + public function uploadFiles(array $upload_path_map, array &$errors){ $upload_base_dir = realpath($GLOBALS['env']['app.path.upload']); @@ -259,6 +311,15 @@ public function uploadFiles(array $upload_path_map, array &$errors){ return $results; } + /** + * + * + * + * + * @param string $file_tmp_name + * @return bool + */ + private function isAllowedBinary($file_tmp_name){ $file_type = exif_imagetype($file_tmp_name); @@ -266,16 +327,43 @@ private function isAllowedBinary($file_tmp_name){ return in_array($file_type, $this->binaryFilesAllowed); } + /** + * + * + * + * + * @param array $field_keys + * @return array + */ + public function getFields(array $field_keys = array()){ return $this->filterInput($field_keys, $this->httpInput['fields']); } + /** + * + * + * + * + * @param array $field_keys + * @return array + */ + public function getFiles(array $file_keys = array()){ return $this->filterInput($file_keys, $this->httpInput['files']); } + /** + * + * + * + * + * @param array $vars + * @return array $parameters + */ + private function filterInput(array $vars = array(), $parameters){ $filtered = NULL; if(count($vars) > 0){ diff --git a/system/base/providers/Core/QueryBuilder.php b/system/base/providers/Core/QueryBuilder.php index f1d39e1..9fa6a46 100644 --- a/system/base/providers/Core/QueryBuilder.php +++ b/system/base/providers/Core/QueryBuilder.php @@ -3,6 +3,7 @@ /*! * Jollof (c) Copyright 2016 * + * * {QueryBuilder.php} * */ diff --git a/system/base/providers/Core/QueryExtender.php b/system/base/providers/Core/QueryExtender.php index f1f1b74..ae037d3 100644 --- a/system/base/providers/Core/QueryExtender.php +++ b/system/base/providers/Core/QueryExtender.php @@ -11,7 +11,7 @@ use \PDO; use \UnexpectedValueException; -use \Model; +use \ReflectionClass; class QueryExtender { @@ -59,6 +59,7 @@ class QueryExtender { /** * @var array - all operators which are allowed in a query */ + protected $allowedOperators = array( 'LIKE', 'BETWEEN', @@ -69,6 +70,12 @@ class QueryExtender { '<' ); + /** + * @var ReflectionClass - for obtaining the object of a class with just the class name + */ + + protected $reflClass; + /** * Constructor * @@ -88,6 +95,8 @@ public function __construct(PDO $connection, array $paramTypes){ $this->connection = $connection; + $this->reflClass = NULL; + } /** @@ -97,7 +106,7 @@ public function __construct(PDO $connection, array $paramTypes){ * @param array $columns - columns for SELECT query * @param array $clauseProps - column/value pairs for WHERE clause in SELECT query * @param string $conjunction - conjunction for WHERE clause - * @return \Providers\Tools\QueryExtender - + * @return \Providers\Core\QueryExtender - * * @throws UnexpectedValueException */ @@ -139,7 +148,7 @@ public function get($columns, $clauseProps, $conjunction){ * @param array $columns - columns for INSERT query * @param array $values - values for the columns for INSERT query * @param array $clauseProps - columns for update where duplicate key exists - * @return \Providers\Tools\QueryExtender - + * @return \Providers\Core\QueryExtender - */ public function set($columns, $values, $clauseProps = array()){ @@ -167,7 +176,7 @@ public function set($columns, $values, $clauseProps = array()){ * @param array $columnValues - column/value pairs for SET clause in UPDATE query * @param array $clauseProps - column/value pairs for WHERE clause in UPDATE query * @param string $conjunction - conjunction for WHERE clause - * @return \Providers\Tools\QueryExtender - + * @return \Providers\Core\QueryExtender - * * @throws UnexpectedValueException */ @@ -201,7 +210,7 @@ public function let($columnValues, $clauseProps, $conjunction){ * * @param $columns - columns * @param $clauseProps - column/value pairs for WHERE clause in UPDATE query - * @return \Providers\Tools\QueryExtender - + * @return \Providers\Core\QueryExtender - */ public function del($columns){ @@ -220,12 +229,12 @@ public function del($columns){ * * * - * @param \Model $model - + * @param string $modelName - * @param string $joinType - - * @return \Providers\Tools\QueryExtender - + * @return \Providers\Core\QueryExtender - */ - public function with(Model $model, $joinType = 'inner'){ + public function with($modelName, $joinType = 'inner'){ if(strlen($this->queryString) == 0){ @@ -234,7 +243,11 @@ public function with(Model $model, $joinType = 'inner'){ $joinType = strtoupper($joinType); - $__atrribs = $model->getAttributes(); + $this->reflClass = new ReflectionClass($modelName); + + $object = $reflClass->newInstanceWithoutContructor(); + + $__atrribs = $object->getAttributes(); # wrap the table name with quotes like so: `table` $table = $this->wrap($this->attribs['table']); @@ -243,7 +256,7 @@ public function with(Model $model, $joinType = 'inner'){ $parentReference = $this->wrap($this->attribs['key']); - $childReference = $this->wrap($__attribs['relations'][get_class($model)]); + $childReference = $this->wrap($__attribs['relations'][$modelName]); # start building query string -> {INNER|LEFT|OUTER RIGHT|OUTER LEFT|CROSS} JOIN $joinExp = " {$joinType} JOIN `{$joinTable}` ON `{$table}`.`{$parentReference}` = `{$joinTable}`.`{$childReference}`"; @@ -260,7 +273,7 @@ public function with(Model $model, $joinType = 'inner'){ * * @param array $colums - ordering columns for SELECT query * @param bool $ascending - flag to specify sorting preference - * @return \Providers\Tools\QueryExtender - + * @return \Providers\Core\QueryExtender - */ public function ordering(array $columns, $ascending = FALSE){ @@ -290,7 +303,7 @@ public function setAttributes($schemaAttribs){ * * * @param array $clauseProps - - * @return \Providers\Tools\QueryExtender - + * @return \Providers\Core\QueryExtender - * @throws \UnexpectedvalueException - */ @@ -318,7 +331,7 @@ public function having(array $clauseProps){ * * * @param array $columns - - * @return \Providers\Tools\QueryExtender - + * @return \Providers\Core\QueryExtender - */ public function grouping(array $columns){ diff --git a/system/base/providers/Services/DBService.php b/system/base/providers/Services/DBService.php index 65eda62..211923c 100644 --- a/system/base/providers/Services/DBService.php +++ b/system/base/providers/Services/DBService.php @@ -1,7 +1,9 @@ PDO::PARAM_INT, "str" => PDO::PARAM_STR ); + /** + * @var array + */ + protected $config; - protected $isMSREnabled; // Master-Slave Replication + /** + * @var array + */ + + protected $mObjects = array(); - public function __construct(array $config){ + /** + * @var array + */ + + protected $isMSREnabled; // Master-Slave Replication - {Not In Use Now} + + /** + * Constructor + * + * + * @param array $config + * + */ + + public function __construct(array $config){ $this->config = $config; @@ -46,45 +85,105 @@ public function __construct(array $config){ } - $this->builders = array(); - } + /** + * Destructor + * + * + * @param void + * + */ + public function __destruct(){ # this will be used to disconnect from DB automatically - // $this->disconnect(); + if($this->hasConnection()){ + + $this->disconnect(); + + } } + /** + * Clone + * + * + * @param void + * + */ + public function __clone(){ } - public function setModelsToBuilder(&$models){ + /** + * + * + * + * + * @param array $models + * @return void + */ + + public function bindSchema($models){ foreach($models as $model){ - $builder = new Builder($this->getConnection(), $this->getParamTypes()); + $model->bindSchema(); - $builder->setAttributes($model->getAttributes()); + } + } - $model->setBuilder($builder); + /** + * Retrieves the query parameter types for a given PDO + * connection which can either be an [integer] or a [string] + * + * + * @param void + * @return array $param_types + */ - } + protected function getParamTypes(){ + + return $this->param_types; } - private function getParamTypes(){ + /** + * Establishes a valid database connection + * + * + * + * @param string $env_file + * @return void + */ - return $this->param_types; - } + protected function connect($env_file = ''){ + + if($this->hasConnection()){ + + /* do not try to connect to the DB if + we already have an active connection */ + return; + } + + if(empty($env_file) || !isset($env_file)){ + + return; + + }else{ + + if(!file_exists($env_file)){ - public function connect($env_file){ + return; + } + } - $engines = $this->config['engines']; + $engines = $this->config['engines']; - $engine = $engines['mysql']; + $engine = $engines['mysql']; - $settings = file($env_file); + $settings = file($env_file); foreach ($settings as $line){ $split = explode('=', $line); @@ -93,10 +192,6 @@ public function connect($env_file){ } } - if($this->connectionHandle !== NULL){ - return; // do not try to connect to the DB if we already have an active connection - } - try { $this->connectionString = 'mysql:host=' . $engine['hostname'] . $this->connectionString; @@ -116,14 +211,83 @@ public function connect($env_file){ } } - private function disconnect(){ + /** + * Checks if a valid database connection has been made + * + * + * + * @param void + * @return bool + */ + + protected function hasConnection(){ + + return ($this->connectionHandle !== NULL); + } + + /** + * Retrieves the builder for a given [Model] object + * using its' attributes [table-name, primary-key, relations] + * + * + * @param array $modelAttributes + * @return \Providers\Core\QueryBuilder $builder; + */ + + public function getBuilder(array $modelAttributes){ + + $db_connection = $this->getConnection(); + + $p_types = $this->getParamTypes(); + + $table = (!array_key_exists('table', $modelAttributes))?: $modelAttributes['table']; + + if(empty($table) || !isset($table)){ + + return NULL; + } + + if(is_null($db_collection)){ + + throw new \Exception("No Database Connection Found, .env File Probably Missing"); + + } + + $builder = $this->builders[$table] = new Builder($db_collection, $p_types); + + $builder->setAttributes($modelAttributes); + + return $builder; + } + + /** + * Manually destroys the database connection object + * and the cached builder objects + * + * + * @param void + * @return void + */ + + protected function disconnect(){ - // disconnect PDO connection + $this->builders = array(); + + $this->connectionHandle = NULL; } - private function getConnection(){ + /** + * Retrieves the database connection object from memory + * + * + * + * @param void + * @return array $connectionHandle + */ + + protected function getConnection(){ - return $this->connectionHandle; + return $this->connectionHandle; } }