Building a Minimalist MVC Framework in PHP from Scratch

Autour d’un point (cut) by František Kupka
Autour d’un point (cut) by František Kupka

Routing

// mvc/Dispatcher.phpclass Router {
// 'METHOD path' => action
private $routing = [];
}
[
'/' => function() { ...},
'POST /abc'=> function() { ... },
'/abc'=> function() { … },
'/abc/{id}'=> function($params) { … },
'GET /abc/{id}/xyz'=> function($params) { … },
'/abc/{id1}/xyz/{id2}'=> function($params) { … }
]
// mvc/Dispatcher.phpclass Router {
private $routing = [];
function addRouting($pattern, $action) {
$this->routing[$pattern] = $action;
}
}
// mvc/Dispatcher.phpclass Router {
private $routing = [];
function addRouting($pattern, $action) { … } function route($method, $path, $params) {
$path = "{$method} /{$path}";
foreach ($this->routing as $pattern => $handler) {
// route parameters as regex
$patternParams = $this->patternParams($pattern);
if (!empty($patternParams)) {
$pattern = $this->withParams($pattern);
}
// add GET into the pattern if necessary
$pattern = $this->withMethod($pattern);
// if the request matches, $params array is filled
if ($this->requestMatches(
$pattern, $path, $patternParams, $params)) {
$handler($params); // execute action
return;
}
}
http_response_code(404);
$this->route['/']([]);
}
}
// mvc/Dispatcher.phpclass Dispatcher {
private $router;
function __construct() {
$this->router = new Router();
}
function dispatch() {
$this->router->route(
$_SERVER['REQUEST_METHOD'],
$_SERVER['REQUEST_URI'],
$_REQUEST);
}
}
// mvc/Dispatcher.phpclass Dispatcher {
private $router;
function __construct() { … } function dispatch() { … } function routing($pattern, $action) {
$this->router->addRouting($pattern, $action);
return $this;
}
}
// index.php(new Dispatcher())
->routing('/hello/{user}', function($params) {
echo "Hello, {$params['user']}!";
})
->dispatch();
curl http://localhost:8080/hello/user123
Hello, user123!

View and Model

// views/layouts/html.php<!doctype html>
<html lang="en">
<head>
<title>Minimalist MVC Framework</title>
<meta charset="utf-8">
</head>
<body>
<main>
<?= $this->content() ?>
</main>
<footer>
&copy; <?= date('Y') ?> by PHP
</footer>
</body>
</html>
// views/layouts/xml.php<?php
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
echo $this->content();
// views/hello.php<h1>Hello, <?= $this->user ?>!</h1>
<p>Make yourself at home.</p>
// mvc/ModelView.phpclass ModelView {
private $name;
private $model;
private $type;
public function __construct($name, $model, $type = 'html') {
$this->name = $name;
$this->model = $model;
$this->type = $type;
}
final function render() {
switch ($this->type) {
case 'xml':
header('Content-type: text/xml; charset=UTF-8');
break;
case 'json':
header("Content-Type: application/json; charset=UTF-8");
break;
default:
header("Content-Type: text/html; charset=UTF-8");
}
require_once "./layout/{$this->type}.php";
}
final function content() {
ob_start();
require_once "./views/{$this->name}.php";
$out = ob_get_contents();
ob_end_clean();
return $out;
}
function __get($key) {
return isset($this->model[$key])
? $this->model[$key]
: "__{$key}__";
}
}

Controller

// mvc/Controller.phpabstract class Controller {
private $model = [];
function render($name, $type = 'html') {
return new ModelView($name, $this->model, $type);
}
function addModelAttribute($key, $value) {
$this->model[$key] = $value;
}
}
// controllers/HelloController.phpclass HelloController extends Controller {  function sayHello($params) {
$this->addModelAttribute('user', $params['user']);
$this->render('hello');
}
}

Put It All Together

// index.php(new Dispatcher())
->routing('/hello/{user}', function($params) {
(new HelloController())->sayHello($params);
})
->dispatch();
curl http://localhost:8080/hello/user123
<!doctype html>
<html lang="en">
<head>
<title>Minimalist MVC Framework</title>
<meta charset="utf-8">
</head>
<body>
<main>
<h1>Hello, user123!</h1>
<p>Make yourself at home.</p>
</main>
<footer>
&copy; 2020 by PHP
</footer>
</body>
</html>

Conclusion

--

--

--

Software developer and occasional blogger: https://blog.ttulka.com

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Getting error similar to “mapping set to strict, dynamic introduction of [location] within [_doc]…

CI/CD pipeline integrating Kubernetes, Github and Jenkins

Laravel Blade Helpers

Handling Failures in Distributed Systems!!

GPS/WEB BASED VEHICLE ROUTE PLANNING APP

January Favourites. The Tech edition. 2020

Microservices Orchestration vs Choreography the Eternal Saga…

A simple Database schema migration approach in .NET

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Tomas Tulka

Tomas Tulka

Software developer and occasional blogger: https://blog.ttulka.com

More from Medium

Create an array and access array elements in PHP

Node.js or PHP — Which is better?

Up and Running with Model in Laravel — Advance use of Model

How to update a mysql column’s data type for an already migrated Table in PHP