Last active
March 3, 2017 23:05
-
-
Save Nicd/cca4875b8615349aead4004c9f13971b to your computer and use it in GitHub Desktop.
PHP project base with MVT model, simple middleware system, DB basics, router, templates with shared layout
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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<meta name="description" content="Blah blah lorem ipsum."> | |
<meta name="author" content="Mikko Ahlroth"> | |
<title>My Great Website</title> | |
<link rel="stylesheet" href="/app.css"> | |
</head> | |
<body> | |
<div class="container"> | |
<header class="header"> | |
<div class="row"> | |
<div class="col-xs-2"> | |
<a class="logo" href="/"><img src="/my_great_logo.png" alt="Best site" title="Ever made" height="100%" /></a> | |
</div> | |
<div class="col-xs-10"> | |
<nav role="navigation"> | |
<ul class="nav nav-pills pull-right"> | |
<li><a href="/login">Log in</a></li> | |
<li><a href="/signup">Sign up</a></li> | |
</ul> | |
</nav> | |
</div> | |
</div> | |
</header> | |
<main role="main"> | |
<?php $render_inner(); ?> | |
</main> | |
<footer> | |
<hr /> | |
<p class="text-center"> | |
<a href="/changes">0.0.1</a> | |
— | |
<?php list($time, $unit) = calculate_request_time($request_start_time); ?> | |
<?= $time ?> <?= $unit ?> | |
— | |
<a href="/api">API docs</a> | |
— | |
<a href="/terms">Legal</a> | |
— | |
<a href="/plugins">Plugins</a> | |
— | |
<a href="/irc">IRC</a> | |
</p> | |
<p class="text-center"> | |
Made with PHP 7. | |
</p> | |
</footer> | |
</div> <!-- /container --> | |
</body> | |
</html> |
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 | |
// Name of default layout file | |
const DEFAULT_LAYOUT = 'app'; | |
const DB_HOST = 'localhost'; | |
const DB_PORT = 5432; | |
const DB_NAME = 'blah'; | |
const DB_USER = 'blah'; | |
const DB_PASS = '1234'; |
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 | |
/** | |
* Utility class for database stuff. | |
*/ | |
class DB { | |
private $pdo; | |
public function __construct($db_host, $db_port, $db_name, $db_user, $db_pass) { | |
$dsn = "pgsql:host=$db_host;port=$db_port;dbname=$db_name;user=$db_user;password=$db_pass"; | |
//$this->pdo = new PDO($dsn); | |
//$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); | |
//$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); | |
} | |
public function getPDO() { | |
return $this->pdo; | |
} | |
} |
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_once 'utils.php'; | |
const TOTAL_XP_CACHE = [ | |
['C++', 16625], | |
['Elixir', 15316], | |
['Java', 215], | |
['Elm', 315531], | |
['C', 3155], | |
['Qwerty', 316361], | |
['Plain text', 57427], | |
['Python', 42747] | |
]; | |
function frontpage($request_data, $db) { | |
// Load total language XPs from "cache" | |
$total_lang_xps = TOTAL_XP_CACHE; | |
usort($total_lang_xps, function ($a, $b) { | |
if ($a[1] === $b[1]) { | |
return 0; | |
} | |
return ($a[1] > $b[1]) ? -1 : 1; | |
}); | |
$total_xp = array_reduce($total_lang_xps, function ($acc, $lang) { | |
return $acc + $lang[1]; | |
}, 0); | |
$most_popular = array_slice($total_lang_xps, 0, 10); | |
// Render template | |
render_with_layout('front', array_merge($request_data, [ | |
'total_lang_xps' => $total_lang_xps, | |
'total_xp' => $total_xp, | |
'most_popular' => $most_popular | |
])); | |
} |
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
<div class="jumbotron"> | |
<h2>Welcome to My Great Website!</h2> | |
<p class="lead">Write code, level up, show off! A free stats tracking service for programmers.</p> | |
</div> | |
<div class="row"> | |
<div class="col-xs-12"> | |
<hr /> | |
</div> | |
</div> | |
<div class="row" id="index-elm-container"> | |
<div class="col-xs-12"> | |
<div class="row"> | |
<div class="col-xs-12 col-md-6"> | |
<h3> | |
Total XP | |
</h3> | |
<h2> | |
<?= format_xp($total_xp) ?> | |
</h2> | |
</div> | |
<div class="col-xs-12 col-md-6"> | |
<h3> | |
Most popular languages | |
</h3> | |
<ol> | |
<?php foreach($most_popular as $tuple): ?> | |
<li> | |
<?= $tuple[0] ?>: <?= format_xp($tuple[1]) ?> XP | |
</li> | |
<?php endforeach; ?> | |
</ol> | |
</div> | |
</div> | |
</div> | |
</div> |
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 | |
// Convert all errors to exceptions | |
function exception_error_handler($severity, $message, $file, $line) { | |
if (!(error_reporting() & $severity)) { | |
return; | |
} | |
throw new ErrorException($message, 0, $severity, $file, $line); | |
} | |
set_error_handler("exception_error_handler"); | |
require_once 'utils.php'; | |
require_once 'config.php'; | |
require_once 'db.php'; | |
require_once 'router.php'; | |
require_once 'routes.php'; | |
$db = new DB(DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASS); | |
$router = new Router(ROUTES, $db); | |
$router->run(); |
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 | |
// Plugs are simple middleware that transform the request. They get | |
// the request data and database handle as arguments and must return | |
// the new request data that is passed forward to the next plug, or | |
// render content and return false to halt processing of the request. | |
// Set the request start time into the request | |
function request_time($request_data, $db) { | |
$request_data['request_start_time'] = microtime(true); | |
return $request_data; | |
} |
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 profilepage($request_data, $db) { | |
$username = substr($_SERVER['PATH_INFO'], strlen('/users/')); | |
$user = get_user($db, $username); | |
render_with_layout('profile', array_merge($request_data, [ | |
'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
<div class="row"> | |
<div class="col-xs-12 col-sm-5"> | |
<h2 | |
id="profile-username" | |
data-name="<?= $user['username'] ?>" | |
> | |
<?= $user['username'] ?> | |
</h2> | |
<ul class="profile-detail-list"> | |
<li> | |
Programming since | |
<time datetime="<?= $user['inserted_at']->format('Y-m-d') ?>"> | |
<?= $user['inserted_at']->format('Y-m-d') ?></time>. | |
</li> | |
</ul> | |
</div> | |
</div> |
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 | |
/** | |
* Simple router that calls the given page function based on the request URL. | |
* | |
* The given routes list should contain 3-tuples of: | |
* 1. Regex for matching the URL | |
* 2. Function to execute on a match | |
* 3. Plugs to use in request. Plugs are passed $request_data and should return a new | |
* copy of it which will be passed to the next plug and then to the request function. | |
* The second argument passed to all plugs and the request function is the database | |
* handler ($db). | |
* | |
* The first match is used. If there is no match, a 404 error is thrown. | |
*/ | |
class Router { | |
private $routes; | |
private $db; | |
private $request_data; | |
public function __construct($routes, $db) { | |
$this->routes = $routes; | |
$this->db = $db; | |
$this->request_data = []; | |
} | |
public function run() { | |
$path = $_SERVER['PATH_INFO'] ?? '/'; | |
foreach ($this->routes as $route) { | |
if (preg_match($route[0], $path)) { | |
foreach ($route[2] as $plug) { | |
$new_data = call_user_func($plug, $this->request_data, $this->db); | |
// If plug returned false, stop processing | |
if ($new_data === false) { | |
return; | |
} | |
$this->request_data = $new_data; | |
} | |
call_user_func($route[1], $this->request_data, $this->db); | |
return; | |
} | |
} | |
} | |
} |
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_once 'plugs.php'; | |
require_once 'front.page.php'; | |
require_once 'profile.page.php'; | |
const ROUTES = [ | |
['(^/users/)', 'profilepage', ['request_time']], | |
['(^/$)', 'frontpage', ['request_time']] | |
]; |
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_once 'config.php'; | |
// Calculate time taken from given time to current time | |
function calculate_request_time($time) { | |
$now = microtime(true); | |
$diff = $now - $time; | |
return humanize_seconds($diff); | |
} | |
const UNIT_MAPPING = [ | |
[1, 's'], | |
[1000, 'ms'], | |
[1000 * 1000, 'µs'], | |
[1000 * 1000 * 1000, 'ns'] | |
]; | |
function humanize_seconds($diff) { | |
foreach (UNIT_MAPPING as $unit) { | |
$m = $diff * $unit[0]; | |
if ($m > 1) { | |
return [$m, $unit[1]]; | |
} | |
} | |
} | |
/** | |
* Render the given template file with the given data extracted as variables | |
* and embed it inside the given layout. | |
* | |
* The given layout must contain the command $render_inner() which will be replaced | |
* with the inner content. | |
*/ | |
function render_with_layout($template, $data, $layout=DEFAULT_LAYOUT) { | |
$render_inner = function () use ($template, $data) { | |
extract($data, EXTR_SKIP); | |
require "${template}.tpl.php"; | |
}; | |
extract($data, EXTR_SKIP); | |
require "${layout}.tpl.php"; | |
} | |
// Get from array with default value | |
function get($arr, $key, $default=null) { | |
if (array_key_exists($key, $arr)) { | |
return $arr[$key]; | |
} | |
return $default; | |
} | |
// Format XP nicely | |
function format_xp($xp) { | |
return number_format($xp); | |
} | |
// Get user from database (now just returns fake user) | |
function get_user($db, $username) { | |
//$pdo = $db->getPDO(); | |
//$q = $pdo->prepare('SELECT u0."id", u0."username", u0."email", u0."inserted_at" FROM "users" AS u0 WHERE (u0."username" = ?)'); | |
//$q->bindValue(1, $username, PDO::PARAM_STR); | |
//$q->execute(); | |
//return $q->fetch(PDO::FETCH_ASSOC); | |
return [ | |
'username' => $username, | |
'inserted_at' => new DateTimeImmutable('now') | |
]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment