From 550e93747c5bcd4ef564ef28fc8dde2b7238685b Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Tue, 12 Apr 2016 23:31:39 +0600 Subject: [PATCH] Added B8Framework to project --- B8Framework/.gitignore | 6 + B8Framework/README.md | 11 + B8Framework/b8/Application.php | 126 ++++ B8Framework/b8/Cache.php | 38 ++ B8Framework/b8/Cache/ApcCache.php | 115 ++++ B8Framework/b8/Cache/RequestCache.php | 90 +++ B8Framework/b8/Config.php | 176 +++++ B8Framework/b8/Controller.php | 116 ++++ B8Framework/b8/Controller/RestController.php | 246 +++++++ B8Framework/b8/Database.php | 149 ++++ B8Framework/b8/Database/CodeGenerator.php | 153 +++++ .../CodeGenerator/BaseControllerTemplate.html | 22 + .../CodeGenerator/BaseModelTemplate.html | 268 ++++++++ .../CodeGenerator/BaseStoreTemplate.html | 101 +++ .../CodeGenerator/ControllerTemplate.html | 20 + .../Database/CodeGenerator/ModelTemplate.html | 18 + .../Database/CodeGenerator/StoreTemplate.html | 18 + B8Framework/b8/Database/Generator.php | 428 ++++++++++++ B8Framework/b8/Database/Map.php | 258 +++++++ B8Framework/b8/Exception/HttpException.php | 24 + .../HttpException/BadRequestException.php | 10 + .../HttpException/ForbiddenException.php | 10 + .../HttpException/NotAuthorizedException.php | 10 + .../HttpException/NotFoundException.php | 10 + .../HttpException/ServerErrorException.php | 8 + .../HttpException/ValidationException.php | 10 + B8Framework/b8/Form.php | 44 ++ B8Framework/b8/Form/ControlGroup.php | 7 + B8Framework/b8/Form/Element.php | 118 ++++ B8Framework/b8/Form/Element/Button.php | 19 + B8Framework/b8/Form/Element/Checkbox.php | 48 ++ B8Framework/b8/Form/Element/CheckboxGroup.php | 8 + B8Framework/b8/Form/Element/Csrf.php | 28 + B8Framework/b8/Form/Element/Email.php | 18 + B8Framework/b8/Form/Element/Hidden.php | 9 + B8Framework/b8/Form/Element/Password.php | 19 + B8Framework/b8/Form/Element/Radio.php | 8 + B8Framework/b8/Form/Element/Select.php | 21 + B8Framework/b8/Form/Element/Submit.php | 21 + B8Framework/b8/Form/Element/Text.php | 14 + B8Framework/b8/Form/Element/TextArea.php | 26 + B8Framework/b8/Form/Element/Url.php | 18 + B8Framework/b8/Form/FieldSet.php | 114 ++++ B8Framework/b8/Form/Input.php | 124 ++++ B8Framework/b8/Form/View/Button.phtml | 1 + B8Framework/b8/Form/View/Checkbox.phtml | 15 + B8Framework/b8/Form/View/CheckboxGroup.phtml | 11 + B8Framework/b8/Form/View/ControlGroup.phtml | 6 + B8Framework/b8/Form/View/Csrf.phtml | 1 + B8Framework/b8/Form/View/FieldSet.phtml | 9 + B8Framework/b8/Form/View/Form.phtml | 6 + B8Framework/b8/Form/View/Hidden.phtml | 1 + B8Framework/b8/Form/View/Radio.phtml | 18 + B8Framework/b8/Form/View/Select.phtml | 17 + B8Framework/b8/Form/View/Text.phtml | 13 + B8Framework/b8/Form/View/TextArea.phtml | 13 + B8Framework/b8/Http/Request.php | 149 ++++ B8Framework/b8/Http/Response.php | 132 ++++ B8Framework/b8/Http/Response/JsonResponse.php | 30 + .../b8/Http/Response/RedirectResponse.php | 27 + B8Framework/b8/Http/Router.php | 123 ++++ B8Framework/b8/HttpClient.php | 188 +++++ B8Framework/b8/Image.php | 149 ++++ B8Framework/b8/Model.php | 224 ++++++ B8Framework/b8/Registry.php | 91 +++ B8Framework/b8/Store.php | 379 +++++++++++ B8Framework/b8/Store/Factory.php | 65 ++ B8Framework/b8/Type/Cache.php | 17 + B8Framework/b8/Type/RestUser.php | 8 + B8Framework/b8/View.php | 86 +++ B8Framework/b8/View/Helper/Format.php | 11 + B8Framework/b8/View/Template.php | 544 +++++++++++++++ B8Framework/b8/View/UserView.php | 14 + B8Framework/composer.json | 22 + B8Framework/phpci.yml | 12 + B8Framework/tests/CacheTest.php | 40 ++ B8Framework/tests/CodeGenerationTest.php | 641 ++++++++++++++++++ B8Framework/tests/DatabaseGenerationTest.php | 80 +++ B8Framework/tests/DatabaseTest.php | 54 ++ B8Framework/tests/FormTest.php | 196 ++++++ B8Framework/tests/HttpClientTest.php | 66 ++ B8Framework/tests/HttpExceptionTest.php | 115 ++++ B8Framework/tests/RegistryTest.php | 82 +++ B8Framework/tests/ViewTest.php | 141 ++++ .../data/generation/ArrayPropertyModel.php | 18 + .../tests/data/generation/TestUser.php | 13 + .../tests/data/generation/models/BadModel.php | 18 + .../tests/data/generation/models/Dos.php | 24 + .../tests/data/generation/models/Tres.php | 27 + .../tests/data/generation/models/Uno.php | 28 + .../tests/data/generation/models/ignore.file | 0 .../data/generation/update_models/Dos.php | 24 + .../data/generation/update_models/Tres.php | 32 + .../data/generation/update_models/Uno.php | 27 + B8Framework/tests/data/view/Form/form.phtml | 1 + B8Framework/tests/data/view/format.phtml | 1 + B8Framework/tests/data/view/simple.phtml | 1 + B8Framework/tests/data/view/vars.phtml | 1 + composer.json | 8 +- composer.lock | 138 ++-- 100 files changed, 7192 insertions(+), 71 deletions(-) create mode 100755 B8Framework/.gitignore create mode 100755 B8Framework/README.md create mode 100755 B8Framework/b8/Application.php create mode 100755 B8Framework/b8/Cache.php create mode 100755 B8Framework/b8/Cache/ApcCache.php create mode 100755 B8Framework/b8/Cache/RequestCache.php create mode 100755 B8Framework/b8/Config.php create mode 100755 B8Framework/b8/Controller.php create mode 100755 B8Framework/b8/Controller/RestController.php create mode 100755 B8Framework/b8/Database.php create mode 100755 B8Framework/b8/Database/CodeGenerator.php create mode 100755 B8Framework/b8/Database/CodeGenerator/BaseControllerTemplate.html create mode 100755 B8Framework/b8/Database/CodeGenerator/BaseModelTemplate.html create mode 100755 B8Framework/b8/Database/CodeGenerator/BaseStoreTemplate.html create mode 100755 B8Framework/b8/Database/CodeGenerator/ControllerTemplate.html create mode 100755 B8Framework/b8/Database/CodeGenerator/ModelTemplate.html create mode 100755 B8Framework/b8/Database/CodeGenerator/StoreTemplate.html create mode 100755 B8Framework/b8/Database/Generator.php create mode 100755 B8Framework/b8/Database/Map.php create mode 100755 B8Framework/b8/Exception/HttpException.php create mode 100755 B8Framework/b8/Exception/HttpException/BadRequestException.php create mode 100755 B8Framework/b8/Exception/HttpException/ForbiddenException.php create mode 100755 B8Framework/b8/Exception/HttpException/NotAuthorizedException.php create mode 100755 B8Framework/b8/Exception/HttpException/NotFoundException.php create mode 100755 B8Framework/b8/Exception/HttpException/ServerErrorException.php create mode 100755 B8Framework/b8/Exception/HttpException/ValidationException.php create mode 100755 B8Framework/b8/Form.php create mode 100755 B8Framework/b8/Form/ControlGroup.php create mode 100755 B8Framework/b8/Form/Element.php create mode 100755 B8Framework/b8/Form/Element/Button.php create mode 100755 B8Framework/b8/Form/Element/Checkbox.php create mode 100755 B8Framework/b8/Form/Element/CheckboxGroup.php create mode 100755 B8Framework/b8/Form/Element/Csrf.php create mode 100755 B8Framework/b8/Form/Element/Email.php create mode 100755 B8Framework/b8/Form/Element/Hidden.php create mode 100755 B8Framework/b8/Form/Element/Password.php create mode 100755 B8Framework/b8/Form/Element/Radio.php create mode 100755 B8Framework/b8/Form/Element/Select.php create mode 100755 B8Framework/b8/Form/Element/Submit.php create mode 100755 B8Framework/b8/Form/Element/Text.php create mode 100755 B8Framework/b8/Form/Element/TextArea.php create mode 100755 B8Framework/b8/Form/Element/Url.php create mode 100755 B8Framework/b8/Form/FieldSet.php create mode 100755 B8Framework/b8/Form/Input.php create mode 100755 B8Framework/b8/Form/View/Button.phtml create mode 100755 B8Framework/b8/Form/View/Checkbox.phtml create mode 100755 B8Framework/b8/Form/View/CheckboxGroup.phtml create mode 100755 B8Framework/b8/Form/View/ControlGroup.phtml create mode 100755 B8Framework/b8/Form/View/Csrf.phtml create mode 100755 B8Framework/b8/Form/View/FieldSet.phtml create mode 100755 B8Framework/b8/Form/View/Form.phtml create mode 100755 B8Framework/b8/Form/View/Hidden.phtml create mode 100755 B8Framework/b8/Form/View/Radio.phtml create mode 100755 B8Framework/b8/Form/View/Select.phtml create mode 100755 B8Framework/b8/Form/View/Text.phtml create mode 100755 B8Framework/b8/Form/View/TextArea.phtml create mode 100755 B8Framework/b8/Http/Request.php create mode 100755 B8Framework/b8/Http/Response.php create mode 100755 B8Framework/b8/Http/Response/JsonResponse.php create mode 100755 B8Framework/b8/Http/Response/RedirectResponse.php create mode 100755 B8Framework/b8/Http/Router.php create mode 100755 B8Framework/b8/HttpClient.php create mode 100755 B8Framework/b8/Image.php create mode 100755 B8Framework/b8/Model.php create mode 100755 B8Framework/b8/Registry.php create mode 100755 B8Framework/b8/Store.php create mode 100755 B8Framework/b8/Store/Factory.php create mode 100755 B8Framework/b8/Type/Cache.php create mode 100755 B8Framework/b8/Type/RestUser.php create mode 100755 B8Framework/b8/View.php create mode 100755 B8Framework/b8/View/Helper/Format.php create mode 100755 B8Framework/b8/View/Template.php create mode 100755 B8Framework/b8/View/UserView.php create mode 100755 B8Framework/composer.json create mode 100755 B8Framework/phpci.yml create mode 100755 B8Framework/tests/CacheTest.php create mode 100755 B8Framework/tests/CodeGenerationTest.php create mode 100755 B8Framework/tests/DatabaseGenerationTest.php create mode 100755 B8Framework/tests/DatabaseTest.php create mode 100755 B8Framework/tests/FormTest.php create mode 100755 B8Framework/tests/HttpClientTest.php create mode 100755 B8Framework/tests/HttpExceptionTest.php create mode 100755 B8Framework/tests/RegistryTest.php create mode 100755 B8Framework/tests/ViewTest.php create mode 100755 B8Framework/tests/data/generation/ArrayPropertyModel.php create mode 100755 B8Framework/tests/data/generation/TestUser.php create mode 100755 B8Framework/tests/data/generation/models/BadModel.php create mode 100755 B8Framework/tests/data/generation/models/Dos.php create mode 100755 B8Framework/tests/data/generation/models/Tres.php create mode 100755 B8Framework/tests/data/generation/models/Uno.php create mode 100755 B8Framework/tests/data/generation/models/ignore.file create mode 100755 B8Framework/tests/data/generation/update_models/Dos.php create mode 100755 B8Framework/tests/data/generation/update_models/Tres.php create mode 100755 B8Framework/tests/data/generation/update_models/Uno.php create mode 100755 B8Framework/tests/data/view/Form/form.phtml create mode 100755 B8Framework/tests/data/view/format.phtml create mode 100755 B8Framework/tests/data/view/simple.phtml create mode 100755 B8Framework/tests/data/view/vars.phtml diff --git a/B8Framework/.gitignore b/B8Framework/.gitignore new file mode 100755 index 00000000..d3aa3c1e --- /dev/null +++ b/B8Framework/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +._.DS_Store +Thumbs.db +.idea +composer.lock +vendor diff --git a/B8Framework/README.md b/B8Framework/README.md new file mode 100755 index 00000000..a6651294 --- /dev/null +++ b/B8Framework/README.md @@ -0,0 +1,11 @@ +b8 Framework +============ +[![Build Status](https://travis-ci.org/Block8/b8framework.png?branch=master)](https://travis-ci.org/Block8/b8framework) + + +*b8 framework* is a lightweight, simple framework for high-throughput PHP applications. It does not enforce a specific type of application, nor particularly an application structure, it simply helps you to build things. + +We've used the framework to build web sites, APIs, and web applications, each handling tens of millions of requests a month. + + +*b8 framework repository is _not quite_ ready for general consumption yet. We have a tool to help you generate all of your project CRUD for you, which still needs to be included. This will be done over the next few days.* diff --git a/B8Framework/b8/Application.php b/B8Framework/b8/Application.php new file mode 100755 index 00000000..7c9c5f96 --- /dev/null +++ b/B8Framework/b8/Application.php @@ -0,0 +1,126 @@ +config = $config; + $this->response = new Http\Response(); + + if (!is_null($request)) { + $this->request = $request; + } else { + $this->request = new Http\Request(); + } + + $this->router = new Http\Router($this, $this->request, $this->config); + + if (method_exists($this, 'init')) { + $this->init(); + } + } + + public function handleRequest() + { + $this->route = $this->router->dispatch(); + + if (!empty($this->route['callback'])) { + $callback = $this->route['callback']; + + if (!$callback($this->route, $this->response)) { + return $this->response; + } + } + + $action = lcfirst($this->toPhpName($this->route['action'])); + + if (!$this->getController()->hasAction($action)) { + throw new NotFoundException('Controller ' . $this->toPhpName($this->route['controller']) . ' does not have action ' . $action); + } + + return $this->getController()->handleAction($action, $this->route['args']); + } + + /** + * @return \b8\Controller + */ + public function getController() + { + if (empty($this->controller)) { + $namespace = $this->toPhpName($this->route['namespace']); + $controller = $this->toPhpName($this->route['controller']); + $controllerClass = $this->config->get('b8.app.namespace') . '\\' . $namespace . '\\' . $controller . 'Controller'; + $this->controller = $this->loadController($controllerClass); + } + + return $this->controller; + } + + protected function loadController($class) + { + $controller = new $class($this->config, $this->request, $this->response); + $controller->init(); + + return $controller; + } + + protected function controllerExists($route) + { + $namespace = $this->toPhpName($route['namespace']); + $controller = $this->toPhpName($route['controller']); + + $controllerClass = $this->config->get('b8.app.namespace') . '\\' . $namespace . '\\' . $controller . 'Controller'; + + return class_exists($controllerClass); + } + + public function isValidRoute($route) + { + if ($this->controllerExists($route)) { + return true; + } + + return false; + } + + protected function toPhpName($string) + { + $string = str_replace('-', ' ', $string); + $string = ucwords($string); + $string = str_replace(' ', '', $string); + + return $string; + } +} diff --git a/B8Framework/b8/Cache.php b/B8Framework/b8/Cache.php new file mode 100755 index 00000000..506c6491 --- /dev/null +++ b/B8Framework/b8/Cache.php @@ -0,0 +1,38 @@ +isEnabled()) { + return $default; + } + + $success = false; + $rtn = apc_fetch($key, $success); + + if (!$success) { + $rtn = $default; + } + + return $rtn; + } + + /** + * Add an item to the cache: + */ + public function set($key, $value = null, $ttl = 0) + { + if (!$this->isEnabled()) { + return false; + } + + return apc_store($key, $value, $ttl); + } + + /** + * Remove an item from the cache: + */ + public function delete($key) + { + if (!$this->isEnabled()) { + return false; + } + + return apc_delete($key); + } + + /** + * Check if an item is in the cache: + */ + public function contains($key) + { + if (!$this->isEnabled()) { + return false; + } + + return apc_exists($key); + } + + /** + * Short-hand syntax for get() + * @see Config::get() + */ + public function __get($key) + { + return $this->get($key, null); + } + + /** + * Short-hand syntax for set() + * @see Config::set() + */ + public function __set($key, $value = null) + { + return $this->set($key, $value); + } + + /** + * Is set + */ + public function __isset($key) + { + return $this->contains($key); + } + + /** + * Unset + */ + public function __unset($key) + { + $this->delete($key); + } +} \ No newline at end of file diff --git a/B8Framework/b8/Cache/RequestCache.php b/B8Framework/b8/Cache/RequestCache.php new file mode 100755 index 00000000..d8ed3035 --- /dev/null +++ b/B8Framework/b8/Cache/RequestCache.php @@ -0,0 +1,90 @@ +contains($key) ? $this->data[$key] : $default; + } + + /** + * Add an item to the cache: + */ + public function set($key, $value = null, $ttl = 0) + { + $this->data[$key] = $value; + + return $this; + } + + /** + * Remove an item from the cache: + */ + public function delete($key) + { + if ($this->contains($key)) { + unset($this->data[$key]); + } + + return $this; + } + + /** + * Check if an item is in the cache: + */ + public function contains($key) + { + return array_key_exists($key, $this->data); + } + + /** + * Short-hand syntax for get() + * @see Config::get() + */ + public function __get($key) + { + return $this->get($key, null); + } + + /** + * Short-hand syntax for set() + * @see Config::set() + */ + public function __set($key, $value = null) + { + return $this->set($key, $value); + } + + /** + * Is set + */ + public function __isset($key) + { + return $this->contains($key); + } + + /** + * Unset + */ + public function __unset($key) + { + $this->delete($key); + } +} \ No newline at end of file diff --git a/B8Framework/b8/Config.php b/B8Framework/b8/Config.php new file mode 100755 index 00000000..b4bd080c --- /dev/null +++ b/B8Framework/b8/Config.php @@ -0,0 +1,176 @@ +setArray($settings); + } elseif (is_string($settings) && file_exists($settings)) { + $this->loadYaml($settings); + } + } + + public function loadYaml($yamlFile) + { + // Path to a YAML file. + $parser = new YamlParser(); + $yaml = file_get_contents($yamlFile); + $config = (array)$parser->parse($yaml); + + if (empty($config)) { + return; + } + + $this->setArray($config); + } + + /** + * Get a configuration value by key, returning a default value if not set. + * @param $key string + * @param $default mixed + * @return mixed + */ + public function get($key, $default = null) + { + $keyParts = explode('.', $key); + $selected = $this->config; + + $i = -1; + $last_part = count($keyParts) - 1; + while ($part = array_shift($keyParts)) { + $i++; + + if (!array_key_exists($part, $selected)) { + return $default; + } + + if ($i === $last_part) { + return $selected[$part]; + } else { + $selected = $selected[$part]; + } + } + + return $default; + } + + /** + * Set a value by key. + * @param $key string + * @param $value mixed + */ + public function set($key, $value = null) + { + $this->config[$key] = $value; + } + + /** + * Set an array of values. + */ + public function setArray($array) + { + self::deepMerge($this->config, $array); + } + + /** + * Short-hand syntax for get() + * @see Config::get() + */ + public function __get($key) + { + return $this->get($key); + } + + /** + * Short-hand syntax for set() + * @see Config::set() + */ + public function __set($key, $value = null) + { + return $this->set($key, $value); + } + + /** + * Is set + */ + public function __isset($key) + { + return isset($this->config[$key]); + } + + /** + * Unset + */ + public function __unset($key) + { + unset($this->config[$key]); + } + + /** + * Deeply merge the $target array onto the $source array. + * The $source array will be modified! + * @param array $source + * @param array $target + */ + public static function deepMerge(&$source, $target) + { + if (count($source) === 0) { + $source = $target; + return; + } + foreach ($target as $target_key => $target_value) { + if (isset($source[$target_key])) { + if (!is_array($source[$target_key]) && !is_array($target_value)) { + // Neither value is an array, overwrite + $source[$target_key] = $target_value; + + } elseif (is_array($source[$target_key]) && is_array($target_value)) { + // Both are arrays, deep merge them + self::deepMerge($source[$target_key], $target_value); + + } elseif (is_array($source[$target_key])) { + // Source is the array, push target value + $source[$target_key][] = $target_value; + } else { + // Target is the array, push source value and copy back + $target_value[] = $source[$target_key]; + $source[$target_key] = $target_value; + } + } else { + // No merge required, just set the value + $source[$target_key] = $target_value; + } + } + } + + public function getArray() + { + return $this->config; + } +} diff --git a/B8Framework/b8/Controller.php b/B8Framework/b8/Controller.php new file mode 100755 index 00000000..269209fd --- /dev/null +++ b/B8Framework/b8/Controller.php @@ -0,0 +1,116 @@ +config = $config; + $this->request = $request; + $this->response = $response; + } + + public function hasAction($name) + { + if (method_exists($this, $name)) { + return true; + } + + if (method_exists($this, '__call')) { + return true; + } + + return false; + } + + /** + * Handles an action on this controller and returns a Response object. + * @return b8\Http\Response + */ + public function handleAction($action, $actionParams) + { + return call_user_func_array(array($this, $action), $actionParams); + } + + /** + * Initialise the controller. + */ + abstract public function init(); + + /** + * Get a hash of incoming request parameters ($_GET, $_POST) + * + * @return array + */ + public function getParams() + { + return $this->request->getParams(); + } + + /** + * Get a specific incoming request parameter. + * + * @param $key + * @param mixed $default Default return value (if key does not exist) + * + * @return mixed + */ + public function getParam($key, $default = null) + { + return $this->request->getParam($key, $default); + } + + /** + * Change the value of an incoming request parameter. + * @param $key + * @param $value + */ + public function setParam($key, $value) + { + return $this->request->setParam($key, $value); + } + + /** + * Remove an incoming request parameter. + * @param $key + */ + public function unsetParam($key) + { + return $this->request->unsetParam($key); + } +} \ No newline at end of file diff --git a/B8Framework/b8/Controller/RestController.php b/B8Framework/b8/Controller/RestController.php new file mode 100755 index 00000000..93b61925 --- /dev/null +++ b/B8Framework/b8/Controller/RestController.php @@ -0,0 +1,246 @@ +response->setContent($response); + + return $this->response; + } + + public function setActiveUser(RestUser $user) + { + $this->activeUser = $user; + } + + public function getActiveUser() + { + return $this->activeUser; + } + + public function index() + { + if(!$this->activeUser->checkPermission('canRead', $this->_resourceName)) + { + throw new HttpException\ForbiddenException('You do not have permission do this.'); + } + + $this->where = $this->_parseWhere(); + $this->limit = is_null($this->limit) ? $this->getParam('limit', 25) : $this->limit; + $this->offset = is_null($this->offset) ? $this->getParam('offset', 0) : $this->offset; + $this->order = is_null($this->order) || !count($this->order) ? $this->getParam('order', array()) : $this->order; + $this->group = is_null($this->group) || !count($this->group) ? $this->getParam('group', null) : $this->group; + $this->searchType = $this->getParam('searchType', self::SEARCHTYPE_AND); + + $store = Factory::getStore($this->_modelName); + $data = $store->getWhere($this->where, $this->limit, $this->offset, $this->joins, $this->order, $this->manualJoins, $this->group, $this->manualWheres, $this->searchType); + + $rtn = array( + 'debug' => array( + 'where' => $this->where, + 'searchType' => $this->searchType, + ), + 'limit' => $this->limit, + 'offset' => $this->offset, + 'total' => $data['count'], + 'items' => array() + ); + + foreach($data['items'] as $item) + { + $rtn['items'][] = $item->toArray($this->arrayDepth); + } + + return $rtn; + } + + /** + * + */ + protected function _parseWhere() + { + $clauses = array( + 'fuzzy' => 'like', + 'gt' => '>', + 'gte' => '>=', + 'lt' => '<', + 'lte' => '<=', + 'neq' => '!=', + 'between' => 'between' + ); + + $where = $this->getParam('where', array()); + $where = array_merge($where, $this->where); + + if(count($where)) + { + foreach($where as &$value) + { + if(!is_array($value) || !isset($value['operator'])) + { + if(is_array($value) && count($value) == 1) + { + $value = array_shift($value); + } + + $value = array( + 'operator' => '=', + 'value' => $value + ); + } + } + + foreach($clauses as $clause => $operator) + { + $fields = $this->getParam($clause, array()); + + if(count($clause)) + { + if(!is_array($fields)) + { + $fields = array($fields); + } + foreach($fields as $field) + { + if(isset($where[$field])) + { + $where[$field]['operator'] = $operator; + if($operator == 'like') + { + $where[$field]['value'] = str_replace(' ', '%', $where[$field]['value']); + } + } + } + } + } + } + + return $where; + } + + public function get($key) + { + if(!$this->activeUser->checkPermission('canRead', $this->_resourceName)) + { + throw new HttpException\ForbiddenException('You do not have permission do this.'); + } + + $rtn = Factory::getStore($this->_modelName)->getByPrimaryKey($key); + + if(is_object($rtn) && method_exists($rtn, 'toArray')) + { + $rtn = $rtn->toArray($this->arrayDepth); + } + + return array(strtolower($this->_modelName) => $rtn); + } + + public function put($key) + { + if(!$this->activeUser->checkPermission('canEdit', $this->_resourceName)) + { + throw new HttpException\ForbiddenException('You do not have permission do this.'); + } + + $store = Factory::getStore($this->_modelName); + + if($obj = $store->getByPrimaryKey($key)) + { + $obj->setValues($this->getParams()); + $rtn = $store->save($obj); + + return array(strtolower($this->_modelName) => $rtn->toArray($this->arrayDepth)); + } + else + { + return null; + } + } + + public function post() + { + if(!$this->activeUser->checkPermission('canCreate', $this->_resourceName)) + { + throw new HttpException\ForbiddenException('You do not have permission do this.'); + } + + $store = Factory::getStore($this->_modelName); + + $modelClass = $this->_modelClass; + $obj = new $modelClass(); + $obj->setValues($this->getParams()); + $rtn = $store->save($obj); + + return array(strtolower($this->_modelName) => $rtn->toArray($this->arrayDepth)); + } + + public function delete($key) + { + if(!$this->activeUser->checkPermission('canDelete', $this->_resourceName)) + { + throw new HttpException\ForbiddenException('You do not have permission do this.'); + } + + $store = Factory::getStore($this->_modelName); + + try + { + if($obj = $store->getByPrimaryKey($key)) + { + $store->delete($obj); + return array('deleted' => true); + } + } + catch(\Exception $ex) + { + } + + return array('deleted' => false); + } +} \ No newline at end of file diff --git a/B8Framework/b8/Database.php b/B8Framework/b8/Database.php new file mode 100755 index 00000000..284ab770 --- /dev/null +++ b/B8Framework/b8/Database.php @@ -0,0 +1,149 @@ + array(), 'write' => array()); + protected static $connections = array('read' => null, 'write' => null); + protected static $details = array(); + protected static $lastUsed = array('read' => null, 'write' => null); + + /** + * @deprecated + */ + public static function setReadServers($read) + { + $config = Config::getInstance(); + + $settings = $config->get('b8.database', array()); + $settings['servers']['read'] = $read; + $config->set('b8.database', $settings); + } + + /** + * @deprecated + */ + public static function setWriteServers($write) + { + $config = Config::getInstance(); + + $settings = $config->get('b8.database', array()); + $settings['servers']['write'] = $write; + $config->set('b8.database', $settings); + } + + /** + * @deprecated + */ + public static function setDetails($database, $username, $password) + { + $config = Config::getInstance(); + $settings = $config->get('b8.database', array()); + $settings['name'] = $database; + $settings['username'] = $username; + $settings['password'] = $password; + $config->set('b8.database', $settings); + } + + protected static function init() + { + $config = Config::getInstance(); + $settings = $config->get('b8.database', array()); + self::$servers['read'] = $settings['servers']['read']; + self::$servers['write'] = $settings['servers']['write']; + self::$details['db'] = $settings['name']; + self::$details['user'] = $settings['username']; + self::$details['pass'] = $settings['password']; + self::$initialised = true; + } + + /** + * @param string $type + * + * @return \b8\Database + * @throws \Exception + */ + public static function getConnection($type = 'read') + { + if (!self::$initialised) { + self::init(); + } + + // If the connection hasn't been used for 5 minutes, force a reconnection: + if (!is_null(self::$lastUsed[$type]) && (time() - self::$lastUsed[$type]) > 300) { + self::$connections[$type] = null; + } + + if(is_null(self::$connections[$type])) + { + if (is_array(self::$servers[$type])) { + // Shuffle, so we pick a random server: + $servers = self::$servers[$type]; + shuffle($servers); + } else { + // Only one server was specified + $servers = array(self::$servers[$type]); + } + + $connection = null; + + // Loop until we get a working connection: + while(count($servers)) + { + // Pull the next server: + $server = array_shift($servers); + + // Try to connect: + try + { + $connection = new self('mysql:host=' . $server . ';dbname=' . self::$details['db'], + self::$details['user'], + self::$details['pass'], + array( + \PDO::ATTR_PERSISTENT => false, + \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, + \PDO::ATTR_TIMEOUT => 2, + \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'', + )); + } + catch(\PDOException $ex) + { + $connection = false; + } + + // Opened a connection? Break the loop: + if($connection) + { + break; + } + } + + // No connection? Oh dear. + if(!$connection && $type == 'read') + { + throw new \Exception('Could not connect to any ' . $type . ' servers.'); + } + + self::$connections[$type] = $connection; + } + + self::$lastUsed[$type] = time(); + return self::$connections[$type]; + } + + public function getDetails() + { + return self::$details; + } + + public static function reset() + { + self::$connections = array('read' => null, 'write' => null); + self::$lastUsed = array('read' => null, 'write' => null); + self::$initialised = false; + } +} diff --git a/B8Framework/b8/Database/CodeGenerator.php b/B8Framework/b8/Database/CodeGenerator.php new file mode 100755 index 00000000..7cc13576 --- /dev/null +++ b/B8Framework/b8/Database/CodeGenerator.php @@ -0,0 +1,153 @@ +_db = $db; + $this->_ns = $namespaces; + $this->_path = $path; + $this->_map = new Map($this->_db); + $this->_tables = $this->_map->generate(); + $this->_counts = $includeCountQueries; + } + + protected function getNamespace($modelName) + { + return array_key_exists($modelName, $this->_ns) ? $this->_ns[$modelName] : $this->_ns['default']; + } + + public function getPath($namespace) + { + return array_key_exists($namespace, $this->_path) ? $this->_path[$namespace] : $this->_path['default']; + } + + public function generateModels() + { + print PHP_EOL . 'GENERATING MODELS' . PHP_EOL . PHP_EOL; + + foreach($this->_tables as $tableName => $table) + { + $namespace = $this->getNamespace($table['php_name']); + $modelPath = $this->getPath($namespace) . str_replace('\\', '/', $namespace) . '/Model/'; + $basePath = $modelPath . 'Base/'; + $modelFile = $modelPath . $table['php_name'] . '.php'; + $baseFile = $basePath . $table['php_name'] . 'Base.php'; + + if (!is_dir($basePath)) { + @mkdir($basePath, 0777, true); + } + + $model = $this->_processTemplate($tableName, $table, 'ModelTemplate'); + $base = $this->_processTemplate($tableName, $table, 'BaseModelTemplate'); + + print '-- ' . $table['php_name'] . PHP_EOL; + + if(!is_file($modelFile)) + { + print '-- -- Writing new Model' . PHP_EOL; + file_put_contents($modelFile, $model); + } + + print '-- -- Writing base Model' . PHP_EOL; + file_put_contents($baseFile, $base); + } + } + + public function generateStores() + { + print PHP_EOL . 'GENERATING STORES' . PHP_EOL . PHP_EOL; + + foreach($this->_tables as $tableName => $table) + { + $namespace = $this->getNamespace($table['php_name']); + $storePath = $this->getPath($namespace) . str_replace('\\', '/', $namespace) . '/Store/'; + $basePath = $storePath . 'Base/'; + $storeFile = $storePath . $table['php_name'] . 'Store.php'; + $baseFile = $basePath . $table['php_name'] . 'StoreBase.php'; + + if (!is_dir($basePath)) { + @mkdir($basePath, 0777, true); + } + + $model = $this->_processTemplate($tableName, $table, 'StoreTemplate'); + $base = $this->_processTemplate($tableName, $table, 'BaseStoreTemplate'); + + print '-- ' . $table['php_name'] . PHP_EOL; + + if(!is_file($storeFile)) + { + print '-- -- Writing new Store' . PHP_EOL; + file_put_contents($storeFile, $model); + } + + print '-- -- Writing base Store' . PHP_EOL; + file_put_contents($baseFile, $base); + } + } + + public function generateControllers() + { + print PHP_EOL . 'GENERATING CONTROLLERS' . PHP_EOL . PHP_EOL; + + @mkdir($this->_path . 'Controller/Base/', 0777, true); + + foreach($this->_tables as $tableName => $table) + { + $namespace = $this->getNamespace($table['php_name']); + $controllerPath = $this->getPath($namespace) . str_replace('\\', '/', $namespace) . '/Controller/'; + $basePath = $controllerPath . 'Base/'; + $controllerFile = $controllerPath . $table['php_name'] . 'Controller.php'; + $baseFile = $basePath . $table['php_name'] . 'ControllerBase.php'; + + if (!is_dir($basePath)) { + @mkdir($basePath, 0777, true); + } + + $model = $this->_processTemplate($tableName, $table, 'ControllerTemplate'); + $base = $this->_processTemplate($tableName, $table, 'BaseControllerTemplate'); + + print '-- ' . $table['php_name'] . PHP_EOL; + + if(!is_file($controllerFile)) + { + print '-- -- Writing new Controller' . PHP_EOL; + file_put_contents($controllerFile, $model); + } + + print '-- -- Writing base Controller' . PHP_EOL; + file_put_contents($baseFile, $base); + } + } + + protected function _processTemplate($tableName, $table, $template) + { + $tpl = Template::createFromFile($template, B8_PATH . 'Database/CodeGenerator/'); + $tpl->appNamespace = $this->getNamespace($table['php_name']); + $tpl->name = $tableName; + $tpl->table = $table; + $tpl->counts = $this->_counts; + $tpl->addFunction('get_namespace', function($args, $view) { + return $this->getNamespace($view->getVariable($args['model'])); + }); + + return $tpl->render(); + } +} \ No newline at end of file diff --git a/B8Framework/b8/Database/CodeGenerator/BaseControllerTemplate.html b/B8Framework/b8/Database/CodeGenerator/BaseControllerTemplate.html new file mode 100755 index 00000000..68871652 --- /dev/null +++ b/B8Framework/b8/Database/CodeGenerator/BaseControllerTemplate.html @@ -0,0 +1,22 @@ + null, +{/loop} + ); + + /** + * @var array + */ + protected $getters = array( + // Direct property getters: +{loop table.columns} + '{@item.name}' => 'get{@item.php_name}', +{/loop} + + // Foreign key getters: +{loop table.relationships.toOne} + '{@item.php_name}' => 'get{@item.php_name}', +{/loop} + ); + + /** + * @var array + */ + protected $setters = array( + // Direct property setters: +{loop table.columns} + '{@item.name}' => 'set{@item.php_name}', +{/loop} + + // Foreign key setters: +{loop table.relationships.toOne} + '{@item.php_name}' => 'set{@item.php_name}', +{/loop} + ); + + /** + * @var array + */ + public $columns = array( +{loop table.columns} + '{@item.name}' => array( + 'type' => '{@item.type}', +{if item.length} + 'length' => {@item.length}, +{/if} +{if item.null} + 'nullable' => true, +{/if} +{if item.is_primary_key} + 'primary_key' => true, +{/if} +{if item.auto} + 'auto_increment' => true, +{/if} +{if item.default_is_null} + 'default' => null, +{/if} +{ifnot item.default_is_null} +{if item.default} + 'default' => {if item.default.isNumeric}{@item.default}{/if}{ifnot item.default.isNumeric}'{@item.default}'{/ifnot}, +{/if} +{/ifnot} + ), +{/loop} + ); + + /** + * @var array + */ + public $indexes = array( +{loop table.indexes} + '{@item.name}' => array({if item.unique}'unique' => true, {/if}'columns' => '{@item.columns}'), +{/loop} + ); + + /** + * @var array + */ + public $foreignKeys = array( +{loop table.relationships.toOne} + '{@item.fk_name}' => array( + 'local_col' => '{@item.from_col}', + 'update' => '{@item.fk_update}', + 'delete' => '{@item.fk_delete}', + 'table' => '{@item.table}', + 'col' => '{@item.col}' + ), +{/loop} + ); + +{loop table.columns} + /** + * Get the value of {@item.php_name} / {@item.name}. + * +{if item.validate_int} + * @return int +{/if}{if item.validate_string} + * @return string +{/if}{if item.validate_float} + * @return float +{/if}{if item.validate_date} + * @return \DateTime +{/if} + */ + public function get{@item.php_name}() + { + $rtn = $this->data['{@item.name}']; + +{if item.validate_date} + if (!empty($rtn)) { + $rtn = new \DateTime($rtn); + } + +{/if} + return $rtn; + } + +{/loop} +{loop table.columns} + /** + * Set the value of {@item.php_name} / {@item.name}. + * +{if item.validate_null} + * Must not be null. +{/if}{if item.validate_int} + * @param $value int +{/if}{if item.validate_string} + * @param $value string +{/if}{if item.validate_float} + * @param $value float +{/if}{if item.validate_date} + * @param $value \DateTime +{/if} + */ + public function set{@item.php_name}($value) + { +{if item.validate_null} + $this->_validateNotNull('{@item.php_name}', $value); +{/if} +{if item.validate_int} + $this->_validateInt('{@item.php_name}', $value); +{/if} +{if item.validate_string} + $this->_validateString('{@item.php_name}', $value); +{/if} +{if item.validate_float} + $this->_validateFloat('{@item.php_name}', $value); +{/if} +{if item.validate_date} + $this->_validateDate('{@item.php_name}', $value); +{/if} + + if ($this->data['{@item.name}'] === $value) { + return; + } + + $this->data['{@item.name}'] = $value; + + $this->_setModified('{@item.name}'); + } + +{/loop}{loop table.relationships.toOne} + /** + * Get the {@item.table_php_name} model for this {@parent.table.php_name} by {@item.col_php}. + * + * @uses \{@parent.appNamespace}\Store\{@item.table_php_name}Store::getBy{@item.col_php}() + * @uses \{@parent.appNamespace}\Model\{@item.table_php_name} + * @return \{@parent.appNamespace}\Model\{@item.table_php_name} + */ + public function get{@item.php_name}() + { + $key = $this->get{@item.from_col_php}(); + + if (empty($key)) { + return null; + } + + $cacheKey = 'Cache.{@item.table_php_name}.' . $key; + $rtn = $this->cache->get($cacheKey, null); + + if (empty($rtn)) { + $rtn = Factory::getStore('{@item.table_php_name}', '{get_namespace model: item.table_php_name}')->getBy{@item.col_php}($key); + $this->cache->set($cacheKey, $rtn); + } + + return $rtn; + } + + /** + * Set {@item.php_name} - Accepts an ID, an array representing a {@item.table_php_name} or a {@item.table_php_name} model. + * + * @param $value mixed + */ + public function set{@item.php_name}($value) + { + // Is this an instance of {@item.table_php_name}? + if ($value instanceof \{@parent.appNamespace}\Model\{@item.table_php_name}) { + return $this->set{@item.php_name}Object($value); + } + + // Is this an array representing a {@item.table_php_name} item? + if (is_array($value) && !empty($value['{@item.col}'])) { + return $this->set{@item.from_col_php}($value['{@item.col}']); + } + + // Is this a scalar value representing the ID of this foreign key? + return $this->set{@item.from_col_php}($value); + } + + /** + * Set {@item.php_name} - Accepts a {@item.table_php_name} model. + * + * @param $value \{@parent.appNamespace}\Model\{@item.table_php_name} + */ + public function set{@item.php_name}Object(\{@parent.appNamespace}\Model\{@item.table_php_name} $value) + { + return $this->set{@item.from_col_php}($value->get{@item.col_php}()); + } + +{/loop}{loop table.relationships.toMany} + /** + * Get {@item.table_php} models by {@item.from_col_php} for this {@parent.table.php_name}. + * + * @uses \{@parent.appNamespace}\Store\{@item.table_php}Store::getBy{@item.from_col_php}() + * @uses \{@parent.appNamespace}\Model\{@item.table_php} + * @return \{@parent.appNamespace}\Model\{@item.table_php}[] + */ + public function get{@item.php_name}() + { + return Factory::getStore('{@item.table_php}', '{get_namespace model: item.table_php_name}')->getBy{@item.from_col_php}($this->get{@item.col_php}()); + } + +{/loop}} diff --git a/B8Framework/b8/Database/CodeGenerator/BaseStoreTemplate.html b/B8Framework/b8/Database/CodeGenerator/BaseStoreTemplate.html new file mode 100755 index 00000000..de651549 --- /dev/null +++ b/B8Framework/b8/Database/CodeGenerator/BaseStoreTemplate.html @@ -0,0 +1,101 @@ +getBy{@table.primary_key.php_name}($value, $useConnection); + } +{/if} +{ifnot table.primary_key} + + public function getByPrimaryKey($value, $useConnection = 'read') + { + throw new \Exception('getByPrimaryKey is not implemented for this store, as the table has no primary key.'); + } +{/ifnot} +{loop table.columns} +{if item.unique_indexed} + + public function getBy{@item.php_name}($value, $useConnection = 'read') + { + if (is_null($value)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM `{@parent.name}` WHERE `{@item.name}` = :{@item.name} LIMIT 1'; + $stmt = Database::getConnection($useConnection)->prepare($query); + $stmt->bindValue(':{@item.name}', $value); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new {@parent.table.php_name}($data); + } + } + + return null; + } +{/if} +{if item.many_indexed} + + public function getBy{@item.php_name}($value, $limit = 1000, $useConnection = 'read') + { + if (is_null($value)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + +{if counts} + $query = 'SELECT COUNT(*) AS cnt FROM `{@parent.name}` WHERE `{@item.name}` = :{@item.name}'; + $stmt = Database::getConnection($useConnection)->prepare($query); + $stmt->bindValue(':{@item.name}', $value); + + if ($stmt->execute()) { + $res = $stmt->fetch(\PDO::FETCH_ASSOC); + $count = (int)$res['cnt']; + } else { + $count = 0; + } +{/if} + + $query = 'SELECT * FROM `{@parent.name}` WHERE `{@item.name}` = :{@item.name} LIMIT :limit'; + $stmt = Database::getConnection($useConnection)->prepare($query); + $stmt->bindValue(':{@item.name}', $value); + $stmt->bindValue(':limit', (int)$limit, \PDO::PARAM_INT); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new {@parent.table.php_name}($item); + }; + $rtn = array_map($map, $res); + + {ifnot counts}$count = count($rtn);{/ifnot} + + return array('items' => $rtn, 'count' => $count); + } else { + return array('items' => array(), 'count' => 0); + } + } +{/if} +{/loop} +} diff --git a/B8Framework/b8/Database/CodeGenerator/ControllerTemplate.html b/B8Framework/b8/Database/CodeGenerator/ControllerTemplate.html new file mode 100755 index 00000000..61e94afe --- /dev/null +++ b/B8Framework/b8/Database/CodeGenerator/ControllerTemplate.html @@ -0,0 +1,20 @@ +_db = $db; + $this->_ns = $namespace; + $this->_path = $path; + $this->_map = new Map($this->_db); + $this->_tables = $this->_map->generate(); + } + + public function generate() + { + error_reporting(E_ERROR & E_WARNING); + $di = new \DirectoryIterator($this->_path); + + $this->_todo = array( + 'drop_fk' => array(), + 'drop_index'=> array(), + 'create' => array(), + 'alter' => array(), + 'add_index' => array(), + 'add_fk' => array(), + ); + + foreach($di as $file) + { + if($file->isDot()) + { + continue; + } + $fileName = explode('.', $file->getBasename()); + if ($fileName[count($fileName)-1] != 'php') + { + continue; + } + + $modelName = '\\' . $this->_ns . '\\Model\\Base\\' . str_replace('.php', '', $file->getFilename()); + + require_once($this->_path . $file->getFilename()); + $model = new $modelName(); + $columns = $model->columns; + $indexes = $model->indexes; + $foreignKeys = $model->foreignKeys; + $tableName = $model->getTableName(); + + if(!array_key_exists($tableName, $this->_tables)) + { + $this->_createTable($tableName, $columns, $indexes, $foreignKeys); + continue; + } + else + { + $table = $this->_tables[$tableName]; + $this->_updateColumns($tableName, $table, $columns); + $this->_updateRelationships($tableName, $table, $foreignKeys); + $this->_updateIndexes($tableName, $table, $indexes); + } + } + + print 'DROP FK: ' . count($this->_todo['drop_fk']) . PHP_EOL; + print 'DROP INDEX: ' . count($this->_todo['drop_index']) . PHP_EOL; + print 'CREATE TABLE: ' . count($this->_todo['create']) . PHP_EOL; + print 'ALTER TABLE: ' . count($this->_todo['alter']) . PHP_EOL; + print 'ADD INDEX: ' . count($this->_todo['add_index']) . PHP_EOL; + print 'ADD FK: ' . count($this->_todo['add_fk']) . PHP_EOL; + + + $order = array_keys($this->_todo); + + while($group = array_shift($order)) + { + if(!isset($this->_todo[$group]) || !is_array($this->_todo[$group]) || !count($this->_todo[$group])) + { + continue; + } + + foreach($this->_todo[$group] as $query) + { + try + { + //print $query . PHP_EOL; + $this->_db->query($query); + } + catch(\Exception $ex) + { + print 'FAILED TO EXECUTE: ' . $query . PHP_EOL; + print $ex->getMessage().PHP_EOL.PHP_EOL; + } + } + } + } + + protected function _createTable($tbl, $cols, $idxs, $fks) + { + $defs = array(); + $pks = array(); + foreach($cols as $colName => $def) + { + $add = '`' . $colName . '` ' . $def['type']; + + switch($def['type']) + { + case 'text': + case 'longtext': + case 'mediumtext': + case 'date': + case 'datetime': + case 'float': + $add .= ''; + break; + + default: + $add .= !empty($def['length']) ? '(' . $def['length'] . ')' : ''; + break; + } + + if(empty($def['nullable']) || !$def['nullable']) + { + $add .= ' NOT NULL '; + } + + if(!empty($def['default'])) + { + $add .= ' DEFAULT ' . (is_numeric($def['default']) ? $def['default'] : '\'' . $def['default'] . '\''); + } + + if(!empty($def['auto_increment']) && $def['auto_increment']) + { + $add .= ' AUTO_INCREMENT '; + } + + if(!empty($def['primary_key']) && $def['primary_key']) + { + $pks[] = '`' . $colName . '`'; + } + + $defs[] = $add; + } + + if(count($pks)) + { + $defs[] = 'PRIMARY KEY (' . implode(', ', $pks) . ')'; + } + + $stmt = 'CREATE TABLE `' . $tbl . '` (' . PHP_EOL; + $stmt .= implode(", \n", $defs); + + $stmt .= PHP_EOL . ') ENGINE=InnoDB DEFAULT CHARSET=utf8'; + $stmt .= PHP_EOL; + + $this->_todo['create'][] = $stmt; + + foreach($idxs as $name => $idx) + { + $this->_addIndex($tbl, $name, $idx); + } + + foreach($fks as $name => $fk) + { + $this->_addFk($tbl, $name, $fk); + } + } + + protected function _updateColumns($tableName, $table, $columns) + { + $currentColumns = $table['columns']; + + while($column = array_shift($currentColumns)) + { + if(!array_key_exists($column['name'], $columns)) + { + $this->_todo['alter'][$tableName.'.'.$column['name']] = 'ALTER TABLE `' . $tableName . '` DROP COLUMN `' . $column['name'] . '`'; + } + else + { + $model = $columns[$column['name']]; + + $model['nullable'] = !isset($model['nullable']) ? false : $model['nullable']; + $model['default'] = !isset($model['default']) ? false : $model['default']; + $model['auto_increment'] = !isset($model['auto_increment']) ? false : $model['auto_increment']; + $model['primary_key'] = !isset($model['primary_key']) ? false : $model['primary_key']; + $column['is_primary_key'] = !isset($column['is_primary_key']) ? false : $column['is_primary_key']; + + if( $column['type'] != $model['type'] || + ($column['length'] != $model['length'] && !in_array($model['type'], array('text', 'longtext', 'mediumtext', 'date', 'datetime', 'float'))) || + $column['null'] != $model['nullable'] || + $column['default'] != $model['default'] || + $column['auto'] != $model['auto_increment']) + { + $this->_updateColumn($tableName, $column['name'], $column['name'], $model); + } + } + + unset($columns[$column['name']]); + } + + if(count($columns)) + { + foreach($columns as $name => $model) + { + // Check if we're renaming a column: + if(isset($model['rename'])) + { + unset($this->_todo['alter'][$tableName.'.'.$model['rename']]); + $this->_updateColumn($tableName, $model['rename'], $name, $model); + continue; + } + + // New column + $add = '`' . $name . '` ' . $model['type'];; + switch($model['type']) + { + case 'text': + case 'longtext': + case 'mediumtext': + case 'date': + case 'datetime': + case 'float': + $add .= ''; + break; + + default: + $add .= !empty($model['length']) ? '(' . $model['length'] . ')' : ''; + break; + } + + if(empty($model['nullable']) || !$model['nullable']) + { + $add .= ' NOT NULL '; + } + + if(!empty($model['default'])) + { + $add .= ' DEFAULT ' . (is_numeric($model['default']) ? $model['default'] : '\'' . $model['default'] . '\''); + } + + if(!empty($model['auto_increment']) && $model['auto_increment']) + { + $add .= ' AUTO_INCREMENT '; + } + + if(!empty($model['primary_key']) && $model['primary_key'] && !isset($table['indexes']['PRIMARY'])) + { + $add .= ' PRIMARY KEY '; + } + + $this->_todo['alter'][] = 'ALTER TABLE `' . $tableName . '` ADD COLUMN ' . $add; + } + } + } + + protected function _updateColumn($tableName, $prevName, $newName, $model) + { + $add = '`' . $newName . '` ' . $model['type'];; + switch($model['type']) + { + case 'text': + case 'longtext': + case 'mediumtext': + case 'date': + case 'datetime': + case 'float': + $add .= ''; + break; + + default: + $add .= !empty($model['length']) ? '(' . $model['length'] . ')' : ''; + break; + } + + if(empty($model['nullable']) || !$model['nullable']) + { + $add .= ' NOT NULL '; + } + + if(!empty($model['default'])) + { + $add .= ' DEFAULT ' . (is_numeric($model['default']) ? $model['default'] : '\'' . $model['default'] . '\''); + } + + if(!empty($model['auto_increment']) && $model['auto_increment']) + { + $add .= ' AUTO_INCREMENT '; + } + + $this->_todo['alter'][] = 'ALTER TABLE `' . $tableName . '` CHANGE COLUMN `' . $prevName . '` ' . $add; + } + + protected function _updateRelationships($tableName, $table, $foreignKeys) + { + $current = $table['relationships']['toOne']; + + while($foreignKey = array_shift($current)) + { + if(!array_key_exists($foreignKey['fk_name'], $foreignKeys)) + { + $this->_dropFk($tableName, $foreignKey['fk_name']); + } + elseif( $foreignKey['from_col'] != $foreignKeys[$foreignKey['fk_name']]['local_col'] || + $foreignKey['table'] != $foreignKeys[$foreignKey['fk_name']]['table'] || + $foreignKey['col'] != $foreignKeys[$foreignKey['fk_name']]['col'] || + $foreignKey['fk_update'] != $foreignKeys[$foreignKey['fk_name']]['update'] || + $foreignKey['fk_delete'] != $foreignKeys[$foreignKey['fk_name']]['delete']) + { + $this->_alterFk($tableName, $foreignKey['fk_name'], $foreignKeys[$foreignKey['fk_name']]); + } + + unset($foreignKeys[$foreignKey['fk_name']]); + } + + if(count($foreignKeys)) + { + foreach($foreignKeys as $name => $foreignKey) + { + // New column + $this->_addFk($tableName, $name, $foreignKey); + } + } + } + + protected function _updateIndexes($tableName, $table, $indexes) + { + $current = $table['indexes']; + + while($index = array_shift($current)) + { + if(!array_key_exists($index['name'], $indexes)) + { + $this->_dropIndex($tableName, $index['name']); + } + elseif( $index['unique'] != $indexes[$index['name']]['unique'] || + $index['columns'] != $indexes[$index['name']]['columns']) + { + $this->_alterIndex($tableName, $index['name'], $index); + } + + unset($indexes[$index['name']]); + } + + if(count($indexes)) + { + foreach($indexes as $name => $index) + { + if($name == 'PRIMARY') + { + continue; + } + + // New index + $this->_addIndex($tableName, $name, $index); + } + } + } + + protected function _addIndex($table, $name, $idx, $stage = 'add_index') + { + if($name == 'PRIMARY') + { + return; + } + + $q = 'CREATE ' . (isset($idx['unique']) && $idx['unique'] ? 'UNIQUE' : '') . ' INDEX `' . $name . '` ON `' . $table . '` (' . $idx['columns'] . ')'; + + $this->_todo[$stage][] = $q; + } + + protected function _alterIndex($table, $name, $idx, $stage = 'index') + { + $this->_dropIndex($table, $name, $stage); + $this->_addIndex($table, $name, $idx, $stage); + } + + protected function _dropIndex($table, $idxName, $stage = 'drop_index') + { + if($idxName == 'PRIMARY') + { + return; + } + + $q = 'DROP INDEX `' . $idxName . '` ON `' . $table . '`'; + $this->_todo[$stage][] = $q; + } + + protected function _addFk($table, $name, $fk) + { + $q = 'ALTER TABLE `' . $table . '` ADD CONSTRAINT `' . $name . '` FOREIGN KEY (`' . $fk['local_col'] . '`) REFERENCES `'.$fk['table'].'` (`'.$fk['col'].'`)'; + + if(!empty($fk['delete'])) + { + $q .= ' ON DELETE ' . $fk['delete'] . ' '; + } + + if(!empty($fk['update'])) + { + $q .= ' ON UPDATE ' . $fk['update'] . ' '; + } + + $this->_todo['add_fk'][] = $q; + } + + protected function _alterFk($table, $name, $fk) + { + $this->_dropFk($table, $name); + $this->_addFk($table, $name, $fk); + } + + protected function _dropFk($table, $name) + { + $q = 'ALTER TABLE `'.$table.'` DROP FOREIGN KEY `' . $name . '`'; + $this->_todo['drop_fk'][] = $q; + } +} diff --git a/B8Framework/b8/Database/Map.php b/B8Framework/b8/Database/Map.php new file mode 100755 index 00000000..70d3e86d --- /dev/null +++ b/B8Framework/b8/Database/Map.php @@ -0,0 +1,258 @@ +_db = $db; + } + + public function generate() + { + $tables = $this->_getTables(); + + + foreach($tables as $table) + { + $this->_tables[$table] = array(); + $this->_tables[$table]['php_name'] = $this->_generatePhpName($table); + } + + $this->_getRelationships(); + $this->_getColumns(); + $this->_getIndexes(); + + return $this->_tables; + } + + protected function _getTables() + { + $details = $this->_db->getDetails(); + + $rtn = array(); + + foreach($this->_db->query('SHOW TABLES')->fetchAll(\PDO::FETCH_ASSOC) as $tbl) + { + $rtn[] = $tbl['Tables_in_' . $details['db']]; + } + + return $rtn; + } + + protected function _getRelationships() + { + foreach($this->_tables as $table => $t) + { + $res = $this->_db->query('SHOW CREATE TABLE `'.$table.'`')->fetchAll(\PDO::FETCH_ASSOC); + + foreach($res as $r) + { + $str = $r['Create Table']; + + $matches = array(); + if(preg_match_all('/CONSTRAINT\s+\`([a-zA-Z0-9_]+)\`\s+FOREIGN\s+KEY\s+\(\`([a-zA-Z0-9_]+)\`\)\s+REFERENCES\s+\`([a-zA-Z0-9_]+)\`\s+\(\`([a-zA-Z0-9_]+)\`\)(\s+ON (DELETE|UPDATE) (SET NULL|NO ACTION|CASCADE|RESTRICT))?(\s+ON (DELETE|UPDATE) (SET NULL|NO ACTION|CASCADE|RESTRICT))?/', $str, $matches)) + { + for($i = 0; $i < count($matches[0]); $i++) + { + $fromTable = $table; + $fromCol = $matches[2][$i]; + $toTable = $matches[3][$i]; + $toCol = $matches[4][$i]; + $fkName = $matches[1][$i]; + $fk = array(); + + if(isset($matches[6][$i])) + { + $fk[$matches[6][$i]] = $matches[7][$i]; + } + + if(isset($matches[9][$i])) + { + $fk[$matches[9][$i]] = $matches[10][$i]; + } + + $fk['UPDATE'] = empty($fk['UPDATE']) ? '' : $fk['UPDATE']; + $fk['DELETE'] = empty($fk['DELETE']) ? '' : $fk['DELETE']; + + if(isset($this->_tables[$fromTable]) && isset($this->_tables[$toTable])) + { + $phpName = $this->_generateFkName($fromCol, $this->_tables[$fromTable]['php_name']); + + $this->_tables[$fromTable]['relationships']['toOne'][$fromCol] = array('fk_name' => $fkName, 'fk_delete' => $fk['DELETE'], 'fk_update' => $fk['UPDATE'], 'table_php_name' => $this->_tables[$toTable]['php_name'], 'from_col_php' => $this->_generatePhpName($fromCol), 'from_col' => $fromCol, 'php_name' => $phpName, 'table' => $toTable, 'col' => $toCol, 'col_php' => $this->_generatePhpName($toCol)); + + $phpName = $this->_generateFkName($fromCol, $this->_tables[$fromTable]['php_name']) . $this->_tables[$fromTable]['php_name'].'s'; + $this->_tables[$toTable]['relationships']['toMany'][] = array('from_col_php' => $this->_generatePhpName($fromCol), 'php_name' => $phpName, 'thisCol' => $toCol, 'table' => $fromTable, 'table_php' => $this->_generatePhpName($fromTable), 'fromCol' => $fromCol, 'col_php' => $this->_generatePhpName($toCol)); + } + } + } + } + } + } + + protected function _getColumns() + { + foreach($this->_tables as $key => &$val) + { + $cols = array(); + foreach($this->_db->query('DESCRIBE `' . $key . '`')->fetchAll(\PDO::FETCH_ASSOC) as $column) + { + $col = $this->_processColumn(array(), $column, $val); + $cols[$col['name']] = $col; + } + + $val['columns'] = $cols; + } + + } + + protected function _getIndexes() + { + foreach($this->_tables as $key => &$val) + { + $indexes = array(); + + foreach($this->_db->query('SHOW INDEXES FROM `' . $key . '`')->fetchAll(\PDO::FETCH_ASSOC) as $idx) + { + if(!isset($indexes[$idx['Key_name']])) + { + $indexes[$idx['Key_name']] = array(); + $indexes[$idx['Key_name']]['name'] = $idx['Key_name']; + $indexes[$idx['Key_name']]['unique'] = ($idx['Non_unique'] == '0') ? true : false; + $indexes[$idx['Key_name']]['columns'] = array(); + } + + $indexes[$idx['Key_name']]['columns'][$idx['Seq_in_index']] = $idx['Column_name']; + } + + $indexes = array_map(function($idx) + { + ksort($idx['columns']); + $idx['columns'] = implode(', ', $idx['columns']); + + return $idx; + }, $indexes); + + $val['indexes'] = $indexes; + } + } + + protected function _processColumn($col, $column, &$table) + { + $col['name'] = $column['Field']; + $col['php_name']= $this->_generatePhpName($col['name']); + $matches = array(); + + preg_match('/^([a-zA-Z]+)(\()?([0-9\,]+)?(\))?/', $column['Type'], $matches); + + $col['type'] = strtolower($matches[1]); + + if(isset($matches[3])) + { + $col['length'] = $matches[3]; + } + + $col['null'] = strtolower($column['Null']) == 'yes' ? true : false; + $col['auto'] = strtolower($column['Extra']) == 'auto_increment' ? true : false; + + if ($column['Default'] == 'NULL' || is_null($column['Default'])) { + $col['default_is_null'] = true; + } else { + $col['default_is_null'] = false; + $col['default'] = $column['Default']; + } + + if(!empty($column['Key'])) + { + if($column['Key'] == 'PRI') + { + $col['is_primary_key'] = true; + $table['primary_key'] = array('column' => $col['name'], 'php_name' => $col['php_name']); + } + + if($column['Key'] == 'PRI' || $column['Key'] == 'UNI') + { + $col['unique_indexed'] = true; + } + else + { + $col['many_indexed'] = true; + } + } + + $col['validate']= array(); + + if(!$col['null']) + { + $col['validate_null'] = true; + } + + switch($col['type']) + { + case 'tinyint': + case 'smallint': + case 'int': + case 'mediumint': + case 'bigint': + $col['php_type'] = 'int'; + $col['to_php'] = '_sqlToInt'; + $col['validate_int']= true; + break; + + case 'float': + case 'decimal': + $col['php_type'] = 'float'; + $col['to_php'] = '_sqlToFloat'; + $col['validate_float'] = true; + break; + + case 'datetime': + case 'date': + $col['php_type'] = 'DateTime'; + $col['to_php'] = '_sqlToDateTime'; + $col['to_sql'] = '_dateTimeToSql'; + $col['validate_date'] = true; + break; + + case 'varchar': + case 'text': + default: + $col['php_type'] = 'string'; + $col['validate_string'] = true; + break; + } + + return $col; + } + + protected function _generatePhpName($sqlName) + { + $rtn = $sqlName; + $rtn = str_replace('_', ' ', $rtn); + $rtn = ucwords($rtn); + $rtn = str_replace(' ', '', $rtn); + + return $rtn; + } + + protected function _generateFkName($sqlName, $tablePhpName) + { + $fkMethod = substr($sqlName, 0, strripos($sqlName, '_')); + + if(empty($fkMethod)) + { + $fkMethod = (substr(strtolower($sqlName), -2) == 'id') ? substr($sqlName, 0, -2) : $tablePhpName; + } + + $fkMethod = str_replace('_', ' ', $fkMethod); + $fkMethod = ucwords($fkMethod); + $fkMethod = str_replace(' ', '', $fkMethod); + + return $fkMethod; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Exception/HttpException.php b/B8Framework/b8/Exception/HttpException.php new file mode 100755 index 00000000..ad6baa0c --- /dev/null +++ b/B8Framework/b8/Exception/HttpException.php @@ -0,0 +1,24 @@ +errorCode; + } + + public function getStatusMessage() + { + return $this->statusMessage; + } + + public function getHttpHeader() + { + return 'HTTP/1.1 ' . $this->errorCode . ' ' . $this->statusMessage; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Exception/HttpException/BadRequestException.php b/B8Framework/b8/Exception/HttpException/BadRequestException.php new file mode 100755 index 00000000..89b5df96 --- /dev/null +++ b/B8Framework/b8/Exception/HttpException/BadRequestException.php @@ -0,0 +1,10 @@ +_action; + } + + public function setAction($action) + { + $this->_action = $action; + } + + public function getMethod() + { + return $this->_method; + } + + public function setMethod($method) + { + $this->_method = $method; + } + + protected function _onPreRender(View &$view) + { + $view->action = $this->getAction(); + $view->method = $this->getMethod(); + + parent::_onPreRender($view); + } + + public function __toString() + { + return $this->render(); + } +} \ No newline at end of file diff --git a/B8Framework/b8/Form/ControlGroup.php b/B8Framework/b8/Form/ControlGroup.php new file mode 100755 index 00000000..cb20f1f6 --- /dev/null +++ b/B8Framework/b8/Form/ControlGroup.php @@ -0,0 +1,7 @@ +setName($name); + } + } + + public function getName() + { + return $this->_name; + } + + public function setName($name) + { + $this->_name = strtolower(preg_replace('/([^a-zA-Z0-9_\-])/', '', $name)); + return $this; + } + + public function getId() + { + return !$this->_id ? 'element-'.$this->_name : $this->_id; + } + + public function setId($id) + { + $this->_id = $id; + return $this; + } + + public function getLabel() + { + return $this->_label; + } + + public function setLabel($label) + { + $this->_label = $label; + return $this; + } + + public function getClass() + { + return $this->_css; + } + + public function setClass($class) + { + $this->_css = $class; + return $this; + } + + public function getContainerClass() + { + return $this->_ccss; + } + + public function setContainerClass($class) + { + $this->_ccss = $class; + return $this; + } + + public function setParent(Element $parent) + { + $this->_parent = $parent; + return $this; + } + + public function render($viewFile = null) + { + $viewPath = Config::getInstance()->get('b8.view.path'); + + if(is_null($viewFile)) + { + $class = explode('\\', get_called_class()); + $viewFile = end($class); + } + + if(file_exists($viewPath . 'Form/' . $viewFile . '.phtml')) + { + $view = new View('Form/' . $viewFile); + } + else + { + $view = new View($viewFile, B8_PATH . 'Form/View/'); + } + + $view->name = $this->getName(); + $view->id = $this->getId(); + $view->label = $this->getLabel(); + $view->css = $this->getClass(); + $view->ccss = $this->getContainerClass(); + $view->parent = $this->_parent; + + $this->_onPreRender($view); + + return $view->render(); + } + + abstract protected function _onPreRender(View &$view); +} \ No newline at end of file diff --git a/B8Framework/b8/Form/Element/Button.php b/B8Framework/b8/Form/Element/Button.php new file mode 100755 index 00000000..f46472f0 --- /dev/null +++ b/B8Framework/b8/Form/Element/Button.php @@ -0,0 +1,19 @@ +type = 'button'; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Form/Element/Checkbox.php b/B8Framework/b8/Form/Element/Checkbox.php new file mode 100755 index 00000000..07607ebf --- /dev/null +++ b/B8Framework/b8/Form/Element/Checkbox.php @@ -0,0 +1,48 @@ +_checkedValue; + } + + public function setCheckedValue($value) + { + $this->_checkedValue = $value; + } + + public function setValue($value) + { + if(is_bool($value) && $value == true) + { + $this->_value = $this->getCheckedValue(); + $this->_checked = true; + return; + } + + if($value == $this->getCheckedValue()) + { + $this->_value = $this->getCheckedValue(); + $this->_checked = true; + return; + } + + $this->_value = $value; + $this->_checked = false; + } + + public function _onPreRender(View &$view) + { + parent::_onPreRender($view); + $view->checkedValue = $this->getCheckedValue(); + $view->checked = $this->_checked; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Form/Element/CheckboxGroup.php b/B8Framework/b8/Form/Element/CheckboxGroup.php new file mode 100755 index 00000000..e924dfaf --- /dev/null +++ b/B8Framework/b8/Form/Element/CheckboxGroup.php @@ -0,0 +1,8 @@ +_value != $_COOKIE[$this->getName()]) + { + return false; + } + + return true; + } + + protected function _onPreRender(View &$view) + { + parent::_onPreRender($view); + $csrf = md5(microtime(true)); + $view->csrf = $csrf; + setcookie($this->getName(), $csrf); + } +} \ No newline at end of file diff --git a/B8Framework/b8/Form/Element/Email.php b/B8Framework/b8/Form/Element/Email.php new file mode 100755 index 00000000..3913de65 --- /dev/null +++ b/B8Framework/b8/Form/Element/Email.php @@ -0,0 +1,18 @@ +type = 'email'; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Form/Element/Hidden.php b/B8Framework/b8/Form/Element/Hidden.php new file mode 100755 index 00000000..6687b9c5 --- /dev/null +++ b/B8Framework/b8/Form/Element/Hidden.php @@ -0,0 +1,9 @@ +type = 'password'; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Form/Element/Radio.php b/B8Framework/b8/Form/Element/Radio.php new file mode 100755 index 00000000..4afd18fc --- /dev/null +++ b/B8Framework/b8/Form/Element/Radio.php @@ -0,0 +1,8 @@ +_options = $options; + } + + protected function _onPreRender(View &$view) + { + parent::_onPreRender($view); + $view->options = $this->_options; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Form/Element/Submit.php b/B8Framework/b8/Form/Element/Submit.php new file mode 100755 index 00000000..01edbe8b --- /dev/null +++ b/B8Framework/b8/Form/Element/Submit.php @@ -0,0 +1,21 @@ +type = 'submit'; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Form/Element/Text.php b/B8Framework/b8/Form/Element/Text.php new file mode 100755 index 00000000..4f6c92a7 --- /dev/null +++ b/B8Framework/b8/Form/Element/Text.php @@ -0,0 +1,14 @@ +type = 'text'; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Form/Element/TextArea.php b/B8Framework/b8/Form/Element/TextArea.php new file mode 100755 index 00000000..1e623632 --- /dev/null +++ b/B8Framework/b8/Form/Element/TextArea.php @@ -0,0 +1,26 @@ +_rows; + } + + public function setRows($rows) + { + $this->_rows = $rows; + } + + protected function _onPreRender(View &$view) + { + parent::_onPreRender($view); + $view->rows = $this->getRows(); + } +} \ No newline at end of file diff --git a/B8Framework/b8/Form/Element/Url.php b/B8Framework/b8/Form/Element/Url.php new file mode 100755 index 00000000..4cad534f --- /dev/null +++ b/B8Framework/b8/Form/Element/Url.php @@ -0,0 +1,18 @@ +type = 'url'; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Form/FieldSet.php b/B8Framework/b8/Form/FieldSet.php new file mode 100755 index 00000000..a246e0cd --- /dev/null +++ b/B8Framework/b8/Form/FieldSet.php @@ -0,0 +1,114 @@ +_children as $field) + { + if($field instanceof FieldSet) + { + $fieldName = $field->getName(); + + if(empty($fieldName)) + { + $rtn = array_merge($rtn, $field->getValues()); + } + else + { + $rtn[$fieldName] = $field->getValues(); + } + } + elseif($field instanceof Input) + { + if($field->getName()) + { + $rtn[$field->getName()] = $field->getValue(); + } + } + } + + return $rtn; + } + + public function setValues(array $values) + { + foreach($this->_children as $field) + { + if($field instanceof FieldSet) + { + $fieldName = $field->getName(); + + if(empty($fieldName) || !isset($values[$fieldName])) + { + $field->setValues($values); + } + else + { + $field->setValues($values[$fieldName]); + } + } + elseif($field instanceof Input) + { + $fieldName = $field->getName(); + + if(isset($values[$fieldName])) + { + $field->setValue($values[$fieldName]); + } + } + } + } + + public function addField(Element $field) + { + $this->_children[$field->getName()] = $field; + $field->setParent($this); + } + + public function validate() + { + $rtn = true; + + foreach($this->_children as $child) + { + if(!$child->validate()) + { + $rtn = false; + } + } + + return $rtn; + } + + protected function _onPreRender(View &$view) + { + $rendered = array(); + + foreach($this->_children as $child) + { + $rendered[] = $child->render(); + } + + $view->children = $rendered; + } + + public function getChildren() + { + return $this->_children; + } + + public function getChild($fieldName) + { + return $this->_children[$fieldName]; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Form/Input.php b/B8Framework/b8/Form/Input.php new file mode 100755 index 00000000..94a0027e --- /dev/null +++ b/B8Framework/b8/Form/Input.php @@ -0,0 +1,124 @@ +setName($name); + $el->setLabel($label); + $el->setRequired($required); + + return $el; + } + + public function getValue() + { + return $this->_value; + } + + public function setValue($value) + { + $this->_value = $value; + return $this; + } + + public function getRequired() + { + return $this->_required; + } + + public function setRequired($required) + { + $this->_required = (bool)$required; + return $this; + } + + public function getValidator() + { + return $this->_validator; + } + + public function setValidator($validator) + { + if(is_callable($validator) || $validator instanceof \Closure) + { + $this->_validator = $validator; + } + + return $this; + } + + public function getPattern() + { + return $this->_pattern; + } + + public function setPattern($pattern) + { + $this->_pattern = $pattern; + return $this; + } + + public function validate() + { + if($this->getRequired() && empty($this->_value)) + { + $this->_error = $this->getLabel() . ' is required.'; + return false; + } + + if($this->getPattern() && !preg_match('/'.$this->getPattern().'/', $this->_value)) + { + $this->_error = 'Invalid value entered.'; + return false; + } + + $validator = $this->getValidator(); + + if(is_callable($validator)) + { + try + { + call_user_func_array($validator, array($this->_value)); + } + catch(\Exception $ex) + { + $this->_error = $ex->getMessage(); + return false; + } + } + + if ($this->_customError) { + return false; + } + + return true; + } + + public function setError($message) + { + $this->_customError = true; + $this->_error = $message; + return $this; + } + + protected function _onPreRender(View &$view) + { + $view->value = $this->getValue(); + $view->error = $this->_error; + $view->pattern = $this->_pattern; + $view->required = $this->_required; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Form/View/Button.phtml b/B8Framework/b8/Form/View/Button.phtml new file mode 100755 index 00000000..afd5efdb --- /dev/null +++ b/B8Framework/b8/Form/View/Button.phtml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/B8Framework/b8/Form/View/Checkbox.phtml b/B8Framework/b8/Form/View/Checkbox.phtml new file mode 100755 index 00000000..20f060d7 --- /dev/null +++ b/B8Framework/b8/Form/View/Checkbox.phtml @@ -0,0 +1,15 @@ + +
+
+ + + + + + +
+
+ \ No newline at end of file diff --git a/B8Framework/b8/Form/View/CheckboxGroup.phtml b/B8Framework/b8/Form/View/CheckboxGroup.phtml new file mode 100755 index 00000000..09f4021c --- /dev/null +++ b/B8Framework/b8/Form/View/CheckboxGroup.phtml @@ -0,0 +1,11 @@ +
+ + + + +
+ + + +
+
\ No newline at end of file diff --git a/B8Framework/b8/Form/View/ControlGroup.phtml b/B8Framework/b8/Form/View/ControlGroup.phtml new file mode 100755 index 00000000..3bf88b21 --- /dev/null +++ b/B8Framework/b8/Form/View/ControlGroup.phtml @@ -0,0 +1,6 @@ +
+ + + + +
\ No newline at end of file diff --git a/B8Framework/b8/Form/View/Csrf.phtml b/B8Framework/b8/Form/View/Csrf.phtml new file mode 100755 index 00000000..42b0e74b --- /dev/null +++ b/B8Framework/b8/Form/View/Csrf.phtml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/B8Framework/b8/Form/View/FieldSet.phtml b/B8Framework/b8/Form/View/FieldSet.phtml new file mode 100755 index 00000000..fbc1fe75 --- /dev/null +++ b/B8Framework/b8/Form/View/FieldSet.phtml @@ -0,0 +1,9 @@ +
+ + + + + + + +
\ No newline at end of file diff --git a/B8Framework/b8/Form/View/Form.phtml b/B8Framework/b8/Form/View/Form.phtml new file mode 100755 index 00000000..3f85d73e --- /dev/null +++ b/B8Framework/b8/Form/View/Form.phtml @@ -0,0 +1,6 @@ +
+ + + + +
\ No newline at end of file diff --git a/B8Framework/b8/Form/View/Hidden.phtml b/B8Framework/b8/Form/View/Hidden.phtml new file mode 100755 index 00000000..28c4fdbf --- /dev/null +++ b/B8Framework/b8/Form/View/Hidden.phtml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/B8Framework/b8/Form/View/Radio.phtml b/B8Framework/b8/Form/View/Radio.phtml new file mode 100755 index 00000000..8c15983c --- /dev/null +++ b/B8Framework/b8/Form/View/Radio.phtml @@ -0,0 +1,18 @@ +
+ + + + +
+ $lbl): ?> + + + + + + +
+
\ No newline at end of file diff --git a/B8Framework/b8/Form/View/Select.phtml b/B8Framework/b8/Form/View/Select.phtml new file mode 100755 index 00000000..a5dbd957 --- /dev/null +++ b/B8Framework/b8/Form/View/Select.phtml @@ -0,0 +1,17 @@ +
+ + + + +
+ + + + + +
+
\ No newline at end of file diff --git a/B8Framework/b8/Form/View/Text.phtml b/B8Framework/b8/Form/View/Text.phtml new file mode 100755 index 00000000..401db77d --- /dev/null +++ b/B8Framework/b8/Form/View/Text.phtml @@ -0,0 +1,13 @@ +
+ + + + +
+ > + + + + +
+
\ No newline at end of file diff --git a/B8Framework/b8/Form/View/TextArea.phtml b/B8Framework/b8/Form/View/TextArea.phtml new file mode 100755 index 00000000..dfe17e8a --- /dev/null +++ b/B8Framework/b8/Form/View/TextArea.phtml @@ -0,0 +1,13 @@ +
+ + + + +
+ + + + + +
+
\ No newline at end of file diff --git a/B8Framework/b8/Http/Request.php b/B8Framework/b8/Http/Request.php new file mode 100755 index 00000000..af701371 --- /dev/null +++ b/B8Framework/b8/Http/Request.php @@ -0,0 +1,149 @@ +parseInput(); + + $this->data['path'] = $this->getRequestPath(); + $this->data['parts'] = array_values(array_filter(explode('/', $this->data['path']))); + } + + protected function getRequestPath() + { + $path = ''; + + // Start out with the REQUEST_URI: + if (!empty($_SERVER['REQUEST_URI'])) { + $path = $_SERVER['REQUEST_URI']; + } + + if ($_SERVER['SCRIPT_NAME'] != $_SERVER['REQUEST_URI']) { + $scriptPath = str_replace('/index.php', '', $_SERVER['SCRIPT_NAME']); + $path = str_replace($scriptPath, '', $path); + } + + // Remove index.php from the URL if it is present: + $path = str_replace(array('/index.php', 'index.php'), '', $path); + + // Also cut out the query string: + $path = explode('?', $path); + $path = array_shift($path); + + return $path; + } + + /** + * Parse incoming variables, incl. $_GET, $_POST and also reads php://input for PUT/DELETE. + */ + protected function parseInput() + { + $params = $_REQUEST; + + if(!isset($_SERVER['REQUEST_METHOD']) || in_array($_SERVER['REQUEST_METHOD'], array('PUT', 'DELETE'))) + { + $vars = file_get_contents('php://input'); + + if(!is_string($vars) || strlen(trim($vars)) === 0) + { + $vars = ''; + } + + $inputData = array(); + parse_str($vars, $inputData); + + $params = array_merge($params, $inputData); + } + + $this->setParams($params); + } + + /** + * Returns all request parameters. + * @return array + */ + public function getParams() + { + return $this->params; + } + + /** + * Return a specific request parameter, or a default value if not set. + */ + public function getParam($key, $default = null) + { + if (isset($this->params[$key])) { + return $this->params[$key]; + } else { + return $default; + } + } + + /** + * Set or override a request parameter. + */ + public function setParam($key, $value = null) + { + $this->params[$key] = $value; + } + + /** + * Set an array of request parameters. + */ + public function setParams(array $params) + { + $this->params = array_merge($this->params, $params); + } + + /** + * Un-set a specific parameter. + */ + public function unsetParam($key) + { + unset($this->params[$key]); + } + + public function getMethod() + { + return strtoupper($_SERVER['REQUEST_METHOD']); + } + + public function getPath() + { + return $this->data['path']; + } + + public function getPathParts() + { + return $this->data['parts']; + } + + public function isAjax() + { + if (!isset($_SERVER['HTTP_X_REQUESTED_WITH'])) { + return false; + } + + if (strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { + return true; + } + + return false; + } +} diff --git a/B8Framework/b8/Http/Response.php b/B8Framework/b8/Http/Response.php new file mode 100755 index 00000000..2bf09edf --- /dev/null +++ b/B8Framework/b8/Http/Response.php @@ -0,0 +1,132 @@ +data = $createFrom->getData(); + } + } + + public function hasLayout() + { + return !isset($this->data['layout']) ? true : $this->data['layout']; + } + + public function disableLayout() + { + $this->data['layout'] = false; + } + + public function enableLayout() + { + $this->data['layout'] = true; + } + + public function getData() + { + return $this->data; + } + + public function setResponseCode($code) + { + $this->data['code'] = (int)$code; + } + + public function setHeader($key, $val) + { + $this->data['headers'][$key] = $val; + } + + public function clearHeaders() + { + $this->data['headers'] = array(); + } + + public function setContent($content) + { + $this->data['body'] = $content; + } + + public function getContent() + { + return $this->data['body']; + } + + public function flush() + { + $this->sendResponseCode(); + + if (isset($this->data['headers'])) { + foreach ($this->data['headers'] as $header => $val) { + header($header . ': ' . $val, true); + } + } + + return $this->flushBody(); + } + + protected function sendResponseCode() + { + if (!isset($this->data['code'])) { + $this->data['code'] = 200; + } + + switch ($this->data['code']) + { + // 300 class + case 301: + $text = 'Moved Permanently'; + break; + case 302: + $text = 'Moved Temporarily'; + break; + + // 400 class errors + case 400: + $text = 'Bad Request'; + break; + case 401: + $text = 'Not Authorized'; + break; + case 403: + $text = 'Forbidden'; + break; + case 404: + $text = 'Not Found'; + break; + + // 500 class errors + case 500: + $text = 'Internal Server Error'; + break; + + // OK + case 200: + default: + $text = 'OK'; + break; + } + + header('HTTP/1.1 ' . $this->data['code'] . ' ' . $text, true, $this->data['code']); + } + + protected function flushBody() + { + if (isset($this->data['body'])) { + return $this->data['body']; + } + + return ''; + } + + public function __toString() + { + return $this->flush(); + } +} \ No newline at end of file diff --git a/B8Framework/b8/Http/Response/JsonResponse.php b/B8Framework/b8/Http/Response/JsonResponse.php new file mode 100755 index 00000000..25e60b02 --- /dev/null +++ b/B8Framework/b8/Http/Response/JsonResponse.php @@ -0,0 +1,30 @@ +setContent(array()); + $this->setHeader('Content-Type', 'application/json'); + } + + public function hasLayout() + { + return false; + } + + protected function flushBody() + { + if (isset($this->data['body'])) { + return json_encode($this->data['body']); + } + + return json_encode(null); + } +} \ No newline at end of file diff --git a/B8Framework/b8/Http/Response/RedirectResponse.php b/B8Framework/b8/Http/Response/RedirectResponse.php new file mode 100755 index 00000000..fa289278 --- /dev/null +++ b/B8Framework/b8/Http/Response/RedirectResponse.php @@ -0,0 +1,27 @@ +setContent(null); + $this->setResponseCode(302); + } + + public function hasLayout() + { + return false; + } + + public function flush() + { + parent::flush(); + die; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Http/Router.php b/B8Framework/b8/Http/Router.php new file mode 100755 index 00000000..10652f45 --- /dev/null +++ b/B8Framework/b8/Http/Router.php @@ -0,0 +1,123 @@ + '/:controller/:action', 'callback' => null, 'defaults' => array())); + + public function __construct(Application $application, Request $request, Config $config) + { + $this->application = $application; + $this->request = $request; + $this->config = $config; + } + + public function clearRoutes() + { + $this->routes = array(); + } + + /** + * @param string $route Route definition + * @param array $options + * @param callable $callback + * @throws \InvalidArgumentException + */ + public function register($route, $options = array(), $callback = null) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException('$callback must be callable.'); + } + + array_unshift($this->routes, array('route' => $route, 'callback' => $callback, 'defaults' => $options)); + } + + public function dispatch() + { + foreach ($this->routes as $route) { + $pathParts = $this->request->getPathParts(); + + //------- + // Set up default values for everything: + //------- + $thisNamespace = 'Controller'; + $thisController = null; + $thisAction = null; + + if (array_key_exists('namespace', $route['defaults'])) { + $thisNamespace = $route['defaults']['namespace']; + } + + if (array_key_exists('controller', $route['defaults'])) { + $thisController = $route['defaults']['controller']; + } + + if (array_key_exists('action', $route['defaults'])) { + $thisAction = $route['defaults']['action']; + } + + $routeParts = array_filter(explode('/', $route['route'])); + $routeMatches = true; + + while (count($routeParts)) { + $routePart = array_shift($routeParts); + $pathPart = array_shift($pathParts); + + switch ($routePart) { + case ':namespace': + $thisNamespace = !is_null($pathPart) ? $pathPart : $thisNamespace; + break; + case ':controller': + $thisController = !is_null($pathPart) ? $pathPart : $thisController; + break; + case ':action': + $thisAction = !is_null($pathPart) ? $pathPart : $thisAction; + break; + default: + if ($routePart != $pathPart) { + $routeMatches = false; + } + } + + if (!$routeMatches || !count($pathParts)) { + break; + } + } + + $thisArgs = $pathParts; + + if ($routeMatches) { + $route = array('namespace' => $thisNamespace, 'controller' => $thisController, 'action' => $thisAction, 'args' => $thisArgs, 'callback' => $route['callback']); + + if ($this->application->isValidRoute($route)) { + return $route; + } + } + } + + return null; + } +} \ No newline at end of file diff --git a/B8Framework/b8/HttpClient.php b/B8Framework/b8/HttpClient.php new file mode 100755 index 00000000..befec086 --- /dev/null +++ b/B8Framework/b8/HttpClient.php @@ -0,0 +1,188 @@ +get('b8.http.client', array('base_url' => '', 'params' => array())); + $this->_base = $settings['base_url']; + $this->_params = isset($settings['params']) && is_array($settings['params']) ? $settings['params'] : array(); + $this->_headers = array('Content-Type: application/x-www-form-urlencoded'); + + if(!is_null($base)) + { + $this->_base = $base; + } + } + + public function setHeaders(array $headers) + { + $this->_headers = $headers; + } + + public function request($method, $uri, $params = array()) + { + // Clean incoming: + $method = strtoupper($method); + $getParams = $this->_params; + + if($method == 'GET' || $method == 'DELETE') + { + $getParams = array_merge($getParams, $params); + } + else + { + $bodyParams = is_array($params) ? http_build_query($params) : $params; + } + + $getParams = http_build_query($getParams); + + if(substr($uri, 0, 1) != '/' && !empty($this->_base)) + { + $uri = '/' . $uri; + } + + // Build HTTP context array: + $context = array(); + $context['http']['user_agent'] = 'b8/1.0'; + $context['http']['timeout'] = 30; + $context['http']['method'] = $method; + $context['http']['ignore_errors'] = true; + $context['http']['header'] = implode(PHP_EOL, $this->_headers); + + if(in_array($method, array('PUT', 'POST'))) + { + $context['http']['content'] = $bodyParams; + } + + $uri .= '?' . $getParams; + + $context = stream_context_create($context); + $result = file_get_contents($this->_base . $uri, false, $context); + + $res = array(); + $res['headers'] = $http_response_header; + $res['code'] = (int)preg_replace('/HTTP\/1\.[0-1] ([0-9]+)/', '$1', $res['headers'][0]); + $res['success'] = false; + $res['body'] = $this->_decodeResponse($result); + + if($res['code'] >= 200 && $res['code'] < 300) + { + $res['success'] = true; + } + + // Handle JSON responses: + foreach($res['headers'] as $header) + { + if(stripos($header, 'Content-Type') !== false || stripos($header, 'b8-Type') !== false) + { + if(stripos($header, 'application/json') !== false) + { + $res['text_body'] = $res['body']; + $res['body'] = json_decode($res['body'], true); + } + } + } + + return $res; + } + + public function get($uri, $params = array()) + { + return $this->request('GET', $uri, $params); + } + + public function put($uri, $params = array()) + { + return $this->request('PUT', $uri, $params); + } + + public function post($uri, $params = array()) + { + return $this->request('POST', $uri, $params); + } + + public function delete($uri, $params = array()) + { + return $this->request('DELETE', $uri, $params); + } + + protected function _decodeResponse($originalResponse) + { + $response = $originalResponse; + $body = ''; + + do + { + $line = $this->_readChunk($response); + + if($line == PHP_EOL) + { + continue; + } + + $length = hexdec(trim($line)); + + if(!is_int($length) || empty($response) || $line === false || $length < 1) + { + break; + } + + do + { + $data = $this->_readChunk($response, $length); + + // remove the amount received from the total length on the next loop + // it'll attempt to read that much less data + $length -= strlen($data); + + // store in string for later use + $body .= $data; + + // zero or less or end of connection break + if($length <= 0 || empty($response)) + { + break; + } + } + while(true); + } + while(true); + + if(empty($body)) + { + $body = $originalResponse; + } + + return $body; + } + + function _readChunk(&$string, $len = 4096) + { + $rtn = ''; + for($i = 0; $i <= $len; $i++) + { + if(empty($string)) + { + break; + } + + $char = $string[0]; + $string = substr($string, 1); + $rtn .= $char; + + if($char == PHP_EOL) + { + break; + } + } + + return $rtn; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Image.php b/B8Framework/b8/Image.php new file mode 100755 index 00000000..cb9af178 --- /dev/null +++ b/B8Framework/b8/Image.php @@ -0,0 +1,149 @@ +setSource(new \Imagick(self::$sourcePath . $imagePath)); + } + + /** + * @return \Imagick + */ + public function getSource() + { + return $this->source; + } + + /** + * @param \Imagick $image + */ + public function setSource(\Imagick $image) + { + $this->source = $image; + } + + public function render($media, $width, $height, $format = 'jpeg') + { + $cachePath = self::$cachePath . $media['fileId'] . '.' . $width . 'x' . $height . '.' . $format; + + if(file_exists($cachePath) && 0) + { + $output = file_get_contents($cachePath); + } + else + { + $output = $this->doRender($media, $width, $height, $format); + file_put_contents($cachePath, $output); + } + + return $output; + } + + public function doRender($media, $width, $height, $format = 'jpeg') + { + $focal = !empty($media['focal_point']) ? $media['focal_point'] : array(0, 0); + $focalX = (int)$focal[0]; + $focalY = (int)$focal[1]; + + $width = (int)$width; + $height = (int)$height; + + $source = $this->getSource(); + $sourceWidth = $source->getImageWidth(); + $sourceHeight = $source->getImageHeight(); + $sourceRatio = $sourceWidth / $sourceHeight; + $targetRatio = $height != 'auto' ? $width / $height : $sourceRatio; + + $quads = $this->_getQuadrants($sourceWidth, $sourceHeight); + + foreach($quads as $name => $l) + { + if($focalX >= $l[0] && $focalX <= $l[1] && $focalY >= $l[2] && $focalY <= $l[3]) + { + $useQuad = $name; + } + } + + if($sourceRatio <= $targetRatio) + { + $scale = $sourceWidth / $width; + } + else + { + $scale = $sourceHeight / $height; + } + + $resizeWidth = (int)($sourceWidth / $scale); + $resizeHeight = (int)($sourceHeight / $scale); + + if($height == 'auto') + { + $height = $resizeHeight; + } + + $source->scaleImage($resizeWidth, $resizeHeight); + + switch($useQuad) + { + case 'top_left': + $cropX = 0; + $cropY = 0; + break; + + case 'top_right': + $cropX = ($resizeWidth - $width); + $cropY = 0; + break; + + case 'middle_left': + $cropX = 0; + $cropY = ($resizeHeight - $height) / 2; + break; + + case 'middle-right': + $cropX = ($resizeWidth - $width); + $cropY = ($resizeHeight - $height) / 2; + break; + + case 'bottom_left': + $cropX = 0; + $cropY = ($resizeHeight - $height); + break; + + case 'bottom_right': + $cropX = ($resizeWidth - $width); + $cropY = ($resizeHeight - $height); + break; + } + + $source->cropImage($width, $height, $cropX, $cropY); + $source->setImageFormat($format); + + return $source; + } + + protected function _getQuadrants($x, $y) + { + $rtn = array(); + $rtn['top_left'] = array(0, $x / 2, 0, $y / 3); + $rtn['top_right'] = array(($x / 2) + 1, $x, 0, $y / 3); + $rtn['middle_left'] = array(0, $y / 2, ($y / 3)+1, (($y / 3) * 2)); + $rtn['middle_right'] = array(($x / 2) + 1, $x, ($y / 3)+1, (($y / 3) * 2)); + $rtn['bottom_left'] = array(0, $y / 2, (($y / 3) * 2)+1, $y); + $rtn['bottom_right'] = array(($x / 2) + 1, $x, (($y / 3) * 2)+1, $y); + + return $rtn; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Model.php b/B8Framework/b8/Model.php new file mode 100755 index 00000000..618aafcf --- /dev/null +++ b/B8Framework/b8/Model.php @@ -0,0 +1,224 @@ +data = array_merge($this->data, $initialData); + } + + $this->cache = Cache::getCache(Cache::TYPE_REQUEST); + } + + public function getTableName() + { + return $this->tableName; + } + + public function toArray($depth = 2, $currentDepth = 0) + { + if(isset(static::$sleepable) && is_array(static::$sleepable) && count(static::$sleepable)) + { + $sleepable = static::$sleepable; + } + else + { + $sleepable = array_keys($this->getters); + } + + $rtn = array(); + foreach($sleepable as $property) + { + $rtn[$property] = $this->_propertyToArray($property, $currentDepth, $depth); + } + + return $rtn; + } + + protected function _propertyToArray($property, $currentDepth, $depth) + { + $rtn = null; + + if(array_key_exists($property, $this->getters)) + { + $method = $this->getters[$property]; + $rtn = $this->{$method}(); + + if(is_object($rtn) || is_array($rtn)) + { + $rtn = ($depth > $currentDepth) ? $this->_valueToArray($rtn, $currentDepth, $depth) : null; + } + } + + return $rtn; + } + + protected function _valueToArray($value, $currentDepth, $depth) + { + $rtn = null; + if(!is_null($value)) + { + if(is_object($value) && method_exists($value, 'toArray')) + { + $rtn = $value->toArray($depth, $currentDepth + 1); + } + elseif(is_array($value)) + { + $childArray = array(); + + foreach($value as $k => $v) + { + $childArray[$k] = $this->_valueToArray($v, $currentDepth + 1, $depth); + } + + $rtn = $childArray; + } + else + { + $rtn = (is_string($value) && !mb_check_encoding($value, 'UTF-8')) ? mb_convert_encoding($value, 'UTF-8') : $value; + } + } + + return $rtn; + } + + public function getDataArray() + { + return $this->data; + } + + public function getModified() + { + return $this->modified; + } + + public function setValues(array $values) + { + foreach($values as $key => $value) + { + if(isset($this->setters[$key])) + { + $func = $this->setters[$key]; + + if($value === 'null') + { + $value = null; + } + elseif($value === 'true') + { + $value = true; + } + elseif($value === 'false') + { + $value = false; + } + + $this->{$func}($value); + } + } + } + + protected function _setModified($column) + { + $this->modified[$column] = $column; + } + + //---------------- + // Validation + //---------------- + protected function _validateString($name, $value) + { + if(!is_string($value) && !is_null($value)) + { + throw new HttpException\ValidationException($name . ' must be a string.'); + } + } + + protected function _validateInt($name, &$value) + { + if(is_bool($value)) + { + $value = $value ? 1 : 0; + } + + if(!is_numeric($value) && !is_null($value)) + { + throw new HttpException\ValidationException($name . ' must be an integer.'); + } + + if(!is_int($value) && !is_null($value)) + { + $value = (int)$value; + } + } + + protected function _validateFloat($name, &$value) + { + if(!is_numeric($value) && !is_null($value)) + { + throw new HttpException\ValidationException($name . ' must be a float.'); + } + + if(!is_float($value) && !is_null($value)) + { + $value = (float)$value; + } + } + + protected function _validateDate($name, &$value) + { + if(is_string($value)) + { + $value = empty($value) ? null : new \DateTime($value); + } + + if((!is_object($value) || !($value instanceof \DateTime)) && !is_null($value)) + { + throw new HttpException\ValidationException($name . ' must be a date object.'); + } + + + $value = empty($value) ? null : $value->format('Y-m-d H:i:s'); + } + + protected function _validateNotNull($name, &$value) + { + if(is_null($value)) + { + throw new HttpException\ValidationException($name . ' must not be null.'); + } + } + + public function __get($key) + { + if (array_key_exists($key, $this->getters)) { + $getter = $this->getters[$key]; + return $this->{$getter}(); + } + + return null; + } + + public function __set($key, $value) + { + if (array_key_exists($key, $this->setters)) { + $setter = $this->setters[$key]; + return $this->{$setter}($value); + } + } +} diff --git a/B8Framework/b8/Registry.php b/B8Framework/b8/Registry.php new file mode 100755 index 00000000..905c220c --- /dev/null +++ b/B8Framework/b8/Registry.php @@ -0,0 +1,91 @@ +config = $config; + $this->request = $request; + + self::$instance = $this; + } + + public function get($key, $default = null) + { + return $this->config->get($key, $default); + } + + public function set($key, $value) + { + return $this->config->set($key, $value); + } + + public function setArray($array) + { + return $this->config->set($array); + } + + public function getParams() + { + return $this->request->getParams(); + } + + public function getParam($key, $default) + { + return $this->request->getParam($key, $default); + } + + public function setParam($key, $value) + { + return $this->request->setParam($key, $value); + } + + public function unsetParam($key) + { + return $this->request->unsetParam($key); + } + + public function parseInput() + { + } +} \ No newline at end of file diff --git a/B8Framework/b8/Store.php b/B8Framework/b8/Store.php new file mode 100755 index 00000000..983bb569 --- /dev/null +++ b/B8Framework/b8/Store.php @@ -0,0 +1,379 @@ +tableName . '.* FROM ' . $this->tableName; + $countQuery = 'SELECT COUNT(*) AS cnt FROM ' . $this->tableName; + + $wheres = array(); + $params = array(); + foreach($where as $key => $value) + { + $key = $this->fieldCheck($key); + + if(!is_array($value)) + { + $params[] = $value; + $wheres[] = $key . ' = ?'; + } + else + { + if(isset($value['operator'])) + { + if(is_array($value['value'])) + { + if($value['operator'] == 'between') + { + $params[] = $value['value'][0]; + $params[] = $value['value'][1]; + $wheres[] = $key . ' BETWEEN ? AND ?'; + } + elseif($value['operator'] == 'IN') + { + $in = array(); + + foreach($value['value'] as $item) + { + $params[] = $item; + $in[] = '?'; + } + + $wheres[] = $key . ' IN (' . implode(', ', $in) . ') '; + } + else + { + $ors = array(); + foreach($value['value'] as $item) + { + if($item == 'null') + { + switch($value['operator']) + { + case '!=': + $ors[] = $key . ' IS NOT NULL'; + break; + + case '==': + default: + $ors[] = $key . ' IS NULL'; + break; + } + } + else + { + $params[] = $item; + $ors[] = $this->fieldCheck($key) . ' ' . $value['operator'] . ' ?'; + } + } + $wheres[] = '(' . implode(' OR ', $ors) . ')'; + } + } + else + { + if($value['operator'] == 'like') + { + $params[] = '%' . $value['value'] . '%'; + $wheres[] = $key . ' ' . $value['operator'] . ' ?'; + } + else + { + if($value['value'] === 'null') + { + switch($value['operator']) + { + case '!=': + $wheres[] = $key . ' IS NOT NULL'; + break; + + case '==': + default: + $wheres[] = $key . ' IS NULL'; + break; + } + } + else + { + $params[] = $value['value']; + $wheres[] = $key . ' ' . $value['operator'] . ' ?'; + } + } + } + } + else + { + $wheres[] = $key . ' IN (' . implode(', ', array_map(array(Database::getConnection('read'), 'quote'), $value)) . ')'; + } + } + } + + if(count($joins)) + { + foreach($joins as $table => $join) + { + $query .= ' LEFT JOIN ' . $table . ' ' . $join['alias'] . ' ON ' . $join['on'] . ' '; + $countQuery .= ' LEFT JOIN ' . $table . ' ' . $join['alias'] . ' ON ' . $join['on'] . ' '; + } + } + + if(count($manualJoins)) + { + foreach($manualJoins as $join) + { + $query .= ' ' . $join . ' '; + $countQuery .= ' ' . $join . ' '; + } + } + + $hasWhere = false; + if(count($wheres)) + { + $hasWhere = true; + $query .= ' WHERE (' . implode(' ' . $whereType . ' ', $wheres) . ')'; + $countQuery .= ' WHERE (' . implode(' ' . $whereType . ' ', $wheres) . ')'; + } + + if(count($manualWheres)) + { + foreach($manualWheres as $where) + { + if(!$hasWhere) + { + $hasWhere = true; + $query .= ' WHERE '; + $countQuery .= ' WHERE '; + } + else + { + $query .= ' ' . $where['type'] . ' '; + $countQuery .= ' ' . $where['type'] . ' '; + } + + $query .= ' ' . $where['query']; + $countQuery .= ' ' . $where['query']; + + if(isset($where['params'])) + { + foreach($where['params'] as $param) + { + $params[] = $param; + } + } + } + } + + if(!is_null($group)) + { + $query .= ' GROUP BY ' . $group . ' '; + } + + if(count($order)) + { + $orders = array(); + if(is_string($order) && $order == 'rand') + { + $query .= ' ORDER BY RAND() '; + } + else + { + foreach($order as $key => $value) + { + $orders[] = $this->fieldCheck($key) . ' ' . $value; + } + + $query .= ' ORDER BY ' . implode(', ', $orders); + } + } + + if($limit) + { + $query .= ' LIMIT ' . $limit; + } + + if($offset) + { + $query .= ' OFFSET ' . $offset; + } + + try + { + $stmt = Database::getConnection('read')->prepare($countQuery); + $stmt->execute($params); + $res = $stmt->fetch(\PDO::FETCH_ASSOC); + $count = (int)$res['cnt']; + } + catch(\PDOException $ex) + { + $count = 0; + } + + try + { + $stmt = Database::getConnection('read')->prepare($query); + $stmt->execute($params); + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + $rtn = array(); + + foreach($res as $data) + { + $rtn[] = new $this->modelName($data); + } + + return array('items' => $rtn, 'count' => $count); + } + catch(\PDOException $ex) + { + throw $ex; + } + } + + public function save(Model $obj, $saveAllColumns = false) + { + if(!isset($this->primaryKey)) + { + throw new HttpException\BadRequestException('Save not implemented for this store.'); + } + + if(!($obj instanceof $this->modelName)) + { + throw new HttpException\BadRequestException(get_class($obj) . ' is an invalid model type for this store.'); + } + + $data = $obj->getDataArray(); + + if(isset($data[$this->primaryKey])) + { + $rtn = $this->saveByUpdate($obj, $saveAllColumns); + } + else + { + $rtn = $this->saveByInsert($obj, $saveAllColumns); + } + + return $rtn; + } + + public function saveByUpdate(Model $obj, $saveAllColumns = false) + { + $rtn = null; + $data = $obj->getDataArray(); + $modified = ($saveAllColumns) ? array_keys($data) : $obj->getModified(); + + $updates = array(); + $update_params = array(); + foreach($modified as $key) + { + $updates[] = $key . ' = :' . $key; + $update_params[] = array($key, $data[$key]); + } + + if(count($updates)) + { + $qs = 'UPDATE ' . $this->tableName . ' + SET ' . implode(', ', $updates) . ' + WHERE ' . $this->primaryKey . ' = :primaryKey'; + $q = Database::getConnection('write')->prepare($qs); + + foreach($update_params as $update_param) + { + $q->bindValue(':' . $update_param[0], $update_param[1]); + } + + $q->bindValue(':primaryKey', $data[$this->primaryKey]); + $q->execute(); + + $rtn = $this->getByPrimaryKey($data[$this->primaryKey], 'write'); + } + else + { + $rtn = $obj; + } + + return $rtn; + } + + public function saveByInsert(Model $obj, $saveAllColumns = false) + { + $rtn = null; + $data = $obj->getDataArray(); + $modified = ($saveAllColumns) ? array_keys($data) : $obj->getModified(); + + $cols = array(); + $values = array(); + $qParams = array(); + foreach($modified as $key) + { + $cols[] = $key; + $values[] = ':' . $key; + $qParams[':' . $key] = $data[$key]; + } + + if(count($cols)) + { + $qs = 'INSERT INTO ' . $this->tableName . ' (' . implode(', ', $cols) . ') VALUES (' . implode(', ', $values) . ')'; + $q = Database::getConnection('write')->prepare($qs); + + if($q->execute($qParams)) + { + $id = !empty($data[$this->primaryKey]) ? $data[$this->primaryKey] : Database::getConnection('write')->lastInsertId(); + $rtn = $this->getByPrimaryKey($id, 'write'); + } + } + + return $rtn; + } + + public function delete(Model $obj) + { + if(!isset($this->primaryKey)) + { + throw new HttpException\BadRequestException('Delete not implemented for this store.'); + } + + if(!($obj instanceof $this->modelName)) + { + throw new HttpException\BadRequestException(get_class($obj) . ' is an invalid model type for this store.'); + } + + $data = $obj->getDataArray(); + + $q = Database::getConnection('write')->prepare('DELETE FROM ' . $this->tableName . ' WHERE ' . $this->primaryKey . ' = :primaryKey'); + $q->bindValue(':primaryKey', $data[$this->primaryKey]); + $q->execute(); + + return true; + } + + /** + * + */ + protected function fieldCheck($field) + { + if(empty($field)) + { + throw new HttpException('You cannot have an empty field name.'); + } + + if(strpos($field, '.') === false) + { + return $this->tableName . '.' . $field; + } + + return $field; + } +} \ No newline at end of file diff --git a/B8Framework/b8/Store/Factory.php b/B8Framework/b8/Store/Factory.php new file mode 100755 index 00000000..1c6f9b52 --- /dev/null +++ b/B8Framework/b8/Store/Factory.php @@ -0,0 +1,65 @@ +loadStore($storeName, $namespace); + } + + protected function __construct() + { + } + + /** + * @param $store + * + * @return \b8\Store; + */ + public function loadStore($store, $namespace = null) + { + if(!isset($this->loadedStores[$store])) + { + $namespace = is_null($namespace) ? Config::getInstance()->get('b8.app.namespace') : $namespace; + $class = $namespace . '\\Store\\' . $store . 'Store'; + $obj = new $class(); + + $this->loadedStores[$store] = $obj; + } + + return $this->loadedStores[$store]; + } +} diff --git a/B8Framework/b8/Type/Cache.php b/B8Framework/b8/Type/Cache.php new file mode 100755 index 00000000..e02eecf4 --- /dev/null +++ b/B8Framework/b8/Type/Cache.php @@ -0,0 +1,17 @@ +viewFile = self::getViewFile($file, $path); + } + + protected static function getViewFile($file, $path = null) + { + $viewPath = is_null($path) ? Config::getInstance()->get('b8.view.path') : $path; + $fullPath = $viewPath . $file . '.' . static::$extension; + + return $fullPath; + } + + public static function exists($file, $path = null) + { + if (!file_exists(self::getViewFile($file, $path))) { + return false; + } + + return true; + } + + public function __isset($var) + { + return isset($this->_vars[$var]); + } + + public function __get($var) + { + return $this->_vars[$var]; + } + + public function __set($var, $val) + { + $this->_vars[$var] = $val; + } + + public function __call($method, $params = array()) + { + if(!isset(self::$_helpers[$method])) + { + $class = '\\' . Config::getInstance()->get('b8.app.namespace') . '\\Helper\\' . $method; + + if(!class_exists($class)) + { + $class = '\\b8\\View\\Helper\\' . $method; + } + + if(!class_exists($class)) + { + throw new HttpException('Helper class does not exist: ' . $class); + } + + self::$_helpers[$method] = new $class(); + } + + return self::$_helpers[$method]; + } + + public function render() + { + extract($this->_vars); + + ob_start(); + require($this->viewFile); + $html = ob_get_contents(); + ob_end_clean(); + + return $html; + } +} \ No newline at end of file diff --git a/B8Framework/b8/View/Helper/Format.php b/B8Framework/b8/View/Helper/Format.php new file mode 100755 index 00000000..ea0628f0 --- /dev/null +++ b/B8Framework/b8/View/Helper/Format.php @@ -0,0 +1,11 @@ +viewCode = $viewCode; + + if (!count(self::$templateFunctions)) { + self::$templateFunctions = array('include' => array($this, 'includeTemplate'), 'call' => array($this, 'callHelperFunction')); + } + } + + public static function createFromFile($file, $path = null) + { + if (!static::exists($file, $path)) { + throw new \Exception('View file does not exist: ' . $file); + } + + $viewFile = static::getViewFile($file, $path); + return new static(file_get_contents($viewFile)); + } + + public static function createFromString($string) + { + return new static($string); + } + + public function addFunction($name, $handler) + { + self::$templateFunctions[$name] = $handler; + } + + public function removeFunction($name) + { + unset(self::$templateFunctions[$name]); + } + + public function render() + { + return $this->parse($this->viewCode); + } + + protected function parse($string) + { + $lastCond = null; + $keywords = array('ifnot', 'if', 'else', 'for', 'loop', '@', '/ifnot', '/if', '/for', '/loop'); + + foreach (self::$templateFunctions as $function => $handler) { + $keywords[] = $function; + } + + $stack = array('children' => array(array('type' => 'string', 'body' => ''))); + $stack['children'][0]['parent'] =& $stack; + $current =& $stack['children'][0]; + + while (!empty($string)) { + $current['body'] .= $this->readUntil('{', $string); + + if (!empty($string)) { + $gotKeyword = false; + + foreach($keywords as $keyword) { + $kwLen = strlen($keyword) + 1; + + if (substr($string, 0, $kwLen) == '{' . $keyword) { + $gotKeyword = true; + $item = array('type' => $keyword, 'cond' => '', 'children' => ''); + $string = substr($string, $kwLen); + + $cond = trim($this->readUntil('}', $string)); + $item['cond'] = $cond; + $lastCond = $cond; + $string = substr($string, 1); + + if (array_key_exists($keyword, self::$templateFunctions)) { + $item['function_name'] = $keyword; + $item['type'] = 'function'; + } + + $str = array('type' => 'string', 'body' => ''); + $parent =& $current['parent']; + + if (substr($current['body'], (0 - strlen(PHP_EOL))) === PHP_EOL) { + $current['body'] = substr($current['body'], 0, strlen($current['body']) - strlen(PHP_EOL)); + } + + $item['parent'] =& $parent; + + $parent['children'][] = $item; + + if ($keyword == '@' || $item['type'] == 'function') { + // If we're processing a variable, add a string to the parent and move up to that as current. + $parent['children'][] = $str; + $current =& $parent['children'][count($parent['children']) - 1]; + $current['parent'] =& $parent; + } elseif (substr($keyword, 0, 1) == '/') { + // If we're processing the end of a block (if/loop), add a string to the parent's parent and move up to that. + $parent =& $parent['parent']; + $parent['children'][] = $str; + $current =& $parent['children'][count($parent['children']) - 1]; + $current['parent'] =& $parent; + } else { + $parent['children'][count($parent['children']) - 1]['children'][] = $str; + $current =& $parent['children'][count($parent['children']) - 1]['children'][0]; + $current['parent'] =& $parent['children'][count($parent['children']) - 1]; + } + + break; + } + } + + if (!$gotKeyword) { + $current['body'] .= substr($string, 0, 1); + $string = substr($string, 1); + } + } + } + + return $this->processStack($stack); + } + + protected function processStack($stack) + { + $res = ''; + + while (count($stack['children'])) { + $current = array_shift($stack['children']); + + switch ($current['type']) { + case 'string': + $res .= $current['body']; + break; + + case '@': + $res .= $this->doParseVar($current['cond']); + break; + + case 'if': + $res .= $this->doParseIf($current['cond'], $current); + break; + + case 'ifnot': + $res .= $this->doParseIfNot($current['cond'], $current); + break; + + case 'loop': + $res .= $this->doParseLoop($current['cond'], $current); + break; + + case 'for': + $res .= $this->doParseFor($current['cond'], $current); + break; + + case 'function': + $res .= $this->doParseFunction($current); + break; + } + } + + return $res; + } + + protected function readUntil($until, &$string) + { + $read = ''; + + while (!empty($string)) { + $char = substr($string, 0, 1); + + if ($char == $until) { + break; + } + + $read .= $char; + $string = substr($string, 1); + } + + return $read; + } + + protected function doParseVar($var) + { + if($var == 'year') + { + return date('Y'); + } + + $val = $this->processVariableName($var); + return $val; + } + + protected function doParseIf($condition, $stack) + { + if ($this->ifConditionIsTrue($condition)) { + return $this->processStack($stack); + } else { + return ''; + } + } + + protected function doParseIfNot($condition, $stack) + { + if (!$this->ifConditionIsTrue($condition)) { + return $this->processStack($stack); + } else { + return ''; + } + } + + protected function ifConditionIsTrue($condition) + { + $matches = array(); + + if (preg_match('/([a-zA-Z0-9_\-\(\):\s.\"]+)\s+?([\!\=\<\>]+)?\s+?([a-zA-Z0-9\(\)_\-:\s.\"]+)?/', $condition, $matches)) { + $left = is_numeric($matches[1]) ? intval($matches[1]) : $this->processVariableName($matches[1]); + $right = is_numeric($matches[3]) ? intval($matches[3]) : $this->processVariableName($matches[3]); + $operator = $matches[2]; + + switch ($operator) { + case '==': + case '=': + return ($left == $right); + + case '!=': + return ($left != $right); + + case '>=': + return ($left >= $right); + + case '<=': + return ($left <= $right); + + case '>': + return ($left > $right); + + case '<': + return ($left < $right); + } + } elseif (preg_match('/([a-zA-Z0-9_\-\(\):\s.]+)/', $condition, $matches)) { + return $this->processVariableName($condition) ? true : false; + } + } + + protected function doParseLoop($var, $stack) + { + $working = $this->processVariableName($var); + + if(is_null($working)) + { + return ''; + } + + if(!is_array($working)) + { + $working = array($working); + } + + $rtn = ''; + foreach ($working as $key => $val) { + // Make sure we support nesting loops: + $keyWas = isset($this->key) ? $this->key : null; + $valWas = isset($this->value) ? $this->value : null; + $itemWas = isset($this->item) ? $this->item : null; + + // Set up the necessary variables within the stack: + $this->parent = $this; + $this->item = $val; + $this->key = $key; + $this->value = $val; + $rtn .= $this->processStack($stack); + + // Restore state for any parent nested loops: + $this->item = $itemWas; + $this->key = $keyWas; + $this->value = $valWas; + } + + return $rtn; + } + + /** + * Processes loops in templates, of the following styles: + * + * + * {for myarray.items} + * {@item.title} + * {/for} + * + * + * Or: + * + * + * {for 0:pages.count; i++} + * {@i} + * {/for} + * + * + * @param $cond string The condition string for the loop, to be parsed (e.g. "myarray.items" or "0:pages.count; i++") + * @param $stack string The child stack for this loop, to be processed for each item. + * @return string + * @throws \Exception + */ + protected function doParseFor($cond, $stack) + { + // If this is a simple foreach loop, jump over to parse loop: + if (strpos($cond, ';') === false) { + return $this->doParseLoop($cond, $stack); + } + + // Otherwise, process as a for loop: + $parts = explode(';', $cond); + $range = explode(':', trim($parts[0])); + + // Process range: + $rangeLeft = $this->getForRangePart($range[0]); + $rangeRight = $this->getForRangePart($range[1]); + + // Process variable & incrementor / decrementor: + $parts[1] = trim($parts[1]); + + $matches = array(); + if (preg_match('/([a-zA-Z0-9_]+)(\+\+|\-\-)/', $parts[1], $matches)) { + $varName = $matches[1]; + $direction = $matches[2] == '++' ? 'increment' : 'decrement'; + } else { + throw new \Exception('Syntax error in for loop: ' . $cond); + } + + $rtn = ''; + + if ($direction == 'increment') { + for ($i = $rangeLeft; $i < $rangeRight; $i++) { + $this->parent = $this; + $this->{$varName} = $i; + $rtn .= $this->processStack($stack); + } + } else { + for ($i = $rangeLeft; $i > $rangeRight; $i--) { + $this->parent = $this; + $this->{$varName} = $i; + $rtn .= $this->processStack($stack); + } + } + + return $rtn; + } + + protected function getForRangePart($part) + { + if (is_numeric($part)) { + return intval($part); + } + + $varPart = $this->processVariableName($part); + + if (is_numeric($varPart)) { + return intval($varPart); + } + + throw new \Exception('Invalid range in for loop: ' . $part); + } + + public function processVariableName($varName) + { + // Case one - Test for function calls: + if (substr($varName, 0, 1) == '(' && substr($varName, -1) == ')') { + + $functionCall = substr($varName, 1, -1); + $parts = explode(' ', $functionCall, 2); + $functionName = $parts[0]; + $arguments = isset($parts[1]) ? $parts[1] : null; + + return $this->executeTemplateFunction($functionName, $arguments); + } + + // Case two - Test if it is just a string: + if (substr($varName, 0, 1) == '"' && substr($varName, -1) == '"') { + return substr($varName, 1, -1); + } + + // Case three - Test if it is just a number: + if (is_numeric($varName)) { + return $varName; + } + + // Case four - Test for helper calls: + if (strpos($varName, ':') !== false) { + list($helper, $property) = explode(':', $varName); + + $helper = $this->{$helper}(); + + if (property_exists($helper, $property) || method_exists($helper, '__get')) { + return $helper->{$property}; + } + + return null; + } + + // Case five - Process as a variable: + $varPart = explode('.', $varName); + $thisPart = array_shift($varPart); + + + if(!array_key_exists($thisPart, $this->_vars)) + { + return null; + } + + $working = $this->{$thisPart}; + + while(count($varPart)) + { + $thisPart = array_shift($varPart); + + if(is_object($working)) { + // Check if we're working with an actual property: + if (property_exists($working, $thisPart)) { + $working = $working->{$thisPart}; + continue; + } + + // Check if the object has a magic __get method: + if (method_exists($working, '__get')) { + $working = $working->{$thisPart}; + continue; + } + } + + + if(is_array($working) && array_key_exists($thisPart, $working)) + { + $working = $working[$thisPart]; + continue; + } + + if($thisPart == 'toLowerCase') + { + $working = strtolower($working); + continue; + } + + if($thisPart == 'toUpperCase') + { + $working = strtoupper($working); + continue; + } + + if ($thisPart == 'isNumeric') + { + return is_numeric($working); + } + + return null; + } + + return $working; + } + + protected function doParseFunction($stack) + { + return $this->executeTemplateFunction($stack['function_name'], $stack['cond']); + } + + protected function executeTemplateFunction($function, $args) + { + if (array_key_exists($function, self::$templateFunctions)) { + $handler = self::$templateFunctions[$function]; + $args = $this->processFunctionArguments($args); + return $handler($args, $this); + } + + return null; + } + + protected function processFunctionArguments($args) + { + $rtn = array(); + + $args = explode(';', $args); + + foreach ($args as $arg) { + $arg = explode(':', $arg); + + if (count($arg) == 2) { + + $key = trim($arg[0]); + $val = trim($arg[1]); + + if (strpos($val, ',') !== false) { + $val = explode(',', $val); + } + + $rtn[$key] = $val; + } + } + + return $rtn; + } + + public function getVariable($variable) + { + return $this->processVariableName($variable); + } + + protected function includeTemplate($args, $view) + { + $template = static::createFromFile($view->getVariable($args['template'])); + + if (isset($args['variables'])) { + if (!is_array($args['variables'])) { + $args['variables'] = array($args['variables']); + } + + foreach ($args['variables'] as $variable) { + + $variable = explode('=>', $variable); + $variable = array_map('trim', $variable); + + if (count($variable) == 1) { + $template->{$variable[0]} = $view->getVariable($variable[0]); + } else { + $template->{$variable[1]} = $view->getVariable($variable[0]); + } + } + } + + return $template->render(); + } + + protected function callHelperFunction($args) + { + $helper = $args['helper']; + $function = $args['method']; + + return $this->{$helper}()->{$function}(); + } +} \ No newline at end of file diff --git a/B8Framework/b8/View/UserView.php b/B8Framework/b8/View/UserView.php new file mode 100755 index 00000000..954f9a42 --- /dev/null +++ b/B8Framework/b8/View/UserView.php @@ -0,0 +1,14 @@ +=5.3.0", + "symfony/yaml": "2.*" + }, + "autoload": { + "psr-0": { "b8": "" } + } +} diff --git a/B8Framework/phpci.yml b/B8Framework/phpci.yml new file mode 100755 index 00000000..7a0169bd --- /dev/null +++ b/B8Framework/phpci.yml @@ -0,0 +1,12 @@ +build_settings: + ignore: + - "vendor" + - "tests" + irc: + server: "irc.freenode.net" + port: 6667 + room: "#phpci" + nick: "phpcidev" + +test: + lint: diff --git a/B8Framework/tests/CacheTest.php b/B8Framework/tests/CacheTest.php new file mode 100755 index 00000000..69f544f3 --- /dev/null +++ b/B8Framework/tests/CacheTest.php @@ -0,0 +1,40 @@ +assertTrue($cache instanceof Cache); + } + + public function testDisableCaching() + { + b8\Registry::getInstance()->set('DisableCaching', true); + + $cache = b8\Cache::getInstance(); + $this->assertFalse($cache->isEnabled()); + $this->assertFalse($cache->set('anything', 10)); + $this->assertTrue(is_null($cache->get('anything'))); + + b8\Registry::getInstance()->set('DisableCaching', false); + } + + public function testCaching() + { + $cache = b8\Cache::getInstance(); + + if($cache->isEnabled()) + { + $this->assertTrue($cache->set('anything', 10)); + $this->assertTrue($cache->get('anything') == 10); + $this->assertTrue(is_null($cache->get('invalid'))); + } + } +} diff --git a/B8Framework/tests/CodeGenerationTest.php b/B8Framework/tests/CodeGenerationTest.php new file mode 100755 index 00000000..d5121da4 --- /dev/null +++ b/B8Framework/tests/CodeGenerationTest.php @@ -0,0 +1,641 @@ +set('b8.app.namespace', 'Generation'); + + self::$_db = Database::getConnection('write'); + + self::$_db->query('DROP TABLE IF EXISTS tres'); + self::$_db->query('DROP TABLE IF EXISTS dos'); + self::$_db->query('DROP TABLE IF EXISTS uno'); + + self::$_base = dirname(__FILE__) . '/data/generation/'; + $gen = new Generator(self::$_db, 'Test', self::$_base .'models/'); + $gen->generate(); + } + + public static function tearDownAfterClass() + { + self::$_db->query('DROP TABLE IF EXISTS tres'); + self::$_db->query('DROP TABLE IF EXISTS dos'); + self::$_db->query('DROP TABLE IF EXISTS uno'); + + unlink(self::$_base . 'Generation/Model/Base/UnoBase.php'); + unlink(self::$_base . 'Generation/Model/Base/DosBase.php'); + unlink(self::$_base . 'Generation/Model/Base/TresBase.php'); + unlink(self::$_base . 'Generation/Store/Base/UnoStoreBase.php'); + unlink(self::$_base . 'Generation/Store/Base/DosStoreBase.php'); + unlink(self::$_base . 'Generation/Store/Base/TresStoreBase.php'); + unlink(self::$_base . 'Generation/Controller/Base/UnoControllerBase.php'); + unlink(self::$_base . 'Generation/Controller/Base/DosControllerBase.php'); + unlink(self::$_base . 'Generation/Controller/Base/TresControllerBase.php'); + unlink(self::$_base . 'Generation/Model/Uno.php'); + unlink(self::$_base . 'Generation/Model/Dos.php'); + unlink(self::$_base . 'Generation/Model/Tres.php'); + unlink(self::$_base . 'Generation/Store/UnoStore.php'); + unlink(self::$_base . 'Generation/Store/DosStore.php'); + unlink(self::$_base . 'Generation/Store/TresStore.php'); + unlink(self::$_base . 'Generation/Controller/UnoController.php'); + unlink(self::$_base . 'Generation/Controller/DosController.php'); + unlink(self::$_base . 'Generation/Controller/TresController.php'); + } + + public function testGenerate() + { + error_reporting(E_ALL); + $codeGenerator = new CodeGenerator(self::$_db, 'Generation', self::$_base . 'Generation/'); + $codeGenerator->generateModels(); + $codeGenerator->generateStores(); + $codeGenerator->generateControllers(); + + $this->assertFileExists(self::$_base . 'Generation/Model/Base/UnoBase.php'); + $this->assertFileExists(self::$_base . 'Generation/Model/Base/DosBase.php'); + $this->assertFileExists(self::$_base . 'Generation/Model/Base/TresBase.php'); + $this->assertFileExists(self::$_base . 'Generation/Store/Base/UnoStoreBase.php'); + $this->assertFileExists(self::$_base . 'Generation/Store/Base/DosStoreBase.php'); + $this->assertFileExists(self::$_base . 'Generation/Store/Base/TresStoreBase.php'); + $this->assertFileExists(self::$_base . 'Generation/Controller/Base/UnoControllerBase.php'); + $this->assertFileExists(self::$_base . 'Generation/Controller/Base/DosControllerBase.php'); + $this->assertFileExists(self::$_base . 'Generation/Controller/Base/TresControllerBase.php'); + $this->assertFileExists(self::$_base . 'Generation/Model/Uno.php'); + $this->assertFileExists(self::$_base . 'Generation/Model/Dos.php'); + $this->assertFileExists(self::$_base . 'Generation/Model/Tres.php'); + $this->assertFileExists(self::$_base . 'Generation/Store/UnoStore.php'); + $this->assertFileExists(self::$_base . 'Generation/Store/DosStore.php'); + $this->assertFileExists(self::$_base . 'Generation/Store/TresStore.php'); + $this->assertFileExists(self::$_base . 'Generation/Controller/UnoController.php'); + $this->assertFileExists(self::$_base . 'Generation/Controller/DosController.php'); + $this->assertFileExists(self::$_base . 'Generation/Controller/TresController.php'); + } + + /** + * @depends testGenerate + */ + public function testGeneratedModels() + { + if(!defined('APPLICATION_PATH')) + { + define('APPLICATION_PATH', self::$_base); + } + + require_once(self::$_base . 'Generation/Model/Base/UnoBase.php'); + require_once(self::$_base . 'Generation/Model/Base/DosBase.php'); + require_once(self::$_base . 'Generation/Model/Base/TresBase.php'); + require_once(self::$_base . 'Generation/Model/Uno.php'); + require_once(self::$_base . 'Generation/Model/Dos.php'); + require_once(self::$_base . 'Generation/Model/Tres.php'); + require_once(self::$_base . 'ArrayPropertyModel.php'); + + $uno = new Generation\Model\Uno(); + $dos = new Generation\Model\Dos(); + $tres = new Generation\Model\Tres(); + + $this->assertTrue($uno instanceof b8\Model); + $this->assertTrue($dos instanceof b8\Model); + $this->assertTrue($tres instanceof b8\Model); + + $this->assertTrue($uno instanceof Generation\Model\Base\UnoBase); + $this->assertTrue($dos instanceof Generation\Model\Base\DosBase); + $this->assertTrue($tres instanceof Generation\Model\Base\TresBase); + + $this->assertTrue($uno->getTableName() == 'uno'); + $this->assertTrue($dos->getTableName() == 'dos'); + $this->assertTrue($tres->getTableName() == 'tres'); + + $uno->setId(1); + $uno->setFieldDatetime(new DateTime()); + $this->assertTrue($uno->getFieldDatetime() instanceof DateTime); + + $unoArray = $uno->toArray(); + $this->assertArrayHasKey('field_varchar', $unoArray); + $this->assertTrue($unoArray['field_datetime'] instanceof DateTime); + + Generation\Model\Uno::$sleepable = array('id', 'field_varchar'); + $unoArray = $uno->toArray(); + $this->assertArrayHasKey('field_varchar', $unoArray); + $this->assertFalse(array_key_exists('field_datetime', $unoArray)); + + $tres->setField($uno); + $this->assertTrue($tres->getFieldInt() == 1); + + $this->assertTrue(in_array('id', $uno->getModified())); + $this->assertTrue(is_array($uno->getDataArray())); + + $uno->setValues(array('field_int' => 100, 'field_bob' => 100)); + $this->assertFalse(in_array('field_bob', $uno->getModified())); + $this->assertTrue($uno->getFieldInt() === 100); + + $uno->setFieldInt(true); + $this->assertTrue($uno->getFieldInt() === 1); + + $caught = false; + + try + { + $uno->setFieldInt('invalid'); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + $uno->setFieldInt('500'); + $this->assertTrue($uno->getFieldInt() === 500); + + $caught = false; + + try + { + $uno->setFieldFloat('invalid'); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + $uno->setFieldFloat('4.12'); + $this->assertTrue($uno->getFieldFloat() === 4.12); + + + $uno->setFieldDatetime('2014-01-01'); + $this->assertTrue($uno->getFieldDatetime() instanceof DateTime); + + $caught = false; + + try + { + $uno->setFieldDatetime(2012); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + + $caught = false; + + try + { + $uno->setFieldInt(null); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + $caught = false; + + try + { + $uno->setValues(array('field_int' => 'null')); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + $uno->setValues(array('field_int' => 'true')); + $this->assertTrue($uno->getFieldInt() === 1); + + $uno->setValues(array('field_int' => 'false')); + $this->assertTrue($uno->getFieldInt() === 0); + + $caught = false; + + try + { + $uno->setFieldVarchar(false); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + $caught = false; + + try + { + $uno->setFieldVarchar('Hi'); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertFalse($caught); + + // Test toArray() with an array property: + $aModel = new Generation\ArrayPropertyModel(); + $array = $aModel->toArray(); + + $this->assertArrayHasKey('array_property', $array); + $this->assertTrue(is_array($array['array_property'])); + $this->assertTrue(is_array($array['array_property']['three'])); + $this->assertTrue($array['array_property']['one'] == 'two'); + } + + /** + * @depends testGeneratedModels + */ + public function testGeneratedStores() + { + require_once(self::$_base . 'Generation/Store/Base/UnoStoreBase.php'); + require_once(self::$_base . 'Generation/Store/Base/DosStoreBase.php'); + require_once(self::$_base . 'Generation/Store/Base/TresStoreBase.php'); + require_once(self::$_base . 'Generation/Store/UnoStore.php'); + require_once(self::$_base . 'Generation/Store/DosStore.php'); + require_once(self::$_base . 'Generation/Store/TresStore.php'); + + $uno = new Generation\Store\UnoStore(); + $dos = new Generation\Store\DosStore(); + $tres = new Generation\Store\TresStore(); + + $this->assertTrue($uno instanceof b8\Store); + $this->assertTrue($dos instanceof b8\Store); + $this->assertTrue($tres instanceof b8\Store); + + $this->assertTrue($uno instanceof Generation\Store\Base\UnoStoreBase); + $this->assertTrue($dos instanceof Generation\Store\Base\DosStoreBase); + $this->assertTrue($tres instanceof Generation\Store\Base\TresStoreBase); + + $tresModel = new Generation\Model\Tres(); + $tresModel->setFieldVarchar('Hi'); + + $caught = false; + + try + { + $tres->save($tresModel); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + + $caught = false; + + try + { + $uno->save($tresModel); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + + $unoModel = new Generation\Model\Uno(); + $unoModel->setFieldVarchar('Hi'); + + $unoModel = $uno->save($unoModel); + $id = $unoModel->getId(); + $this->assertTrue(!empty($id)); + $this->assertTrue($unoModel->getFieldVarchar() == 'Hi'); + + $unoModel->setFieldVarchar('Ha'); + $unoModel = $uno->save($unoModel); + $this->assertTrue($id == $unoModel->getId()); + $this->assertTrue($unoModel->getFieldVarchar() == 'Ha'); + + $unoModel = $uno->save($unoModel); + $this->assertTrue($id == $unoModel->getId()); + $this->assertTrue($unoModel->getFieldVarchar() == 'Ha'); + + $unoModel2 = $uno->getByPrimaryKey($id); + $this->assertTrue($unoModel2->getId() == $unoModel->getId()); + + $res = $uno->getWhere(array('field_varchar' => 'Ha')); + $this->assertTrue($res['count'] != 0); + + $res = $uno->getWhere(array('id' => array('operator' => 'between', 'value' => array(0, 100)))); + $this->assertTrue($res['count'] != 0); + + $res = $uno->getWhere(array('id' => array('operator' => 'IN', 'value' => array(1, 2, 3, 4)))); + $this->assertTrue($res['count'] != 0); + + $res = $uno->getWhere(array('id' => array('operator' => '!=', 'value' => array('null', 100)))); + $this->assertTrue($res['count'] != 0); + + $res = $uno->getWhere(array('id' => array('operator' => '==', 'value' => array('null')))); + $this->assertTrue($res['count'] == 0); + + $res = $uno->getWhere(array('id' => array('operator' => '==', 'value' => 'null'))); + $this->assertTrue($res['count'] == 0); + + $res = $uno->getWhere(array('id' => array('operator' => '!=', 'value' => 'null'))); + $this->assertTrue($res['count'] != 0); + + $res = $uno->getWhere(array('field_varchar' => array('operator' => 'like', 'value' => 'Ha'))); + $this->assertTrue($res['count'] != 0); + + $res = $uno->getWhere(array('field_varchar' => array('operator' => '!=', 'value' => 'Hi'))); + $this->assertTrue($res['count'] != 0); + + $res = $uno->getWhere(array('field_varchar' => array('Ha', 'Hi'))); + $this->assertTrue($res['count'] != 0); + + $res = $uno->getWhere(array('id' => 1), 1, 0, array('dos' => array('alias' => 'd', 'on' => 'd.id = uno.id')), array('id' => 'ASC')); + $this->assertTrue($res['count'] != 0); + + $res = $uno->getWhere(array('id' => 1), 1, 0, array('dos' => array('alias' => 'd', 'on' => 'd.id = uno.id')), 'rand'); + $this->assertTrue($res['count'] != 0); + + $res = $uno->getWhere(array('id' => 1), 1, 10); + $this->assertTrue(count($res['items']) == 0 && $res['count'] == 1); + + + $caught = false; + + try + { + $uno->getWhere(array('invalid_column' => 1)); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + $res = $uno->getWhere(array('id' => 1), 1, 0, array(), 'rand', array('LEFT JOIN dos d ON d.id = uno.id')); + $this->assertTrue($res['count'] != 0); + + + $res = $uno->getWhere(array('id' => 1), 1, 0, array(), 'rand', array(), 'field_varchar'); + $this->assertTrue($res['count'] != 0); + + $res = $uno->getWhere(array(), 1, 0, array(), 'rand', array(), null, array(array('type' => 'AND', 'query' => 'id = 1', 'params' => array()))); + $this->assertTrue($res['count'] != 0); + + $res = $uno->getWhere(array('id' => 2), 1, 0, array(), 'rand', array(), null, array(array('type' => 'AND', 'query' => 'id = ?', 'params' => array('id')))); + $this->assertTrue($res['count'] == 0); + + $caught = false; + + try + { + $uno->getWhere(array('' => 1)); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + // ---- + // Tests for Model::toArray() with relationships: + // ---- + $tresModel->setField($unoModel); + $array = $tresModel->toArray(); + + $this->assertTrue(array_key_exists('Field', $array)); + $this->assertTrue(array_key_exists('id', $array['Field'])); + $this->assertTrue($array['Field']['id'] == $unoModel->getId()); + + // ---- + // Tests for Store::delete() + // ---- + + $caught = false; + try + { + $tres->delete($tresModel); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + $caught = false; + try + { + $uno->delete($tresModel); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + + $this->assertTrue($uno->delete($unoModel)); + $this->assertTrue(is_null($uno->getByPrimaryKey(1))); + } + + /** + * @depends testGeneratedStores + */ + public function testGeneratedControllers() + { + require_once(self::$_base . 'Generation/Controller/Base/UnoControllerBase.php'); + require_once(self::$_base . 'Generation/Controller/Base/DosControllerBase.php'); + require_once(self::$_base . 'Generation/Controller/Base/TresControllerBase.php'); + require_once(self::$_base . 'Generation/Controller/UnoController.php'); + require_once(self::$_base . 'Generation/Controller/DosController.php'); + require_once(self::$_base . 'Generation/Controller/TresController.php'); + require_once(self::$_base . 'TestUser.php'); + + $uno = new Generation\Controller\UnoController(); + $dos = new Generation\Controller\DosController(); + $tres = new Generation\Controller\TresController(); + + $uno->init(); + $dos->init(); + $tres->init(); + + $this->assertTrue($uno instanceof b8\Controller); + $this->assertTrue($dos instanceof b8\Controller); + $this->assertTrue($tres instanceof b8\Controller); + + $this->assertTrue($uno instanceof Generation\Controller\Base\UnoControllerBase); + $this->assertTrue($dos instanceof Generation\Controller\Base\DosControllerBase); + $this->assertTrue($tres instanceof Generation\Controller\Base\TresControllerBase); + + + Registry::getInstance()->setParam('hello', 'world'); + $this->assertTrue($uno->getParam('hello', 'dave') == 'world'); + + $uno->setParam('hello', 'dave'); + $this->assertTrue($uno->getParam('hello', 'world') == 'dave'); + $this->assertTrue(array_key_exists('hello', $uno->getParams())); + + $uno->unsetParam('hello'); + $this->assertFalse(array_key_exists('hello', $uno->getParams())); + + $testUser = new \TestUser(); + $uno->setActiveUser($testUser); + $dos->setActiveUser($uno->getActiveUser()); + $tres->setActiveUser($uno->getActiveUser()); + + $unoModel = new Generation\Model\Uno(); + $unoStore = \b8\Store\Factory::getStore('Uno'); + $unoModel->setFieldVarchar('Hi'); + + $unoStore->save($unoModel); + $list = $uno->index(); + + $this->assertTrue(is_array($list)); + $this->assertTrue(is_array($list['items'])); + $this->assertTrue(count($list['items']) > 0); + + $caught = false; + try + { + $dos->index(); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + + $first = array_shift($list['items']); + + $uno1 = $uno->get($first['id']); + $this->assertTrue(is_array($uno1)); + $this->assertTrue(isset($uno1['uno'])); + $this->assertTrue($uno1['uno']['id'] == $first['id']); + + $caught = false; + try + { + $dos->get(1); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + + $uno->setParam('field_varchar', 'Un'); + $uno1 = $uno->put($first['id']); + $this->assertTrue($uno1['uno']['id'] == $first['id']); + + $caught = false; + try + { + $dos->put(1); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + $this->assertTrue(is_null($uno->put(10000))); + + + $uno->setParam('field_text', 'Hello'); + $res = $uno->post(); + $this->assertTrue($res['uno']['field_varchar'] == 'Un'); + $this->assertTrue(!empty($res['uno']['id'])); + + $caught = false; + try + { + $dos->post(); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + $del = $uno->delete($res['uno']['id']); + $this->assertTrue($del['deleted']); + + $del = $uno->delete($res['uno']['id']); + $this->assertFalse($del['deleted']); + + $del = $tres->delete(100); + $this->assertFalse($del['deleted']); + + $caught = false; + try + { + $dos->delete(1000); + } + catch(Exception $ex) + { + $caught = true; + } + + $this->assertTrue($caught); + + //---- + // Tests for _parseWhere() + //---- + $uno->setParam('where', array('id' => array(1000))); + $uno->setParam('neq', 'id'); + $list = $uno->index(); + + $this->assertTrue(is_array($list)); + $this->assertTrue(count($list['items']) != 0); + + Registry::getInstance()->forceReset(); + $uno->setParam('where', array('id' => 1000)); + $uno->setParam('fuzzy', 'id'); + $list = $uno->index(); + + $this->assertTrue(is_array($list)); + $this->assertTrue(count($list['items']) == 0); + } +} diff --git a/B8Framework/tests/DatabaseGenerationTest.php b/B8Framework/tests/DatabaseGenerationTest.php new file mode 100755 index 00000000..00674f7e --- /dev/null +++ b/B8Framework/tests/DatabaseGenerationTest.php @@ -0,0 +1,80 @@ +_name, $this->_user, $this->_pass); + \b8\Database::setWriteServers(array($this->_host)); + + $this->_db = \b8\Database::getConnection('write'); + + $this->_db->query('DROP TABLE IF EXISTS tres'); + $this->_db->query('DROP TABLE IF EXISTS dos'); + $this->_db->query('DROP TABLE IF EXISTS uno'); + } + + public function testCreateDatabase() + { + $gen = new Generator($this->_db, 'Test', dirname(__FILE__) . '/data/generation/models/'); + $gen->generate(); + + $map = new Map($this->_db); + $t = $map->generate(); + + $this->assertTrue(array_key_exists('uno', $t)); + $this->assertTrue(array_key_exists('dos', $t)); + $this->assertTrue(array_key_exists('tres', $t)); + $this->assertFalse(array_key_exists('bad_table', $t)); + $this->assertTrue(count($t['uno']['indexes']) == 1); + $this->assertTrue(count($t['dos']['indexes']) == 3); + $this->assertTrue(count($t['tres']['indexes']) == 2); + $this->assertTrue(count($t['uno']['columns']) == 11); + $this->assertTrue(count($t['dos']['columns']) == 4); + $this->assertTrue(count($t['tres']['columns']) == 6); + $this->assertTrue(array_key_exists('PRIMARY', $t['uno']['indexes'])); + $this->assertTrue(array_key_exists('PRIMARY', $t['dos']['indexes'])); + $this->assertFalse(array_key_exists('PRIMARY', $t['tres']['indexes'])); + } + + public function testUpdateDatabase() + { + $gen = new Generator($this->_db, 'Test', dirname(__FILE__) . '/data/generation/models/'); + $gen->generate(); + + $gen = new Generator($this->_db, 'Update', dirname(__FILE__) . '/data/generation/update_models/'); + $gen->generate(); + + $map = new Map($this->_db); + $t = $map->generate(); + + $this->assertTrue(array_key_exists('uno', $t)); + $this->assertTrue(array_key_exists('dos', $t)); + $this->assertTrue(array_key_exists('tres', $t)); + $this->assertFalse(array_key_exists('bad_table', $t)); + $this->assertTrue(count($t['uno']['indexes']) == 1); + $this->assertTrue(count($t['dos']['indexes']) == 3); + $this->assertTrue(count($t['tres']['indexes']) == 3); + $this->assertTrue(count($t['uno']['columns']) == 10); + $this->assertTrue(count($t['dos']['columns']) == 4); + $this->assertTrue(count($t['tres']['columns']) == 10); + $this->assertTrue(array_key_exists('PRIMARY', $t['uno']['indexes'])); + $this->assertTrue(array_key_exists('PRIMARY', $t['dos']['indexes'])); + $this->assertTrue(array_key_exists('PRIMARY', $t['tres']['indexes'])); + } +} diff --git a/B8Framework/tests/DatabaseTest.php b/B8Framework/tests/DatabaseTest.php new file mode 100755 index 00000000..e60e815b --- /dev/null +++ b/B8Framework/tests/DatabaseTest.php @@ -0,0 +1,54 @@ +_name, $this->_user, $this->_pass); + \b8\Database::setReadServers(array($this->_host)); + + $connection = \b8\Database::getConnection('read'); + + $this->assertInstanceOf('\b8\Database', $connection); + } + + public function testGetWriteConnection() + { + \b8\Database::setDetails($this->_name, $this->_user, $this->_pass); + \b8\Database::setWriteServers(array($this->_host)); + + $connection = \b8\Database::getConnection('write'); + + $this->assertInstanceOf('\b8\Database', $connection); + } + + public function testGetDetails() + { + \b8\Database::setDetails($this->_name, $this->_user, $this->_pass); + \b8\Database::setReadServers(array('localhost')); + + $details = \b8\Database::getConnection('read')->getDetails(); + $this->assertTrue(is_array($details)); + $this->assertTrue(($details['db'] == $this->_name)); + $this->assertTrue(($details['user'] == $this->_user)); + $this->assertTrue(($details['pass'] == $this->_pass)); + } + + /** + * @expectedException \Exception + */ + public function testConnectionFailure() + { + \b8\Database::setDetails('non_existant', 'invalid_user', 'incorrect_password'); + \b8\Database::setReadServers(array('localhost')); + \b8\Database::getConnection('read'); + } +} \ No newline at end of file diff --git a/B8Framework/tests/FormTest.php b/B8Framework/tests/FormTest.php new file mode 100755 index 00000000..e8973d9d --- /dev/null +++ b/B8Framework/tests/FormTest.php @@ -0,0 +1,196 @@ +setAction('/'); + $f->setMethod('POST'); + + $this->assertTrue($f->getAction() == '/'); + $this->assertTrue($f->getMethod() == 'POST'); + + Registry::getInstance()->set('ViewPath', dirname(__FILE__) . '/data/view/'); + + $this->assertTrue($f->render('form') == '/POST'); + + Registry::getInstance()->set('ViewPath', ''); + $this->assertTrue(strpos((string)$f, 'setId('element-id'); + $f->setLabel('element-label'); + $f->setClass('element-class'); + $f->setContainerClass('container-class'); + + $this->assertTrue($f->getName() == 'elementname'); + $this->assertTrue($f->getId() == 'element-id'); + $this->assertTrue($f->getLabel() == 'element-label'); + $this->assertTrue($f->getClass() == 'element-class'); + $this->assertTrue($f->getContainerClass() == 'container-class'); + + $output = $f->render(); + $this->assertTrue(is_string($output)); + $this->assertTrue(!empty($output)); + $this->assertTrue(strpos($output, 'container-class') !== false); + } + + public function testInputBasics() + { + $f = new Form\Element\Text(); + $f->setValue('input-value'); + $f->setRequired(true); + $f->setValidator(function($value) { return ($value == 'input-value'); }); + + $this->assertTrue($f->getValue() == 'input-value'); + $this->assertTrue($f->getRequired() == true); + $this->assertTrue(is_callable($f->getValidator())); + } + + public function testInputValidation() + { + $f = new Form\Element\Text(); + $f->setRequired(true); + $this->assertFalse($f->validate()); + + $f->setRequired(false); + $f->setPattern('input\-value'); + $this->assertFalse($f->validate()); + + $f->setValue('input-value'); + $this->assertTrue($f->validate()); + + $f->setValidator(function($item) + { + if($item != 'input-value') + { + throw new \Exception('Invalid input value.'); + } + }); + + $this->assertTrue($f->validate()); + + $f->setValue('fail'); + $f->setPattern(null); + $this->assertFalse($f->validate()); + } + + public function testFieldSetBasics() + { + $f = new Form\FieldSet(); + $f2 = new Form\FieldSet('group'); + $f3 = new Form\FieldSet(); + + $t = new Form\Element\Text('one'); + $t->setRequired(true); + $f2->addField($t); + + $t = new Form\Element\Text('two'); + $f2->addField($t); + + $t = new Form\Element\Text('three'); + $f3->addField($t); + + $f->addField($f2); + $f->addField($f3); + + $this->assertFalse($f->validate()); + + $f->setValues(array('group' => array('one' => 'ONE', 'two' => 'TWO'), 'three' => 'THREE')); + + $values = $f->getValues(); + $this->assertTrue(is_array($values)); + $this->assertTrue(array_key_exists('group', $values)); + $this->assertTrue(array_key_exists('one', $values['group'])); + $this->assertTrue(array_key_exists('three', $values)); + $this->assertTrue($values['group']['one'] == 'ONE'); + $this->assertTrue($values['group']['two'] == 'TWO'); + $this->assertTrue($values['three'] == 'THREE'); + $this->assertTrue($f->validate()); + + $html = $f->render(); + $this->assertTrue(strpos($html, 'one') !== false); + $this->assertTrue(strpos($html, 'two') !== false); + } + + public function testElements() + { + $e = new Form\Element\Button(); + $this->assertTrue($e->validate()); + $this->assertTrue(strpos($e->render(), 'button') !== false); + + $e = new Form\Element\Checkbox(); + $e->setCheckedValue('ten'); + $this->assertTrue($e->getCheckedValue() == 'ten'); + $this->assertTrue(strpos($e->render(), 'checkbox') !== false); + $this->assertTrue(strpos($e->render(), 'checked') === false); + + $e->setValue(true); + $this->assertTrue(strpos($e->render(), 'checked') !== false); + + $e->setValue('ten'); + $this->assertTrue(strpos($e->render(), 'checked') !== false); + + $e->setValue('fail'); + $this->assertTrue(strpos($e->render(), 'checked') === false); + + $e = new Form\Element\CheckboxGroup(); + $this->assertTrue(strpos($e->render(), 'group') !== false); + + $e = new Form\ControlGroup(); + $this->assertTrue(strpos($e->render(), 'group') !== false); + + $e = new Form\Element\Email(); + $this->assertTrue(strpos($e->render(), 'email') !== false); + + $e = new Form\Element\Select(); + $e->setOptions(array('key' => 'Val')); + $html = $e->render(); + $this->assertTrue(strpos($html, 'select') !== false); + $this->assertTrue(strpos($html, 'option') !== false); + $this->assertTrue(strpos($html, 'key') !== false); + $this->assertTrue(strpos($html, 'Val') !== false); + + $e = new Form\Element\Submit(); + $this->assertTrue($e->validate()); + $this->assertTrue(strpos($e->render(), 'submit') !== false); + + $e = new Form\Element\Text(); + $e->setValue('test'); + $this->assertTrue(strpos($e->render(), 'test') !== false); + + $e = new Form\Element\TextArea(); + $e->setRows(10); + $this->assertTrue(strpos($e->render(), '10') !== false); + + $e = new Form\Element\Url(); + $this->assertTrue(strpos($e->render(), 'url') !== false); + } +} \ No newline at end of file diff --git a/B8Framework/tests/HttpClientTest.php b/B8Framework/tests/HttpClientTest.php new file mode 100755 index 00000000..51288e0e --- /dev/null +++ b/B8Framework/tests/HttpClientTest.php @@ -0,0 +1,66 @@ +request('GET', 'https://www.cloudflare.com/'); + + $this->assertContains('CloudFlare', $html['body']); + } + + public function testBaseUrl() + { + $http = new HttpClient('https://www.cloudflare.com'); + $html = $http->request('GET', '/'); + + $this->assertContains('CloudFlare', $html['body']); + } + + public function testGet() + { + $http = new HttpClient('https://www.cloudflare.com'); + $html = $http->get('overview', array('x' => 1)); + + $this->assertContains('CloudFlare', $html['body']); + } + + public function testGetJson() + { + $http = new HttpClient('http://echo.jsontest.com'); + $data = $http->get('/key/value'); + + $this->assertArrayHasKey('key', $data['body']); + } + + public function testPost() + { + $http = new HttpClient('http://echo.jsontest.com'); + $data = $http->post('/key/value', array('test' => 'x')); + + $this->assertTrue(is_array($data)); + } + + public function testPut() + { + $http = new HttpClient('http://echo.jsontest.com'); + $data = $http->put('/key/value', array('test' => 'x')); + + $this->assertTrue(is_array($data)); + } + + public function testDelete() + { + $http = new HttpClient('http://echo.jsontest.com'); + $data = $http->delete('/key/value', array('test' => 'x')); + + $this->assertTrue(is_array($data)); + } +} \ No newline at end of file diff --git a/B8Framework/tests/HttpExceptionTest.php b/B8Framework/tests/HttpExceptionTest.php new file mode 100755 index 00000000..6d0895dc --- /dev/null +++ b/B8Framework/tests/HttpExceptionTest.php @@ -0,0 +1,115 @@ +assertTrue($ex instanceof \Exception); + } + + public function testHttpException() + { + try + { + throw new HttpException('Test'); + } + catch(HttpException $ex) + { + $this->assertTrue($ex->getMessage() == 'Test'); + $this->assertTrue($ex->getErrorCode() == 500); + $this->assertTrue($ex->getStatusMessage() == 'Internal Server Error'); + $this->assertTrue($ex->getHttpHeader() == 'HTTP/1.1 500 Internal Server Error'); + } + } + + public function testBadRequestException() + { + try + { + throw new HttpException\BadRequestException('Test'); + } + catch(HttpException $ex) + { + $this->assertTrue($ex->getErrorCode() == 400); + $this->assertTrue($ex->getStatusMessage() == 'Bad Request'); + } + } + + public function testForbiddenException() + { + try + { + throw new HttpException\ForbiddenException('Test'); + } + catch(HttpException $ex) + { + $this->assertTrue($ex->getErrorCode() == 403); + $this->assertTrue($ex->getStatusMessage() == 'Forbidden'); + } + } + + public function testNotAuthorizedException() + { + try + { + throw new HttpException\NotAuthorizedException('Test'); + } + catch(HttpException $ex) + { + $this->assertTrue($ex->getErrorCode() == 401); + $this->assertTrue($ex->getStatusMessage() == 'Not Authorized'); + } + } + + public function testNotFoundException() + { + try + { + throw new HttpException\NotFoundException('Test'); + } + catch(HttpException $ex) + { + $this->assertTrue($ex->getErrorCode() == 404); + $this->assertTrue($ex->getStatusMessage() == 'Not Found'); + } + } + + public function testServerErrorException() + { + try + { + throw new HttpException\ServerErrorException('Test'); + } + catch(HttpException $ex) + { + $this->assertTrue($ex->getErrorCode() == 500); + $this->assertTrue($ex->getStatusMessage() == 'Internal Server Error'); + } + } + + public function testValidationException() + { + try + { + throw new HttpException\ValidationException('Test'); + } + catch(HttpException $ex) + { + $this->assertTrue($ex->getErrorCode() == 400); + $this->assertTrue($ex->getStatusMessage() == 'Bad Request'); + } + } +} \ No newline at end of file diff --git a/B8Framework/tests/RegistryTest.php b/B8Framework/tests/RegistryTest.php new file mode 100755 index 00000000..97abecea --- /dev/null +++ b/B8Framework/tests/RegistryTest.php @@ -0,0 +1,82 @@ +set('test', true); + + $instance2 = Registry::getInstance(); + $this->assertTrue($instance2->get('test', false)); + } + + public function testStoreAndRetrieve() + { + Registry::forceReset(); + $r = Registry::getInstance(); + $r->set('test', 'cat'); + + $this->assertTrue($r->get('test', 'dog') == 'cat'); + } + + public function testSetArray() + { + Registry::forceReset(); + $r = Registry::getInstance(); + $r->set('one', 'two'); + $r->setArray(array('test' => 'cat')); + + $this->assertTrue($r->get('test', 'dog') == 'cat'); + $this->assertTrue($r->get('one', 'three') == 'two'); + } + + public function testGetNonExistent() + { + Registry::forceReset(); + $r = Registry::getInstance(); + $this->assertTrue(!$r->get('cat', false)); + } + + public function testGetParams() + { + Registry::forceReset(); + + $_REQUEST = array(); + $_REQUEST['cat'] = 'dog'; + $_SERVER['REQUEST_METHOD'] = 'GET'; + + $r = Registry::getInstance(); + $params = $r->getParams(); + $this->assertTrue(is_array($params)); + $this->assertArrayHasKey('cat', $params); + $this->assertArrayNotHasKey('dog', $params); + } + + public function testEmptyInput() + { + Registry::forceReset(); + $r = Registry::getInstance(); + $params = $r->getParams(); + + $this->assertTrue(is_array($params)); + $this->assertTrue(!count($params)); + } + + public function testGetSetUnsetParam() + { + Registry::forceReset(); + $r = Registry::getInstance(); + $this->assertTrue($r->getParam('cat', false) == false); + $r->setParam('cat', 'dog'); + + $this->assertTrue($r->getParam('cat', false) == 'dog'); + + $r->unsetParam('cat'); + $this->assertTrue($r->getParam('cat', false) == false); + } +} \ No newline at end of file diff --git a/B8Framework/tests/ViewTest.php b/B8Framework/tests/ViewTest.php new file mode 100755 index 00000000..96ee362b --- /dev/null +++ b/B8Framework/tests/ViewTest.php @@ -0,0 +1,141 @@ +assertTrue($view->render() == 'Hello'); + } + + /** + * @expectedException \Exception + */ + public function testInvalidView() + { + new b8\View('dogs', dirname(__FILE__) . '/data/view/'); + } + + public function testViewVars() + { + $view = new b8\View('vars', dirname(__FILE__) . '/data/view/'); + $view->who = 'World'; + + $this->assertTrue(isset($view->who)); + $this->assertFalse(isset($view->what)); + $this->assertTrue($view->render() == 'Hello World'); + } + + public function testFormatViewHelper() + { + $view = new b8\View('format', dirname(__FILE__) . '/data/view/'); + $view->number = 1000000.25; + $view->symbol = true; + + $this->assertTrue($view->render() == '£1,000,000.25'); + + $view->number = 1024; + $view->symbol = false; + $this->assertTrue($view->render() == '1,024.00'); + } + + /** + * @expectedException \b8\Exception\HttpException + */ + public function testInvalidHelper() + { + $view = new UserView('{@Invalid:test}'); + $view->render(); + } + + public function testSimpleUserView() + { + $view = new UserView('Hello'); + $this->assertTrue($view->render() == 'Hello'); + } + + public function testUserViewYear() + { + $view = new UserView('{@year}'); + $this->assertTrue($view->render() == date('Y')); + } + + public function testUserViewVars() + { + $view = new UserView('Hello {@who}'); + $view->who = 'World'; + $this->assertTrue($view->render() == 'Hello World'); + + $view = new UserView('Hello {@who}'); + $this->assertTrue($view->render() == 'Hello '); + + $view = new UserView('Hello {@who.name}'); + $view->who = array('name' => 'Dan'); + $this->assertTrue($view->render() == 'Hello Dan'); + + $tmp = new UserView('Hello'); + $tmp->who = 'World'; + $view = new UserView('Hello {@tmp.who}'); + $view->tmp = $tmp; + + $this->assertTrue($view->render() == 'Hello World'); + + $tmp = new UserView('Hello'); + $view = new UserView('Hello {@tmp.who}'); + $view->tmp = $tmp; + + $this->assertTrue($view->render() == 'Hello '); + + $view = new UserView('Hello {@who.toUpperCase}'); + $view->who = 'World'; + $this->assertTrue($view->render() == 'Hello WORLD'); + + $view = new UserView('Hello {@who.toLowerCase}'); + $view->who = 'World'; + $this->assertTrue($view->render() == 'Hello world'); + } + + public function testUserViewIf() + { + $view = new UserView('Hello{if who} World{/if}'); + $view->who = true; + $this->assertTrue($view->render() == 'Hello World'); + + $view = new UserView('Hello{if who} World{/if}'); + $view->who = false; + $this->assertTrue($view->render() == 'Hello'); + + $view = new UserView('Hello{ifnot who} World{/ifnot}'); + $view->who = true; + $this->assertTrue($view->render() == 'Hello'); + + $view = new UserView('Hello {if Format:not_present}World{/if}'); + $this->assertTrue($view->render() == 'Hello '); + + $view = new UserView('Hello {ifnot Format:not_present}World{/ifnot}'); + $this->assertTrue($view->render() == 'Hello World'); + } + + public function testUserViewLoop() + { + $view = new UserView('Hello {loop who}{@item}{/loop}'); + $view->who = array('W', 'o', 'r', 'l', 'd'); + $this->assertTrue($view->render() == 'Hello World'); + + $view = new UserView('Hello {loop who}{@item}{/loop}'); + $this->assertTrue($view->render() == 'Hello '); + + $view = new UserView('Hello {loop who}{@item}{/loop}'); + $view->who = 'World'; + $this->assertTrue($view->render() == 'Hello World'); + } +} \ No newline at end of file diff --git a/B8Framework/tests/data/generation/ArrayPropertyModel.php b/B8Framework/tests/data/generation/ArrayPropertyModel.php new file mode 100755 index 00000000..a8be40d1 --- /dev/null +++ b/B8Framework/tests/data/generation/ArrayPropertyModel.php @@ -0,0 +1,18 @@ +_getters['array_property'] = 'getArrayProperty'; + self::$sleepable[] = 'array_property'; + } + + public function getArrayProperty() + { + return array('one' => 'two', 'three' => array('four' => 'five')); + } +} \ No newline at end of file diff --git a/B8Framework/tests/data/generation/TestUser.php b/B8Framework/tests/data/generation/TestUser.php new file mode 100755 index 00000000..324f8459 --- /dev/null +++ b/B8Framework/tests/data/generation/TestUser.php @@ -0,0 +1,13 @@ + array('type' => 'catfish'), + ); + + public $indexes = array( + ); + public $foreignKeys = array( + ); +} \ No newline at end of file diff --git a/B8Framework/tests/data/generation/models/Dos.php b/B8Framework/tests/data/generation/models/Dos.php new file mode 100755 index 00000000..f3c3f4c0 --- /dev/null +++ b/B8Framework/tests/data/generation/models/Dos.php @@ -0,0 +1,24 @@ + array('type' => 'int', 'primary_key' => true, 'auto_increment' => false), + 'field_varchar' => array('type' => 'varchar', 'length' => '250', 'default' => 'Hello World'), + 'field_datetime'=> array('type' => 'datetime'), + 'field_int' => array('type' => 'int'), + ); + + public $indexes = array( + 'PRIMARY' => array('unique' => true, 'columns' => 'id'), + 'idx_test_1' => array('unique' => true, 'columns' => 'field_int'), + 'idx_test_2' => array('columns' => 'field_datetime'), + ); + public $foreignKeys = array( + ); +} \ No newline at end of file diff --git a/B8Framework/tests/data/generation/models/Tres.php b/B8Framework/tests/data/generation/models/Tres.php new file mode 100755 index 00000000..4edee2ae --- /dev/null +++ b/B8Framework/tests/data/generation/models/Tres.php @@ -0,0 +1,27 @@ + array('type' => 'int'), + 'field_varchar' => array('type' => 'varchar', 'length' => '250'), + 'field_date' => array('type' => 'date'), + 'field_datetime'=> array('type' => 'datetime'), + 'field_int' => array('type' => 'int'), + 'field_int_2' => array('type' => 'int'), + ); + + public $indexes = array( + 'fk_tres_uno' => array('columns' => 'field_int'), + 'fk_tres_dos' => array('columns' => 'field_int_2'), + ); + public $foreignKeys = array( + 'fk_tres_uno' => array('local_col' => 'field_int', 'table' => 'uno', 'col' => 'id'), + 'fk_tres_dos' => array('local_col' => 'field_int_2', 'update' => 'NO ACTION', 'delete' => 'CASCADE', 'table' => 'dos', 'col' => 'id'), + ); +} \ No newline at end of file diff --git a/B8Framework/tests/data/generation/models/Uno.php b/B8Framework/tests/data/generation/models/Uno.php new file mode 100755 index 00000000..0625ac97 --- /dev/null +++ b/B8Framework/tests/data/generation/models/Uno.php @@ -0,0 +1,28 @@ + array('type' => 'int', 'primary_key' => true, 'auto_increment' => true), + 'field_varchar' => array('type' => 'varchar', 'length' => '250'), + 'field_text' => array('type' => 'text'), + 'field_ltext' => array('type' => 'longtext'), + 'field_mtext' => array('type' => 'mediumtext'), + 'field_date' => array('type' => 'date'), + 'field_datetime'=> array('type' => 'datetime'), + 'field_int' => array('type' => 'int'), + 'field_tinyint' => array('type' => 'tinyint', 'length' => '1'), + 'field_float' => array('type' => 'float'), + 'field_double' => array('type' => 'double', 'length' => '15,2'), + ); + + public $indexes = array( + ); + public $foreignKeys = array( + ); +} \ No newline at end of file diff --git a/B8Framework/tests/data/generation/models/ignore.file b/B8Framework/tests/data/generation/models/ignore.file new file mode 100755 index 00000000..e69de29b diff --git a/B8Framework/tests/data/generation/update_models/Dos.php b/B8Framework/tests/data/generation/update_models/Dos.php new file mode 100755 index 00000000..f82e333c --- /dev/null +++ b/B8Framework/tests/data/generation/update_models/Dos.php @@ -0,0 +1,24 @@ + array('type' => 'int', 'primary_key' => true, 'auto_increment' => true), + 'field_varchar' => array('type' => 'varchar', 'length' => '250', 'default' => 'Hello World'), + 'field_datetime'=> array('type' => 'datetime'), + 'field_int' => array('type' => 'int'), + ); + + public $indexes = array( + 'PRIMARY' => array('unique' => true, 'columns' => 'id'), + 'idx_test_1' => array('unique' => false, 'columns' => 'field_int'), + 'idx_test_2' => array('columns' => 'field_datetime'), + ); + public $foreignKeys = array( + ); +} \ No newline at end of file diff --git a/B8Framework/tests/data/generation/update_models/Tres.php b/B8Framework/tests/data/generation/update_models/Tres.php new file mode 100755 index 00000000..6d974063 --- /dev/null +++ b/B8Framework/tests/data/generation/update_models/Tres.php @@ -0,0 +1,32 @@ + array('type' => 'int', 'primary_key' => true, 'auto_increment' => true), + 'id' => array('type' => 'int'), + 'field_varchar' => array('type' => 'varchar', 'length' => '250', 'default' => 'Hello World'), + 'field_datetime'=> array('type' => 'datetime'), + 'field_int' => array('type' => 'int'), + 'field_int_2' => array('type' => 'int'), + 'field_dt' => array('type' => 'date', 'rename' => 'field_date'), + 'field_float_1' => array('type' => 'float', 'default' => '1'), + 'field_varchar_2' => array('type' => 'varchar', 'length' => '10', 'default' => 'Hello'), + 'dosid' => array('type' => 'int'), + ); + + public $indexes = array( + 'PRIMARY' => array('unique' => true, 'columns' => 'key_col'), + 'fk_tres_dos' => array('columns' => 'field_int_2'), + 'fk_tres_dos_2' => array('columns' => 'dosid'), + ); + public $foreignKeys = array( + 'fk_tres_dos' => array('local_col' => 'field_int_2', 'update' => 'CASCADE', 'delete' => 'CASCADE', 'table' => 'dos', 'col' => 'id'), + 'fk_tres_dos_2' => array('local_col' => 'dosid', 'update' => 'CASCADE', 'delete' => 'CASCADE', 'table' => 'dos', 'col' => 'id'), + ); +} \ No newline at end of file diff --git a/B8Framework/tests/data/generation/update_models/Uno.php b/B8Framework/tests/data/generation/update_models/Uno.php new file mode 100755 index 00000000..cbe359cc --- /dev/null +++ b/B8Framework/tests/data/generation/update_models/Uno.php @@ -0,0 +1,27 @@ + array('type' => 'int', 'primary_key' => true), + 'field_varchar' => array('type' => 'varchar', 'length' => '250'), + 'field_text' => array('type' => 'text'), + 'field_ltext' => array('type' => 'longtext'), + 'field_mtext' => array('type' => 'mediumtext'), + 'field_datetime'=> array('type' => 'datetime'), + 'field_int' => array('type' => 'int'), + 'field_tinyint' => array('type' => 'tinyint', 'length' => '1'), + 'field_float' => array('type' => 'float'), + 'field_double' => array('type' => 'double', 'length' => '15,2'), + ); + + public $indexes = array( + ); + public $foreignKeys = array( + ); +} \ No newline at end of file diff --git a/B8Framework/tests/data/view/Form/form.phtml b/B8Framework/tests/data/view/Form/form.phtml new file mode 100755 index 00000000..1473e2fa --- /dev/null +++ b/B8Framework/tests/data/view/Form/form.phtml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/B8Framework/tests/data/view/format.phtml b/B8Framework/tests/data/view/format.phtml new file mode 100755 index 00000000..e646a066 --- /dev/null +++ b/B8Framework/tests/data/view/format.phtml @@ -0,0 +1 @@ +Format()->Currency($number, $symbol); ?> \ No newline at end of file diff --git a/B8Framework/tests/data/view/simple.phtml b/B8Framework/tests/data/view/simple.phtml new file mode 100755 index 00000000..5ab2f8a4 --- /dev/null +++ b/B8Framework/tests/data/view/simple.phtml @@ -0,0 +1 @@ +Hello \ No newline at end of file diff --git a/B8Framework/tests/data/view/vars.phtml b/B8Framework/tests/data/view/vars.phtml new file mode 100755 index 00000000..b4c976c8 --- /dev/null +++ b/B8Framework/tests/data/view/vars.phtml @@ -0,0 +1 @@ +Hello \ No newline at end of file diff --git a/composer.json b/composer.json index a9080a94..b8b10020 100644 --- a/composer.json +++ b/composer.json @@ -24,13 +24,15 @@ "autoload": { "psr-4": { - "PHPCI\\": "PHPCI" + "PHPCI\\": "PHPCI/", + "b8\\": "B8Framework/b8/" } }, "autoload-dev": { "psr-4": { - "Tests\\PHPCI\\": "Tests/PHPCI/" + "Tests\\PHPCI\\": "Tests/PHPCI/", + "Tests\\b8\\": "B8Framework/tests/" } }, @@ -38,7 +40,6 @@ "php": ">=5.3.8", "ext-pdo": "*", "ext-pdo_mysql": "*", - "block8/b8framework": "~1.0", "ircmaxell/password-compat": "~1.0", "swiftmailer/swiftmailer": "~5.0", "symfony/yaml": "~2.1", @@ -57,6 +58,7 @@ "require-dev": { "phpunit/phpunit": "~4.5", "phpmd/phpmd": "~2.0", + "sebastian/phpcpd": "~2.0", "squizlabs/php_codesniffer": "~2.3", "block8/php-docblock-checker": "~1.0", "phploc/phploc": "~2.0", diff --git a/composer.lock b/composer.lock index 9714ff78..8ae4c75e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,56 +4,9 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "de65276e03e231d7072c744a3c63662e", - "content-hash": "1d9f6f487e6d906bbed73e2667c276d6", + "hash": "4ab58f85da00db2f80ee2f0f4588339c", + "content-hash": "13a76d8d0e7057023ac961bd9462122f", "packages": [ - { - "name": "block8/b8framework", - "version": "1.1.10", - "source": { - "type": "git", - "url": "https://github.com/Block8/b8framework.git", - "reference": "5d2d2863ce15a6b91e0b2aed4250dd29c6224446" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Block8/b8framework/zipball/5d2d2863ce15a6b91e0b2aed4250dd29c6224446", - "reference": "5d2d2863ce15a6b91e0b2aed4250dd29c6224446", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "symfony/yaml": "2.*" - }, - "type": "library", - "autoload": { - "psr-0": { - "b8": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD" - ], - "authors": [ - { - "name": "Block 8 Limited", - "email": "hello@block8.co.uk", - "homepage": "http://www.block8.co.uk" - } - ], - "description": "Simple, lightweight framework for high-throughput applications", - "homepage": "https://github.com/block8/b8framework", - "keywords": [ - "b8", - "block8", - "framework", - "lightweight", - "mvc", - "php" - ], - "time": "2015-10-05 10:50:20" - }, { "name": "guzzle/guzzle", "version": "v3.9.3", @@ -264,16 +217,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.3.0", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "31382fef2889136415751badebbd1cb022a4ed72" + "reference": "2e89629ff057ebb49492ba08e6995d3a6a80021b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/31382fef2889136415751badebbd1cb022a4ed72", - "reference": "31382fef2889136415751badebbd1cb022a4ed72", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/2e89629ff057ebb49492ba08e6995d3a6a80021b", + "reference": "2e89629ff057ebb49492ba08e6995d3a6a80021b", "shasum": "" }, "require": { @@ -318,7 +271,7 @@ "stream", "uri" ], - "time": "2016-04-13 19:56:01" + "time": "2016-02-18 21:54:00" }, { "name": "hipchat/hipchat-php", @@ -455,16 +408,16 @@ }, { "name": "monolog/monolog", - "version": "1.19.0", + "version": "1.18.2", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "5f56ed5212dc509c8dc8caeba2715732abb32dbf" + "reference": "064b38c16790249488e7a8b987acf1c9d7383c09" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/5f56ed5212dc509c8dc8caeba2715732abb32dbf", - "reference": "5f56ed5212dc509c8dc8caeba2715732abb32dbf", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/064b38c16790249488e7a8b987acf1c9d7383c09", + "reference": "064b38c16790249488e7a8b987acf1c9d7383c09", "shasum": "" }, "require": { @@ -529,7 +482,7 @@ "logging", "psr-3" ], - "time": "2016-04-12 18:29:35" + "time": "2016-04-02 13:12:58" }, { "name": "mremi/flowdock", @@ -663,9 +616,7 @@ "authors": [ { "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" + "email": "fabien@symfony.com" } ], "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", @@ -1551,20 +1502,20 @@ }, { "name": "phpmd/phpmd", - "version": "2.4.3", + "version": "2.4.2", "source": { "type": "git", "url": "https://github.com/phpmd/phpmd.git", - "reference": "2b9c2417a18696dfb578b38c116cd0ddc19b256e" + "reference": "fccbdb6b222f6d7a6d35af1c396ba5435cec76a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpmd/phpmd/zipball/2b9c2417a18696dfb578b38c116cd0ddc19b256e", - "reference": "2b9c2417a18696dfb578b38c116cd0ddc19b256e", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/fccbdb6b222f6d7a6d35af1c396ba5435cec76a9", + "reference": "fccbdb6b222f6d7a6d35af1c396ba5435cec76a9", "shasum": "" }, "require": { - "pdepend/pdepend": "^2.0.4", + "pdepend/pdepend": "~2.0", "php": ">=5.3.0" }, "require-dev": { @@ -1612,7 +1563,7 @@ "phpmd", "pmd" ], - "time": "2016-04-04 11:52:04" + "time": "2016-03-10 17:17:44" }, { "name": "phpspec/prophecy", @@ -2411,6 +2362,57 @@ ], "time": "2015-10-12 03:26:01" }, + { + "name": "sebastian/phpcpd", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpcpd.git", + "reference": "d3ad100fdf15805495f6ff19f473f4314c99390c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/d3ad100fdf15805495f6ff19f473f4314c99390c", + "reference": "d3ad100fdf15805495f6ff19f473f4314c99390c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-timer": "~1.0", + "sebastian/finder-facade": "~1.1", + "sebastian/version": "~1.0", + "symfony/console": "~2.2", + "theseer/fdomdocument": "~1.4" + }, + "bin": [ + "phpcpd" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Copy/Paste Detector (CPD) for PHP code.", + "homepage": "https://github.com/sebastianbergmann/phpcpd", + "time": "2015-03-26 14:47:38" + }, { "name": "sebastian/recursion-context", "version": "1.0.2",