Created
October 30, 2014 04:31
-
-
Save jm42/bf14542932e585ae01ff to your computer and use it in GitHub Desktop.
MVC Framework Test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$container['db.config'] = array( | |
'file' => __DIR__ . '/../tmp/mvc.db', | |
'init' => function(PDO $pdo) { | |
$pdo->exec('CREATE TABLE user (id INTEGER PRIMARY KEY, name VARCHAR(255))'); | |
$pdo->exec('INSERT INTO user (id, name) VALUES (1, "John Doe")'); | |
$pdo->exec('INSERT INTO user (id, name) VALUES (2, "Jane Doe")'); | |
}, | |
); | |
$container['routes'] = array( | |
array( | |
'model' => null, | |
'action' => '', | |
'controller' => function() {}, | |
'view' => 'home_view', | |
), | |
array( | |
'model' => 'UserList', | |
'action' => '', | |
'controller' => 'search_controller', | |
'view' => 'list_view', | |
), | |
array( | |
'model' => 'User', | |
'action' => '', | |
'controller' => function() {}, | |
'view' => 'user_view', | |
), | |
); | |
$container['root_factory'] = function($di) { | |
return new Resource(array( | |
'users' => new UserList($di->get('db')), | |
)); | |
}; | |
$container['renderer.template.options'] = array( | |
'dir' => __DIR__ . '/templates', | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
function search_controller( | |
Request $request, | |
Response $response, | |
Searchable $model | |
) { | |
if (isset($request->query['criteria'])) { | |
$model->setCriteria($request->query['criteria']); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
interface Listable { | |
function getData(); | |
} | |
interface Searchable { | |
function setCriteria($criteria); | |
} | |
class UserList implements Listable, Searchable, ArrayAccess { | |
public $__parent; | |
public $__name; | |
private $pdo; | |
private $criteria; | |
private $users; | |
public function __construct(PDO $pdo) { | |
$this->pdo = $pdo; | |
} | |
public function setCriteria($criteria) { | |
$this->criteria = $criteria; | |
} | |
public function load() { | |
if ($this->users !== null) { | |
return $this->users; | |
} | |
if ($this->criteria === null) { | |
$stmt = $this->pdo->prepare('SELECT id, name FROM user'); | |
} else { | |
$stmt = $this->pdo->prepare('SELECT id, name FROM user WHERE name LIKE :name'); | |
$stmt->bindValue(':name', "%{$this->criteria}%"); | |
} | |
$stmt->execute(); | |
$users = array(); | |
while ($user = $stmt->fetchObject('User')) { | |
$user->__parent = $this; | |
$user->__name = $user->id; | |
$users[$user->id] = $user; | |
} | |
$stmt->closeCursor(); | |
return $this->users = $users; | |
} | |
public function getData() { | |
$this->load(); | |
return $this->users; | |
} | |
public function offsetExists($id) { | |
$this->load(); | |
return isset($this->users[$id]); | |
} | |
public function offsetGet($id) { | |
$this->load(); | |
return $this->users[$id]; | |
} | |
public function offsetSet($id, $user) { | |
if (!$user instanceof User) { | |
$user = new User(); | |
$user->id = $id; | |
$user->name = $user; | |
$user->__parent = $this; | |
$user->__name = $id; | |
} | |
$this->load(); | |
$this->users[$id] = $user; | |
} | |
public function offsetUnset($id) { | |
$this->load(); | |
unset($this->users[$id]); | |
} | |
} | |
class User { | |
public $__parent; | |
public $__name; | |
public $id; | |
public $name; | |
public function __toString() { | |
return $this->name; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<pre><a href="/users"><code>/users</code></a></pre> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<h2>Users</h2> | |
<form method="get"> | |
<input type="text" name="criteria"> | |
<input type="submit" value="Search"> | |
</form> | |
<a href="/users">View all</a> | |
<ul> | |
<?php foreach ($data as $item): ?> | |
<li><a href="<?= $this->assemble($item) ?>"><?= $item ?></a></li> | |
<?php endforeach ?> | |
</ul> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<h2><?= $user->name ?></h2> | |
<a href="<?= $this->assemble($user->__parent) ?>?criteria=<?= $user->name ?>">Go back</a> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
function home_view() { | |
return new TemplateResult('home.phtml'); | |
} | |
function list_view(Listable $model) { | |
return new TemplateResult('list.phtml', array( | |
'data' => $model->getData(), | |
)); | |
} | |
function user_view(User $user) { | |
return new TemplateResult('user.phtml', array( | |
'user' => $user, | |
)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require __DIR__ . '/lib/app.php'; | |
require __DIR__ . '/lib/http.php'; | |
require __DIR__ . '/lib/di.php'; | |
require __DIR__ . '/lib/routing.php'; | |
require __DIR__ . '/lib/controller.php'; | |
require __DIR__ . '/lib/container.php'; | |
require __DIR__ . '/app/controller.php'; | |
require __DIR__ . '/app/model.php'; | |
require __DIR__ . '/app/view.php'; | |
require __DIR__ . '/app/config.php'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
bbbb<?php | |
class Application { | |
private $container; | |
private $controller; | |
public function __construct($container, callable $controller = null) { | |
if ($controller === null) { | |
$controller = $container->get('front-controller'); | |
} | |
$this->container = $container; | |
$this->controller = $controller; | |
} | |
public function run() { | |
$request = Request::fromEnviron(); | |
try { | |
$response = new Response(''); | |
$controller = $this->controller; | |
$controller($request, $response); | |
} catch (NotFoundException $e) { | |
$response = new Response($e->getMessage(), 404); | |
} catch (ServerErrorException $e) { | |
$response = new Response($e->getMessage(), 500); | |
} | |
$response->send(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$container = new Container; | |
$container['db'] = function($di) { | |
$exists = file_exists($di['db.config']['file']); | |
$pdo = new PDO('sqlite://' . $di['db.config']['file']); | |
if (!$exists && isset($di['db.config']['init'])) { | |
$di['db.config']['init']($pdo); | |
} | |
return $pdo; | |
}; | |
$container['root_factory'] = function() { | |
return array(); | |
}; | |
$container['router'] = function($di) { | |
if (isset($di['root'])) { | |
$root = $di['root']; | |
} else { | |
$root = $di->get('root_factory'); | |
} | |
$router = new Router($root); | |
foreach (array_reverse($di['routes']) as $route) { | |
$router->addRoute( | |
$route['model'], | |
$route['action'], | |
$route['controller'], | |
$route['view'] | |
); | |
} | |
return $router; | |
}; | |
$container['front-controller'] = function($di) { | |
$router = $di->get('router'); | |
$renderers = array(); | |
foreach ($di['renderers'] as $renderer) { | |
$renderers[$renderer] = $di->get("renderer.$renderer"); | |
} | |
$front = new FrontController($router, $renderers); | |
return $front; | |
}; | |
$container['renderer.template'] = function($di) { | |
$options = $di['renderer.template.options']; | |
$renderer = new TemplateRenderer($options); | |
foreach ($di['renderer.template.helpers'] as $name => $helper) { | |
$renderer->helpers[$name] = $helper; | |
} | |
return $renderer; | |
}; | |
$container['renderer.template.options'] = array( | |
'dir' => 'templates', | |
); | |
$container['renderer.template.helpers'] = array( | |
'assemble' => function($resource) use ($container) { | |
$segments = $container->get('router')->assemble($resource); | |
$path = '/' . implode('/', $segments); // FIXME | |
return $path; | |
}, | |
); | |
$container['renderers'] = array('template'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class FrontController { | |
private $router; | |
private $renderers; | |
public function __construct(Router $router, array $renderers) { | |
$this->router = $router; | |
$this->renderers = $renderers; | |
} | |
public function __invoke(Request $request, Response $response) { | |
$matched = $this->router->matchRequest($request); | |
if ($matched['model'] === null) { | |
$matched['controller']($request, $response); | |
} else { | |
$matched['controller']($request, $response, $matched['model']); | |
} | |
$result = $matched['view']($matched['model']); | |
$renderer = $this->renderers[$result->getRenderer()]; | |
$content = $renderer->render($result); | |
$response->content = $content; | |
} | |
} | |
interface Result { | |
function getRenderer(); | |
} | |
class TemplateResult implements Result { | |
public $template; | |
public $vars; | |
public function __construct($template, array $vars = array()) { | |
$this->template = $template; | |
$this->vars = $vars; | |
} | |
public function getRenderer() { | |
return 'template'; | |
} | |
} | |
interface Renderer { | |
function render(Result $result); | |
} | |
abstract class AbstractRenderer implements Renderer { | |
public $options = array(); | |
public $helpers = array(); | |
public static $defaultOptions = array(); | |
public function __construct(array $options) { | |
$this->options = array_merge(static::$defaultOptions, $options); | |
} | |
public function __call($name, array $arguments = array()) { | |
if (isset($this->helpers[$name])) { | |
return call_user_func_array($this->helpers[$name], $arguments); | |
} | |
throw new BadMethodCallException("Helper $name not found"); | |
} | |
} | |
class TemplateRenderer extends AbstractRenderer { | |
public static $defaultOptions = array( | |
'dir' => '.', | |
); | |
public function render(Result $__result) { | |
extract($__result->vars); | |
ob_start(); | |
include $this->options['dir'] . '/' . $__result->template; | |
$content = ob_get_clean(); | |
return $content; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class Container extends ArrayObject { | |
private $services = array(); | |
public function has($id) { | |
return $this->offsetExists($id); | |
} | |
public function get($id) { | |
if (array_key_exists($id, $this->services)) { | |
return $this->services[$id]; | |
} | |
$service = $this->offsetGet($id); | |
if ($service instanceof Closure) { | |
$service = $this->services[$id] = $service($this); | |
} | |
return $service; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class ServerErrorException extends Exception {} | |
class NotFoundException extends Exception {} | |
class Request { | |
public $uri; | |
public $query; | |
public function __construct($uri, array $query) { | |
$this->uri = $uri; | |
$this->query = $query; | |
} | |
public static function fromEnviron() { | |
$uri = $_SERVER['REQUEST_URI']; | |
if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) { | |
$uri = substr($uri, strlen($_SERVER['SCRIPT_NAME'])); | |
} | |
if (($qs = strpos($uri, '?')) !== false) { | |
$uri = substr($uri, 0, $qs); | |
} | |
return new static($uri, $_GET); | |
} | |
} | |
class Response { | |
public $content; | |
public $code; | |
public function __construct($content, $code = 200) { | |
$this->content = $content; | |
$this->code = $code; | |
} | |
public function send() { | |
http_response_code($this->code); | |
print($this->content); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class Router { | |
private $root; | |
private $routes = array(); | |
public function __construct($root) { | |
$this->root = $root; | |
} | |
public function addRoute($model, $action, $controller, $view) { | |
$this->routes[] = array( | |
'model' => $model, | |
'action' => $action, | |
'controller' => $controller, | |
'view' => $view, | |
); | |
} | |
public function match(array $segments) { | |
list($model, $action) = $this->traverse($this->root, $segments); | |
foreach ($this->routes as $route) { | |
if ($action === $route['action'] && | |
($route['model'] === null || $model instanceof $route['model']) | |
) { | |
return array( | |
'model' => $model, | |
'action' => $action, | |
'controller' => $route['controller'], | |
'view' => $route['view'], | |
); | |
} | |
} | |
throw new NotFoundException( | |
sprintf("%s not found", implode('/', $segments)) | |
); | |
} | |
public function matchRequest(Request $request) { | |
$segments = array_filter(explode('/', $request->uri)); | |
return $this->match($segments); | |
} | |
public function assemble($resource, $extra = null) { | |
$parts = array(); | |
while ($resource !== null) { | |
if (!is_object($resource) || | |
!property_exists($resource, '__parent') || | |
!property_exists($resource, '__name') | |
) { | |
throw new LogicException( | |
'The assembler needs a full tree' . | |
' of location aware resources' | |
); | |
} | |
$parts[] = $resource->__name; | |
$resource = $resource->__parent; | |
} | |
$segments = array_values(array_filter(array_reverse($parts))); | |
if ($extra !== null) { | |
$segments = array_merge($segments, array_values((array) $extra)); | |
} | |
return $segments; | |
} | |
protected function traverse($root, array $segments) { | |
foreach ($segments as $segment) { | |
if (!is_array($root) && | |
!$root instanceof ArrayAccess || | |
!isset($root[$segment]) | |
) { | |
return array($root, $segment); | |
} | |
$root = $root[$segment]; | |
} | |
return array($root, ''); | |
} | |
} | |
class Resource extends ArrayObject { | |
public $__parent; | |
public $__name; | |
public function offsetGet($name) | |
{ | |
$resource = parent::offsetGet($name); | |
$resource->__parent = $this; | |
$resource->__name = $name; | |
return $resource; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require __DIR__ . '/../autoload.php'; | |
$app = new Application($container); | |
$app->run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment