Skip to content

Instantly share code, notes, and snippets.

@jm42
Created October 30, 2014 04:31
Show Gist options
  • Save jm42/bf14542932e585ae01ff to your computer and use it in GitHub Desktop.
Save jm42/bf14542932e585ae01ff to your computer and use it in GitHub Desktop.
MVC Framework Test
<?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',
);
<?php
function search_controller(
Request $request,
Response $response,
Searchable $model
) {
if (isset($request->query['criteria'])) {
$model->setCriteria($request->query['criteria']);
}
}
<?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;
}
}
<pre><a href="/users"><code>/users</code></a></pre>
<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>
<h2><?= $user->name ?></h2>
<a href="<?= $this->assemble($user->__parent) ?>?criteria=<?= $user->name ?>">Go back</a>
<?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,
));
}
<?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';
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();
}
}
<?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');
<?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;
}
}
<?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;
}
}
<?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);
}
}
<?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;
}
}
<?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