Added B8Framework to project

This commit is contained in:
Dmitry Khomutov 2016-04-12 23:31:39 +06:00
parent 166e6d8ac0
commit 550e93747c
100 changed files with 7192 additions and 71 deletions

6
B8Framework/.gitignore vendored Executable file
View file

@ -0,0 +1,6 @@
.DS_Store
._.DS_Store
Thumbs.db
.idea
composer.lock
vendor

11
B8Framework/README.md Executable file
View file

@ -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.*

126
B8Framework/b8/Application.php Executable file
View file

@ -0,0 +1,126 @@
<?php
namespace b8;
use b8\Config;
use b8\Exception\HttpException\NotFoundException;
use b8\Http;
use b8\View;
class Application
{
/**
* @var array
*/
protected $route;
/**
* @var \b8\Controller
*/
protected $controller;
/**
* @var b8\Http\Request
*/
protected $request;
/**
* @var b8\Http\Response
*/
protected $response;
/**
* @var b8\Config
*/
protected $config;
public function __construct(Config $config, Http\Request $request = null)
{
$this->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;
}
}

38
B8Framework/b8/Cache.php Executable file
View file

@ -0,0 +1,38 @@
<?php
namespace b8;
/**
* @package b8
* @subpackage Cache
*/
class Cache
{
const TYPE_APC = 'ApcCache';
const TYPE_REQUEST = 'RequestCache';
protected static $instance = array();
/**
* LEGACY: Older apps will expect an APC cache in return.
* @deprecated
*/
public static function getInstance()
{
return self::getCache(self::TYPE_APC);
}
/**
* Get a cache object of a specified type.
*/
public static function getCache($type = self::TYPE_REQUEST)
{
if (!isset(self::$instance[$type])) {
$class = '\\b8\\Cache\\' . $type;
self::$instance[$type] = new $class();
}
return self::$instance[$type];
}
}

115
B8Framework/b8/Cache/ApcCache.php Executable file
View file

@ -0,0 +1,115 @@
<?php
namespace b8\Cache;
use b8\Type;
class ApcCache implements Type\Cache
{
/**
* Check if caching is enabled.
*/
public function isEnabled()
{
$rtn = false;
$apcCli = ini_get('apc.enable_cli');
if( function_exists('apc_fetch') &&
(php_sapi_name() != 'cli' || in_array($apcCli, array('1', 1, true, 'On'))))
{
$rtn = true;
}
return $rtn;
}
/**
* Get item from the cache:
*/
public function get($key, $default = null)
{
if (!$this->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);
}
}

View file

@ -0,0 +1,90 @@
<?php
namespace b8\Cache;
use b8\Type;
class RequestCache implements Type\Cache
{
protected $data = array();
/**
* Check if caching is enabled.
*/
public function isEnabled()
{
return true;
}
/**
* Get item from the cache:
*/
public function get($key, $default = null)
{
return $this->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);
}
}

176
B8Framework/b8/Config.php Executable file
View file

@ -0,0 +1,176 @@
<?php
namespace b8;
use Symfony\Component\Yaml\Parser as YamlParser;
if (!defined('B8_PATH')) {
define('B8_PATH', dirname(__FILE__) . '/');
}
class Config
{
protected static $instance;
public static function getInstance()
{
return self::$instance;
}
/**
* @var array
*/
protected $config = array();
public function __construct($settings = null)
{
self::$instance = $this;
if (empty($settings)) {
return;
} elseif (is_array($settings)) {
// Array of setting data.
$this->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;
}
}

116
B8Framework/b8/Controller.php Executable file
View file

@ -0,0 +1,116 @@
<?php
namespace b8;
use b8\Config;
use b8\Http\Request;
use b8\Http\Response;
use b8\View;
/**
* b8 Abstract Controller class
* @package b8
*/
abstract class Controller
{
/**
* @var b8\Http\Request
*/
protected $request;
/**
* @var b8\Http\Response
*/
protected $response;
/**
* @var b8\Config
*/
protected $config;
/**
* @var b8\View
*/
protected $controllerView;
/**
* @var b8\View
*/
protected $view;
public function __construct(Config $config, Request $request, Response $response)
{
$this->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);
}
}

View file

@ -0,0 +1,246 @@
<?php
namespace b8\Controller;
use b8\Controller,
b8\Type\RestUser,
b8\Store\Factory,
b8\Exception\HttpException;
class RestController extends Controller
{
const SEARCHTYPE_AND = 'AND';
const SEARCHTYPE_OR = 'OR';
public $requiresAuthentication = true;
public $updateLastAction = true;
/**
* @var \b8\Type\RestUser
*/
protected $activeUser;
protected $where = array();
protected $limit = null;
protected $offset = null;
protected $joins = array();
protected $arrayDepth = 2;
protected $params = null;
protected $order = array();
protected $group = null;
protected $manualJoins = array();
protected $manualWheres = array();
protected $searchType = self::SEARCHTYPE_AND;
protected $_resourceName = null;
protected $_modelName = null;
protected $_tableName = null;
protected $_modelClass = null;
public function init()
{
}
protected function setControllerView()
{
}
protected function setView()
{
}
public function handleAction($action, $actionParams)
{
$response = call_user_func_array(array($this, $action), $actionParams);
$this->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);
}
}

149
B8Framework/b8/Database.php Executable file
View file

@ -0,0 +1,149 @@
<?php
namespace b8;
use b8\Config;
class Database extends \PDO
{
protected static $initialised = false;
protected static $servers = array('read' => 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;
}
}

View file

@ -0,0 +1,153 @@
<?php
namespace b8\Database;
use b8\Database,
b8\Database\Map,
b8\View\Template;
class CodeGenerator
{
protected $_db = null;
protected $_map = null;
protected $_tables = null;
protected $_ns = null;
protected $_path = null;
/**
* @param Database $db
* @param array $namespaces
* @param string $path
* @param bool $includeCountQueries
*/
public function __construct(Database $db, array $namespaces, $path, $includeCountQueries = true)
{
$this->_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();
}
}

View file

@ -0,0 +1,22 @@
<?php
/**
* {@table.php_name} base controller for table: {@name}
*/
namespace {@appNamespace}\Controller\Base;
use b8\Controller\RestController;
/**
* {@table.php_name} base Controller
* @see {@appNamespace}\Controller\{@table.php_name}
* @uses {@appNamespace}\Store\{@table.php_name}Store
* @uses {@appNamespace}\Model\{@table.php_name}
*/
class {@table.php_name}ControllerBase extends RestController
{
protected $_modelName = '{@table.php_name}';
protected $_resourceName = '{@table.php_name.toLowerCase}s';
protected $_modelClass = '\{@appNamespace}\Model\{@table.php_name}';
}

View file

@ -0,0 +1,268 @@
<?php
/**
* {@table.php_name} base model for table: {@name}
*/
namespace {@appNamespace}\Model\Base;
use {@appNamespace}\Model;
use b8\Store\Factory;
/**
* {@table.php_name} Base Model
*/
class {@table.php_name}Base extends Model
{
/**
* @var array
*/
public static $sleepable = array();
/**
* @var string
*/
protected $tableName = '{@name}';
/**
* @var string
*/
protected $modelName = '{@table.php_name}';
/**
* @var array
*/
protected $data = array(
{loop table.columns}
'{@item.name}' => 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}}

View file

@ -0,0 +1,101 @@
<?php
/**
* {@table.php_name} base store for table: {@name}
*/
namespace {@appNamespace}\Store\Base;
use b8\Database;
use b8\Exception\HttpException;
use {@appNamespace}\Store;
use {@appNamespace}\Model\{@table.php_name};
/**
* {@table.php_name} Base Store
*/
class {@table.php_name}StoreBase extends Store
{
protected $tableName = '{@name}';
protected $modelName = '\{@appNamespace}\Model\{@table.php_name}';
{if table.primary_key}
protected $primaryKey = '{@table.primary_key.column}';
public function getByPrimaryKey($value, $useConnection = 'read')
{
return $this->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}
}

View file

@ -0,0 +1,20 @@
<?php
/**
* {@table.php_name} controller for table: {@name}
*/
namespace {@appNamespace}\Controller;
use {@appNamespace}\Controller\Base\{@table.php_name}ControllerBase;
/**
* {@table.php_name} Controller
* @uses {@appNamespace}\Controller\Base\{@table.php_name}Base
* @uses {@appNamespace}\Store\{@table.php_name}Store
* @uses {@appNamespace}\Model\{@table.php_name}
*/
class {@table.php_name}Controller extends {@table.php_name}ControllerBase
{
// This class has been left blank so that you can modify it - changes in this file will not be overwritten.
}

View file

@ -0,0 +1,18 @@
<?php
/**
* {@table.php_name} model for table: {@name}
*/
namespace {@appNamespace}\Model;
use {@appNamespace}\Model\Base\{@table.php_name}Base;
/**
* {@table.php_name} Model
* @uses {@appNamespace}\Model\Base\{@table.php_name}Base
*/
class {@table.php_name} extends {@table.php_name}Base
{
// This class has been left blank so that you can modify it - changes in this file will not be overwritten.
}

View file

@ -0,0 +1,18 @@
<?php
/**
* {@table.php_name} store for table: {@name}
*/
namespace {@appNamespace}\Store;
use {@appNamespace}\Store\Base\{@table.php_name}StoreBase;
/**
* {@table.php_name} Store
* @uses {@appNamespace}\Store\Base\{@table.php_name}StoreBase
*/
class {@table.php_name}Store extends {@table.php_name}StoreBase
{
// This class has been left blank so that you can modify it - changes in this file will not be overwritten.
}

View file

@ -0,0 +1,428 @@
<?php
/**
* Database generator updates a database to match a set of Models.
*/
namespace b8\Database;
use b8\Database;
class Generator
{
protected $_db = null;
protected $_map = null;
protected $_tables = null;
protected $_ns = null;
protected $_path = null;
public function __construct(Database $db, $namespace, $path)
{
$this->_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;
}
}

258
B8Framework/b8/Database/Map.php Executable file
View file

@ -0,0 +1,258 @@
<?php
namespace b8\Database;
use b8\Database;
class Map
{
protected $_db = null;
protected $_tables = array();
public function __construct(Database $db)
{
$this->_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;
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace b8\Exception;
class HttpException extends \Exception
{
protected $errorCode = 500;
protected $statusMessage = 'Internal Server Error';
public function getErrorCode()
{
return $this->errorCode;
}
public function getStatusMessage()
{
return $this->statusMessage;
}
public function getHttpHeader()
{
return 'HTTP/1.1 ' . $this->errorCode . ' ' . $this->statusMessage;
}
}

View file

@ -0,0 +1,10 @@
<?php
namespace b8\Exception\HttpException;
use b8\Exception\HttpException;
class BadRequestException extends HttpException
{
protected $errorCode = 400;
protected $statusMessage = 'Bad Request';
}

View file

@ -0,0 +1,10 @@
<?php
namespace b8\Exception\HttpException;
use b8\Exception\HttpException;
class ForbiddenException extends HttpException
{
protected $errorCode = 403;
protected $statusMessage = 'Forbidden';
}

View file

@ -0,0 +1,10 @@
<?php
namespace b8\Exception\HttpException;
use b8\Exception\HttpException;
class NotAuthorizedException extends HttpException
{
protected $errorCode = 401;
protected $statusMessage = 'Not Authorized';
}

View file

@ -0,0 +1,10 @@
<?php
namespace b8\Exception\HttpException;
use b8\Exception\HttpException;
class NotFoundException extends HttpException
{
protected $errorCode = 404;
protected $statusMessage = 'Not Found';
}

View file

@ -0,0 +1,8 @@
<?php
namespace b8\Exception\HttpException;
use b8\Exception\HttpException;
class ServerErrorException extends HttpException
{
}

View file

@ -0,0 +1,10 @@
<?php
namespace b8\Exception\HttpException;
use b8\Exception\HttpException;
class ValidationException extends HttpException
{
protected $errorCode = 400;
protected $statusMessage = 'Bad Request';
}

44
B8Framework/b8/Form.php Executable file
View file

@ -0,0 +1,44 @@
<?php
namespace b8;
use b8\Form\FieldSet,
b8\View;
class Form extends FieldSet
{
protected $_action = '';
protected $_method = 'POST';
public function getAction()
{
return $this->_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();
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace b8\Form;
class ControlGroup extends FieldSet
{
}

118
B8Framework/b8/Form/Element.php Executable file
View file

@ -0,0 +1,118 @@
<?php
namespace b8\Form;
use b8\View;
use b8\Config;
abstract class Element
{
protected $_name;
protected $_id;
protected $_label;
protected $_css;
protected $_ccss;
protected $_parent;
public function __construct($name = null)
{
if(!is_null($name))
{
$this->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);
}

View file

@ -0,0 +1,19 @@
<?php
namespace b8\Form\Element;
use b8\Form\Input,
b8\View;
class Button extends Input
{
public function validate()
{
return true;
}
protected function _onPreRender(View &$view)
{
parent::_onPreRender($view);
$view->type = 'button';
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace b8\Form\Element;
use b8\View,
b8\Form\Input;
class Checkbox extends Input
{
protected $_checked;
protected $_checkedValue;
public function getCheckedValue()
{
return $this->_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;
}
}

View file

@ -0,0 +1,8 @@
<?php
namespace b8\Form\Element;
use b8\Form\FieldSet;
class CheckboxGroup extends FieldSet
{
}

View file

@ -0,0 +1,28 @@
<?php
namespace b8\Form\Element;
use b8\Form\Element\Hidden,
b8\View;
class Csrf extends Hidden
{
protected $_rows = 4;
public function validate()
{
if($this->_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);
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace b8\Form\Element;
use b8\View;
class Email extends Text
{
public function render($viewFile = null)
{
return parent::render(($viewFile ? $viewFile : 'Text'));
}
protected function _onPreRender(View &$view)
{
parent::_onPreRender($view);
$view->type = 'email';
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace b8\Form\Element;
use b8\Form\Input,
b8\View;
class Hidden extends Input
{
}

View file

@ -0,0 +1,19 @@
<?php
namespace b8\Form\Element;
use b8\Form\Element\Text,
b8\View;
class Password extends Text
{
public function render($viewFile = null)
{
return parent::render(($viewFile ? $viewFile : 'Text'));
}
protected function _onPreRender(View &$view)
{
parent::_onPreRender($view);
$view->type = 'password';
}
}

View file

@ -0,0 +1,8 @@
<?php
namespace b8\Form\Element;
use b8\Form\Element\Select;
class Radio extends Select
{
}

View file

@ -0,0 +1,21 @@
<?php
namespace b8\Form\Element;
use b8\View,
b8\Form\Input;
class Select extends Input
{
protected $_options = array();
public function setOptions(array $options)
{
$this->_options = $options;
}
protected function _onPreRender(View &$view)
{
parent::_onPreRender($view);
$view->options = $this->_options;
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace b8\Form\Element;
use b8\Form\Element\Button,
b8\View;
class Submit extends Button
{
protected $_value = 'Submit';
public function render($viewFile = null)
{
return parent::render(($viewFile ? $viewFile : 'Button'));
}
protected function _onPreRender(View &$view)
{
parent::_onPreRender($view);
$view->type = 'submit';
}
}

View file

@ -0,0 +1,14 @@
<?php
namespace b8\Form\Element;
use b8\Form\Input,
b8\View;
class Text extends Input
{
protected function _onPreRender(View &$view)
{
parent::_onPreRender($view);
$view->type = 'text';
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace b8\Form\Element;
use b8\Form\Element\Text,
b8\View;
class TextArea extends Text
{
protected $_rows = 4;
public function getRows()
{
return $this->_rows;
}
public function setRows($rows)
{
$this->_rows = $rows;
}
protected function _onPreRender(View &$view)
{
parent::_onPreRender($view);
$view->rows = $this->getRows();
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace b8\Form\Element;
use b8\View;
class Url extends Text
{
public function render($viewFile = null)
{
return parent::render(($viewFile ? $viewFile : 'Text'));
}
protected function _onPreRender(View &$view)
{
parent::_onPreRender($view);
$view->type = 'url';
}
}

114
B8Framework/b8/Form/FieldSet.php Executable file
View file

@ -0,0 +1,114 @@
<?php
namespace b8\Form;
use b8\Form\Element,
b8\Form\Input,
b8\View;
class FieldSet extends Element
{
protected $_children = array();
public function getValues()
{
$rtn = array();
foreach($this->_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];
}
}

124
B8Framework/b8/Form/Input.php Executable file
View file

@ -0,0 +1,124 @@
<?php
namespace b8\Form;
use b8\Form\Element,
b8\View;
class Input extends Element
{
protected $_required = false;
protected $_pattern;
protected $_validator;
protected $_value;
protected $_error;
protected $_customError = false;
public static function create($name, $label, $required = false)
{
$el = new static();
$el->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;
}
}

View file

@ -0,0 +1 @@
<input class="btn <?php print $css; ?>" type="<?php print $type; ?>" value="<?php print $value; ?>">

View file

@ -0,0 +1,15 @@
<?php if(!($parent instanceof \b8\Form\Element\CheckboxGroup)): ?>
<div class="control-group <?php print $ccss ?> <?php print (isset($error) ? 'error' : ''); ?>">
<div class="controls">
<?php endif; ?>
<label class="checkbox <?php print $css; ?>" for="<?php print $id ?>">
<input type="checkbox" id="<?php print $id; ?>" name="<?php print $name; ?>" value="<?php print $checkedValue; ?>" <?php print ($checked ? 'checked' : ''); ?> <?php print $required ? 'required' : '' ?>>
<?php print $label; ?>
</label>
<?php if(isset($error)): ?>
<span class="help-block"><?php print $error; ?></span>
<?php endif; ?>
<?php if(!($parent instanceof \b8\Form\Element\CheckboxGroup)): ?>
</div>
</div>
<?php endif; ?>

View file

@ -0,0 +1,11 @@
<div class="control-group <?php print $css; ?>">
<?php if($label): ?>
<label class="control-label"><?php print $label; ?></label>
<?php endif; ?>
<div class="controls">
<?php foreach($children as $field): ?>
<?php print $field; ?>
<?php endforeach; ?>
</div>
</div>

View file

@ -0,0 +1,6 @@
<div class="control-group <?php print $css; ?>">
<?php foreach($children as $field): ?>
<?php print $field; ?>
<?php endforeach; ?>
</div>

View file

@ -0,0 +1 @@
<input type="hidden" id="<?php print $id; ?>" name="<?php print $name; ?>" value="<?php print $csrf; ?>">

View file

@ -0,0 +1,9 @@
<fieldset class="row <?php print $css; ?>">
<?php if($label): ?>
<legend><?php print $label; ?></legend>
<?php endif; ?>
<?php foreach($children as $field): ?>
<?php print $field; ?>
<?php endforeach; ?>
</fieldset>

View file

@ -0,0 +1,6 @@
<form id="<?php print $id; ?>" class="<?php print $css; ?>" action="<?php print $action; ?>" method="<?php print $method; ?>">
<?php foreach($children as $field): ?>
<?php print $field; ?>
<?php endforeach; ?>
</form>

View file

@ -0,0 +1 @@
<input type="hidden" id="<?php print $id; ?>" name="<?php print $name; ?>" value="<?php print $value; ?>">

View file

@ -0,0 +1,18 @@
<div id="<?php print $id; ?>" class="control-group <?php print $ccss; ?>">
<?php if($label): ?>
<label class="control-label"><?php print $label; ?></label>
<?php endif; ?>
<div class="controls">
<?php foreach($options as $val => $lbl): ?>
<label class="radio" for="radio-<?php print $id; ?>-<?php print $val; ?>">
<input type="radio" id="radio-<?php print $id; ?>-<?php print $val; ?>" class="<?php print $css; ?>" name="<?php print $name; ?>" value="<?php print $val; ?>" <?php print ($value == $val) ? ' checked="checked"' : ''; ?> <?php print $required ? 'required' : '' ?>>
<?php print $lbl; ?>
</label>
<?php endforeach; ?>
<?php if(isset($error)): ?>
<span class="help-block"><?php print $error; ?></span>
<?php endif; ?>
</div>
</div>

View file

@ -0,0 +1,17 @@
<div class="control-group <?php print $ccss; ?>">
<?php if($label): ?>
<label class="control-label" for="<?php print $id ?>"><?php print $label; ?></label>
<?php endif; ?>
<div class="controls">
<select id="<?php print $id; ?>" class="<?php print $css; ?>" name="<?php print $name; ?>">
<?php foreach($options as $val => $lbl): ?>
<option value="<?php print $val; ?>" <?php print ($value == $val) ? ' selected="selected"' : ''; ?>><?php print $lbl; ?></option>
<?php endforeach; ?>
</select>
<?php if(isset($error)): ?>
<span class="help-block"><?php print $error; ?></span>
<?php endif; ?>
</div>
</div>

View file

@ -0,0 +1,13 @@
<div class="control-group <?php print $ccss; ?> <?php print (isset($error) ? 'error' : ''); ?>">
<?php if($label): ?>
<label class="control-label" for="<?php print $id ?>"><?php print $label; ?></label>
<?php endif; ?>
<div class="controls">
<input id="<?php print $id; ?>" type="<?php print $type; ?>" class="<?php print $css; ?>" name="<?php print $name; ?>" <?php print isset($value) ? ' value="' . $value . '"' : '' ?> <?php print isset($pattern) ? ' pattern="' . $pattern . '"' : '' ?> <?php print $required ? ' required' : '' ?>>
<?php if(isset($error)): ?>
<span class="help-block"><?php print $error; ?></span>
<?php endif; ?>
</div>
</div>

View file

@ -0,0 +1,13 @@
<div class="control-group <?php print $ccss; ?> <?php print (isset($error) ? 'error' : ''); ?>">
<?php if($label): ?>
<label class="control-label" for="<?php print $id ?>"><?php print $label; ?></label>
<?php endif; ?>
<div class="controls">
<textarea rows="<?php print $rows; ?>" id="<?php print $id; ?>" class="<?php print $css; ?>" name="<?php print $name; ?>" <?php print $required ? ' required' : '' ?>><?php print isset($value) ? $value : '' ?></textarea>
<?php if(isset($error)): ?>
<span class="help-block"><?php print $error; ?></span>
<?php endif; ?>
</div>
</div>

149
B8Framework/b8/Http/Request.php Executable file
View file

@ -0,0 +1,149 @@
<?php
namespace b8\Http;
class Request
{
/**
* @var array
*/
protected $params = array();
/**
* Request data.
*/
protected $data = array();
/**
* Set up the request.
*/
public function __construct()
{
$this->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;
}
}

132
B8Framework/b8/Http/Response.php Executable file
View file

@ -0,0 +1,132 @@
<?php
namespace b8\Http;
class Response
{
protected $data = array();
public function __construct(Response $createFrom = null)
{
if (!is_null($createFrom)) {
$this->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();
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace b8\Http\Response;
use b8\Http\Response;
class JsonResponse extends Response
{
public function __construct(Response $createFrom = null)
{
parent::__construct($createFrom);
$this->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);
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace b8\Http\Response;
use b8\Http\Response;
class RedirectResponse extends Response
{
public function __construct(Response $createFrom = null)
{
parent::__construct($createFrom);
$this->setContent(null);
$this->setResponseCode(302);
}
public function hasLayout()
{
return false;
}
public function flush()
{
parent::flush();
die;
}
}

123
B8Framework/b8/Http/Router.php Executable file
View file

@ -0,0 +1,123 @@
<?php
namespace b8\Http;
use b8\Application;
use b8\Config;
use b8\Http\Request;
class Router
{
/**
* @var \b8\Http\Request;
*/
protected $request;
/**
* @var \b8\Http\Config;
*/
protected $config;
/**
* @var \b8\Application
*/
protected $application;
/**
* @var array
*/
protected $routes = array(array('route' => '/: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;
}
}

188
B8Framework/b8/HttpClient.php Executable file
View file

@ -0,0 +1,188 @@
<?php
namespace b8;
class HttpClient
{
protected $_base = '';
protected $_params = array();
protected $_headers = array();
public function __construct($base = null)
{
$settings = Config::getInstance()->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;
}
}

149
B8Framework/b8/Image.php Executable file
View file

@ -0,0 +1,149 @@
<?php
namespace b8;
class Image
{
public static $cachePath = '/tmp/';
public static $sourcePath = './';
/**
* @var \Imagick
*/
protected $source;
public function __construct($imagePath)
{
$this->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;
}
}

224
B8Framework/b8/Model.php Executable file
View file

@ -0,0 +1,224 @@
<?php
namespace b8;
use b8\Exception\HttpException;
use b8\Cache;
class Model
{
public static $sleepable = array();
protected $getters = array();
protected $setters = array();
protected $data = array();
protected $modified = array();
protected $tableName;
protected $cache;
public function __construct($initialData = array())
{
if(is_array($initialData))
{
$this->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);
}
}
}

91
B8Framework/b8/Registry.php Executable file
View file

@ -0,0 +1,91 @@
<?php
namespace b8;
use b8\Config;
use b8\Http\Request;
if (!defined('B8_PATH')) {
define('B8_PATH', dirname(__FILE__) . '/');
}
/**
* b8\Registry is now deprecated in favour of using the following classes:
* @see b8\Http\Request
* @see b8\Http\Response
* @see b8\Config
*/
class Registry
{
/**
* @var \b8\Registry
*/
protected static $instance;
protected $_data = array();
protected $_params = null;
/**
* @var b8\Config
*/
protected $config;
/**
* @var b8\Http\Request
*/
protected $request;
/**
* @return Registry
*/
public static function getInstance()
{
return self::$instance;
}
public function __construct(Config $config, Request $request)
{
$this->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()
{
}
}

379
B8Framework/b8/Store.php Executable file
View file

@ -0,0 +1,379 @@
<?php
namespace b8;
use b8\Exception\HttpException;
use b8\Database,
b8\Model;
abstract class Store
{
protected $modelName = null;
protected $tableName = null;
protected $primaryKey = null;
/**
* @return \b8\Model
*/
abstract public function getByPrimaryKey($key, $useConnection = 'read');
public function getWhere($where = array(), $limit = 25, $offset = 0, $joins = array(), $order = array(), $manualJoins = array(), $group = null, $manualWheres = array(), $whereType = 'AND')
{
$query = 'SELECT ' . $this->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;
}
}

View file

@ -0,0 +1,65 @@
<?php
namespace b8\Store;
use b8\Config;
class Factory
{
/**
* @var \b8\Store\Factory
*/
protected static $instance;
/**
* A collection of the stores currently loaded by the factory.
* @var \b8\Store[]
*/
protected $loadedStores = array();
/**
* @return Factory
*/
public static function getInstance()
{
if(!isset(self::$instance))
{
self::$instance = new self();
}
return self::$instance;
}
/**
* @param $storeName string Store name (should match a model name).
*
* @return \b8\Store
*/
public static function getStore($storeName, $namespace = null)
{
$factory = self::getInstance();
return $factory->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];
}
}

17
B8Framework/b8/Type/Cache.php Executable file
View file

@ -0,0 +1,17 @@
<?php
namespace b8\Type;
interface Cache
{
public function get($key, $default = null);
public function set($key, $value = null, $ttl = 0);
public function delete($key);
public function contains($key);
public function isEnabled();
public function __get($key);
public function __set($key, $value = null);
public function __unset($key);
public function __isset($key);
}

View file

@ -0,0 +1,8 @@
<?php
namespace b8\Type;
interface RestUser
{
public function checkPermission($permission, $resource);
}

86
B8Framework/b8/View.php Executable file
View file

@ -0,0 +1,86 @@
<?php
namespace b8;
use b8\Exception\HttpException;
class View
{
protected $_vars = array();
protected static $_helpers = array();
protected static $extension = 'phtml';
public function __construct($file, $path = null)
{
if (!self::exists($file, $path)) {
throw new \Exception('View file does not exist: ' . $file);
}
$this->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;
}
}

View file

@ -0,0 +1,11 @@
<?php
namespace b8\View\Helper;
class Format
{
public function Currency($number, $symbol = true)
{
return ($symbol ? '£' : '') . number_format($number, 2, '.', ',');
}
}

544
B8Framework/b8/View/Template.php Executable file
View file

@ -0,0 +1,544 @@
<?php
namespace b8\View;
use b8\View;
class Template extends View
{
public static $templateFunctions = array();
protected static $extension = 'html';
public function __construct($viewCode)
{
$this->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:
*
* <code>
* {for myarray.items}
* {@item.title}
* {/for}
* </code>
*
* Or:
*
* <code>
* {for 0:pages.count; i++}
* <a href="/item/{@i}">{@i}</a>
* {/for}
* </code>
*
* @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}();
}
}

View file

@ -0,0 +1,14 @@
<?php
namespace b8\View;
use b8\View\Template;
class UserView extends Template
{
public function __construct($string)
{
trigger_error('Use of UserView is now deprecated. Please use Template instead.', E_USER_NOTICE);
parent::__construct($string);
}
}

22
B8Framework/composer.json Executable file
View file

@ -0,0 +1,22 @@
{
"name": "block8/b8framework",
"type": "library",
"description": "Simple, lightweight framework for high-throughput applications",
"keywords": ["php", "framework", "b8", "block8", "lightweight", "mvc"],
"homepage": "https://github.com/block8/b8framework",
"license": "BSD",
"authors": [
{
"name": "Block 8 Limited",
"email": "hello@block8.co.uk",
"homepage": "http://www.block8.co.uk"
}
],
"require": {
"php": ">=5.3.0",
"symfony/yaml": "2.*"
},
"autoload": {
"psr-0": { "b8": "" }
}
}

12
B8Framework/phpci.yml Executable file
View file

@ -0,0 +1,12 @@
build_settings:
ignore:
- "vendor"
- "tests"
irc:
server: "irc.freenode.net"
port: 6667
room: "#phpci"
nick: "phpcidev"
test:
lint:

40
B8Framework/tests/CacheTest.php Executable file
View file

@ -0,0 +1,40 @@
<?php
require_once(dirname(__FILE__) . '/../b8/Registry.php');
require_once(dirname(__FILE__) . '/../b8/Cache.php');
use b8\Registry,
b8\Cache;
class CacheTest extends PHPUnit_Framework_TestCase
{
public function testCreateSingleton()
{
$cache = b8\Cache::getInstance();
$this->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')));
}
}
}

View file

@ -0,0 +1,641 @@
<?php
require_once(dirname(__FILE__) . '/../b8/Registry.php');
require_once(dirname(__FILE__) . '/../b8/Model.php');
require_once(dirname(__FILE__) . '/../b8/Controller.php');
require_once(dirname(__FILE__) . '/../b8/Controller/RestController.php');
require_once(dirname(__FILE__) . '/../b8/Store.php');
require_once(dirname(__FILE__) . '/../b8/Store/Factory.php');
require_once(dirname(__FILE__) . '/../b8/Database.php');
require_once(dirname(__FILE__) . '/../b8/View.php');
require_once(dirname(__FILE__) . '/../b8/View/UserView.php');
require_once(dirname(__FILE__) . '/../b8/Database/Map.php');
require_once(dirname(__FILE__) . '/../b8/Database/Generator.php');
require_once(dirname(__FILE__) . '/../b8/Database/CodeGenerator.php');
require_once(dirname(__FILE__) . '/../b8/Exception/HttpException.php');
require_once(dirname(__FILE__) . '/../b8/Exception/HttpException/ValidationException.php');
require_once(dirname(__FILE__) . '/../b8/Exception/HttpException/BadRequestException.php');
require_once(dirname(__FILE__) . '/../b8/Exception/HttpException/ForbiddenException.php');
use b8\Database\Generator,
b8\Database\CodeGenerator,
b8\Database,
b8\Registry;
class CodeGenerationTest extends \PHPUnit_Framework_TestCase
{
protected static $_db;
protected static $_base;
public static function setUpBeforeClass()
{
Database::setDetails('b8_test_' . getenv('PHPCI_BUILD'), 'b8_test', 'b8_test');
Database::setWriteServers(array('localhost'));
Database::setReadServers(array('localhost'));
Registry::getInstance()->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);
}
}

View file

@ -0,0 +1,80 @@
<?php
require_once(dirname(__FILE__) . '/../b8/Registry.php');
require_once(dirname(__FILE__) . '/../b8/Model.php');
require_once(dirname(__FILE__) . '/../b8/Database.php');
require_once(dirname(__FILE__) . '/../b8/Database/Map.php');
require_once(dirname(__FILE__) . '/../b8/Database/Generator.php');
use b8\Database\Generator,
b8\Database\Map;
class DatabaseGenerationTest extends \PHPUnit_Framework_TestCase
{
protected $_host = 'localhost';
protected $_user = 'b8_test';
protected $_pass = 'b8_test';
protected $_name = 'b8_test';
protected $_db;
public function setUp()
{
\b8\Database::setDetails($this->_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']));
}
}

View file

@ -0,0 +1,54 @@
<?php
require_once(dirname(__FILE__) . '/../b8/Registry.php');
require_once(dirname(__FILE__) . '/../b8/Database.php');
class DatabaseTest extends \PHPUnit_Framework_TestCase
{
protected $_host = 'localhost';
protected $_user = 'b8_test';
protected $_pass = 'b8_test';
protected $_name = 'b8_test';
public function testGetReadConnection()
{
\b8\Database::setDetails($this->_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');
}
}

196
B8Framework/tests/FormTest.php Executable file
View file

@ -0,0 +1,196 @@
<?php
require_once(dirname(__FILE__) . '/../b8/Registry.php');
require_once(dirname(__FILE__) . '/../b8/View.php');
require_once(dirname(__FILE__) . '/../b8/View/UserView.php');
require_once(dirname(__FILE__) . '/../b8/Form/Element.php');
require_once(dirname(__FILE__) . '/../b8/Form/Input.php');
require_once(dirname(__FILE__) . '/../b8/Form/FieldSet.php');
require_once(dirname(__FILE__) . '/../b8/Form.php');
require_once(dirname(__FILE__) . '/../b8/Form/ControlGroup.php');
require_once(dirname(__FILE__) . '/../b8/Form/Element/Text.php');
require_once(dirname(__FILE__) . '/../b8/Form/Element/Button.php');
require_once(dirname(__FILE__) . '/../b8/Form/Element/Checkbox.php');
require_once(dirname(__FILE__) . '/../b8/Form/Element/CheckboxGroup.php');
require_once(dirname(__FILE__) . '/../b8/Form/Element/Email.php');
require_once(dirname(__FILE__) . '/../b8/Form/Element/Hidden.php');
require_once(dirname(__FILE__) . '/../b8/Form/Element/Select.php');
require_once(dirname(__FILE__) . '/../b8/Form/Element/Radio.php');
require_once(dirname(__FILE__) . '/../b8/Form/Element/Submit.php');
require_once(dirname(__FILE__) . '/../b8/Form/Element/TextArea.php');
require_once(dirname(__FILE__) . '/../b8/Form/Element/Url.php');
use b8\Form,
b8\Registry;
class FormTest extends \PHPUnit_Framework_TestCase
{
public function testFormBasics()
{
$f = new Form();
$f->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, '<form') !== false);
}
public function testElementBasics()
{
$f = new Form\Element\Text('element-name');
$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);
}
}

View file

@ -0,0 +1,66 @@
<?php
require_once(dirname(__FILE__) . '/../b8/Registry.php');
require_once(dirname(__FILE__) . '/../b8/HttpClient.php');
use b8\Registry,
b8\HttpClient;
class HttpClientTest extends \PHPUnit_Framework_TestCase
{
public function testSimpleRequest()
{
$http = new HttpClient();
$html = $http->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));
}
}

View file

@ -0,0 +1,115 @@
<?php
require_once(dirname(__FILE__) . '/../b8/Registry.php');
require_once(dirname(__FILE__) . '/../b8/Exception/HttpException.php');
require_once(dirname(__FILE__) . '/../b8/Exception/HttpException/BadRequestException.php');
require_once(dirname(__FILE__) . '/../b8/Exception/HttpException/ForbiddenException.php');
require_once(dirname(__FILE__) . '/../b8/Exception/HttpException/NotAuthorizedException.php');
require_once(dirname(__FILE__) . '/../b8/Exception/HttpException/NotFoundException.php');
require_once(dirname(__FILE__) . '/../b8/Exception/HttpException/ServerErrorException.php');
require_once(dirname(__FILE__) . '/../b8/Exception/HttpException/ValidationException.php');
use b8\Exception\HttpException,
b8\Exception\HttpException\BadRequestException;
class HttpExceptionTest extends \PHPUnit_Framework_TestCase
{
public function testHttpExceptionIsException()
{
$ex = new HttpException();
$this->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');
}
}
}

View file

@ -0,0 +1,82 @@
<?php
require_once(dirname(__FILE__) . '/../b8/Registry.php');
use b8\Registry;
class RegistryTest extends \PHPUnit_Framework_TestCase
{
public function testSingleton()
{
Registry::forceReset();
$instance = Registry::getInstance();
$instance->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);
}
}

141
B8Framework/tests/ViewTest.php Executable file
View file

@ -0,0 +1,141 @@
<?php
require_once(dirname(__FILE__) . '/../b8/Registry.php');
require_once(dirname(__FILE__) . '/../b8/View.php');
require_once(dirname(__FILE__) . '/../b8/View/UserView.php');
require_once(dirname(__FILE__) . '/../b8/View/Helper/Format.php');
require_once(dirname(__FILE__) . '/../b8/Exception/HttpException.php');
use b8\View,
b8\View\UserView;
class ViewTest extends \PHPUnit_Framework_TestCase
{
public function testSimpleView()
{
$view = new b8\View('simple', dirname(__FILE__) . '/data/view/');
$this->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');
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace Generation;
use Generation\Model\Uno;
class ArrayPropertyModel extends Uno
{
public function __construct($initialData = array())
{
$this->_getters['array_property'] = 'getArrayProperty';
self::$sleepable[] = 'array_property';
}
public function getArrayProperty()
{
return array('one' => 'two', 'three' => array('four' => 'five'));
}
}

View file

@ -0,0 +1,13 @@
<?php
require_once(B8_PATH . 'Type/RestUser.php');
use b8\Type\RestUser;
class TestUser implements RestUser
{
public function checkPermission($permission, $resource)
{
return $resource == 'unos' || $resource == 'tress' ? true : false;
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace Test\Model\Base;
use b8\Model;
class BadModel extends Model
{
protected $_tableName = 'bad_table';
public $columns = array(
'id' => array('type' => 'catfish'),
);
public $indexes = array(
);
public $foreignKeys = array(
);
}

View file

@ -0,0 +1,24 @@
<?php
namespace Test\Model\Base;
use b8\Model;
class Dos extends Model
{
protected $_tableName = 'dos';
public $columns = array(
'id' => 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(
);
}

View file

@ -0,0 +1,27 @@
<?php
namespace Test\Model\Base;
use b8\Model;
class Tres extends Model
{
protected $_tableName = 'tres';
public $columns = array(
'id' => 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'),
);
}

View file

@ -0,0 +1,28 @@
<?php
namespace Test\Model\Base;
use b8\Model;
class Uno extends Model
{
protected $_tableName = 'uno';
public $columns = array(
'id' => 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(
);
}

View file

@ -0,0 +1,24 @@
<?php
namespace Update\Model\Base;
use b8\Model;
class Dos extends Model
{
protected $_tableName = 'dos';
public $columns = array(
'id' => 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(
);
}

View file

@ -0,0 +1,32 @@
<?php
namespace Update\Model\Base;
use b8\Model;
class Tres extends Model
{
protected $_tableName = 'tres';
public $columns = array(
'key_col' => 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'),
);
}

View file

@ -0,0 +1,27 @@
<?php
namespace Update\Model\Base;
use b8\Model;
class Uno extends Model
{
protected $_tableName = 'uno';
public $columns = array(
'id' => 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(
);
}

View file

@ -0,0 +1 @@
<?php print $action . $method; ?>

View file

@ -0,0 +1 @@
<?php print $this->Format()->Currency($number, $symbol); ?>

View file

@ -0,0 +1 @@
Hello

View file

@ -0,0 +1 @@
Hello <?php print $who; ?>

View file

@ -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",

138
composer.lock generated
View file

@ -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",