Skip to content

Instantly share code, notes, and snippets.

@yusufusta
Last active November 25, 2022 17:49
Show Gist options
  • Save yusufusta/d6c5dc861efa111808a6025daf9ce925 to your computer and use it in GitHub Desktop.
Save yusufusta/d6c5dc861efa111808a6025daf9ce925 to your computer and use it in GitHub Desktop.
Proof that PHP works well without any framework.
<?php
function redirect(string $url)
{
header("Location: $url");
die();
}
function json(array $arr)
{
die(json_encode($arr));
}
function text($text)
{
die($text);
}
class BasicPHPFramework
{
/**
* Flash messages session key name.
*
* @var string
*/
private string $FLASH = 'FLASH_MESSAGES';
/**
* @var array
*/
private array $PAGES = [];
/**
* @var array
*/
private array $MIDS = [];
/**
* @var array
*/
private array $CONFIG = [];
/**
* PDO object.
*
* @var PDO
*/
public PDO $pdo;
/**
* Router URL matches.
*
* @var array
*/
public array $matches;
/**
* HTTP status codes helper.
*
* @var array
*/
public array $httpStatusCodes = [100 => "Continue", 101 => "Switching Protocols", 102 => "Processing", 200 => "OK", 201 => "Created", 202 => "Accepted", 203 => "Non-Authoritative Information", 204 => "No Content", 205 => "Reset Content", 206 => "Partial Content", 207 => "Multi-Status", 300 => "Multiple Choices", 301 => "Moved Permanently", 302 => "Found", 303 => "See Other", 304 => "Not Modified", 305 => "Use Proxy", 306 => "(Unused)", 307 => "Temporary Redirect", 308 => "Permanent Redirect", 400 => "Bad Request", 401 => "Unauthorized", 402 => "Payment Required", 403 => "Forbidden", 404 => "Not Found", 405 => "Method Not Allowed", 406 => "Not Acceptable", 407 => "Proxy Authentication Required", 408 => "Request Timeout", 409 => "Conflict", 410 => "Gone", 411 => "Length Required", 412 => "Precondition Failed", 413 => "Request Entity Too Large", 414 => "Request-URI Too Long", 415 => "Unsupported Media Type", 416 => "Requested Range Not Satisfiable", 417 => "Expectation Failed", 418 => "I'm a teapot", 419 => "Authentication Timeout", 420 => "Enhance Your Calm", 422 => "Unprocessable Entity", 423 => "Locked", 424 => "Failed Dependency", 424 => "Method Failure", 425 => "Unordered Collection", 426 => "Upgrade Required", 428 => "Precondition Required", 429 => "Too Many Requests", 431 => "Request Header Fields Too Large", 444 => "No Response", 449 => "Retry With", 450 => "Blocked by Windows Parental Controls", 451 => "Unavailable For Legal Reasons", 494 => "Request Header Too Large", 495 => "Cert Error", 496 => "No Cert", 497 => "HTTP to HTTPS", 499 => "Client Closed Request", 500 => "Internal Server Error", 501 => "Not Implemented", 502 => "Bad Gateway", 503 => "Service Unavailable", 504 => "Gateway Timeout", 505 => "HTTP Version Not Supported", 506 => "Variant Also Negotiates", 507 => "Insufficient Storage", 508 => "Loop Detected", 509 => "Bandwidth Limit Exceeded", 510 => "Not Extended", 511 => "Network Authentication Required", 598 => "Network read timeout error", 599 => "Network connect timeout error"];
/**
* BasicPHPFramework constructor. Set the config here.
*
* @param array $config The config array.
*
* $config = [
* 'db' => [(string) DSN, (string) Username, (string) Password],
* 'assets_folder' => string,
* ];
*/
public function __construct($config = [])
{
$this->CONFIG = $config;
if (!empty($config["db"])) {
$this->pdo = new PDO($config["db"][0], $config["db"][1], $config["db"][2]);
}
if (!empty($config["assets_folder"])) {
$assets = $config["assets_folder"];
$this->addPage("GET_ASSETS", "/assets/(.*)", function (self $fw) use ($assets) {
$filename = $assets . "/" . $fw->matches[1];
if (!is_file($filename)) {
return $this->callHttpResponse(404);
}
header('Content-Type: ' . $this->mimeContentType($filename));
readfile($filename);
die();
});
}
if (empty($config["view"])) {
$config["view"] = "pages";
}
}
/**
* Add an middleware to the framework.
*
* @param Closure $function
* @return BasicPHPFramework
*/
public function add(Closure $function): self
{
$this->MIDS[] = [
$function
];
return $this;
}
/**
* Add a page to the framework.
*
* @param string $method
* @param string $route
* @param Closure $function
* @return BasicPHPFramework
*/
public function addPage($method, $url, Closure $function): self
{
if (is_array($method) && is_array($url)) {
foreach ($method as $m) {
foreach ($url as $u) {
$this->addPage($m, $u, $function);
}
}
return $this;
}
if (is_array($method)) {
foreach ($method as $m) {
$this->addPage($m, $url, $function);
}
return $this;
}
if (is_array($url)) {
foreach ($url as $u) {
$this->addPage($method, $u, $function);
}
return $this;
}
$this->PAGES[] = [
$method,
$url,
$function
];
return $this;
}
/**
* Run the Framework.
*
* @return void
*/
public function run()
{
$status_code = 404;
$url = $_SERVER['REQUEST_URI'];
foreach ($this->PAGES as $page) {
if (!empty($_GET) && strstr($url, "?")) {
$url = strstr($url, "?", true);
}
if (!preg_match("/^" . (str_replace("/", "\/", $page[1])) . "$/m", $url, $matches)) {
continue;
}
$this->matches = $matches;
if (!in_array(strtoupper($page[0]), ["*", "GET_ASSETS"]) && $_SERVER['REQUEST_METHOD'] !== strtoupper($page[0])) {
$status_code = 405;
continue;
}
$status_code = 200;
break;
}
if ($status_code === 200 && $page[0] === "GET_ASSETS") {
return call_user_func($page[2], $this);
}
foreach ($this->MIDS as $mid) {
call_user_func($mid[0], $this, [$status_code, $page]);
}
if ($status_code === 200) {
return call_user_func($page[2], $this);
}
return $this->callHttpResponse($status_code);
}
/**
* Call the HTTP Response.
*
* @param int $status_code
* @return never
*/
public function callHttpResponse($code = 404)
{
if (!empty($this->CONFIG["status_codes"][$code])) {
call_user_func($this->CONFIG["status_codes"][$code], $this);
} else if (array_key_exists($code, $this->httpStatusCodes)) {
http_response_code($code);
die("<html><head><title>" . $code . " " . $this->httpStatusCodes[$code] . "</title></head><body><h1>" . $code . "</h1><h2>" . $this->httpStatusCodes[$code] . "</h2></body></html>");
} else {
http_response_code(500);
die("<html><head><title>500 Internal Server Error</title></head><body><h1>500</h1><br><h2>Internal Server Error -- BasicPHPFramework error</h2></body></html>");
}
}
/**
* Get the MIME Content Type.
*
* @param string $filename
* @return string
*/
function mimeContentType($filename)
{
$mime_types = [
'txt' => 'text/plain',
'htm' => 'text/html',
'html' => 'text/html',
'php' => 'text/html',
'css' => 'text/css',
'js' => 'application/javascript',
'json' => 'application/json',
'xml' => 'application/xml',
'swf' => 'application/x-shockwave-flash',
'flv' => 'video/x-flv',
'png' => 'image/png',
'jpe' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg',
'gif' => 'image/gif',
'bmp' => 'image/bmp',
'ico' => 'image/vnd.microsoft.icon',
'tiff' => 'image/tiff',
'tif' => 'image/tiff',
'svg' => 'image/svg+xml',
'svgz' => 'image/svg+xml',
'zip' => 'application/zip',
'rar' => 'application/x-rar-compressed',
'exe' => 'application/x-msdownload',
'msi' => 'application/x-msdownload',
'cab' => 'application/vnd.ms-cab-compressed',
'mp3' => 'audio/mpeg',
'qt' => 'video/quicktime',
'mov' => 'video/quicktime',
'pdf' => 'application/pdf',
'psd' => 'image/vnd.adobe.photoshop',
'ai' => 'application/postscript',
'eps' => 'application/postscript',
'ps' => 'application/postscript',
'doc' => 'application/msword',
'rtf' => 'application/rtf',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
];
$ext = pathinfo($filename, PATHINFO_EXTENSION);
if (array_key_exists($ext, $mime_types)) {
return $mime_types[$ext];
} elseif (function_exists('finfo_open')) {
$finfo = finfo_open(FILEINFO_MIME);
$mimetype = finfo_file($finfo, $filename);
finfo_close($finfo);
return $mimetype;
} elseif (function_exists('mime_content_type')) {
return mime_content_type($filename);
} else {
return 'application/octet-stream';
}
}
/**
* Create or get one|all flash messages.
*
* @param string $name
* @param string $message
* @param string $type
* @return void
*/
public function flash(string $name = '', string $message = '', string $type = '')
{
if ($name !== '' && $message !== '' && $type !== '') {
if (isset($_SESSION[$this->FLASH][$name])) {
unset($_SESSION[$this->FLASH][$name]);
}
$_SESSION[$this->FLASH][$name] = ['message' => $message, 'type' => $type];
return true;
} elseif ($name !== '' && $message === '' && $type === '') {
if (!isset($_SESSION[$this->FLASH][$name])) {
return false;
}
$msg = $_SESSION[$this->FLASH][$name];
unset($_SESSION[$this->FLASH][$name]);
return $msg;
} elseif ($name === '' && $message === '' && $type === '') {
if (!isset($_SESSION[$this->FLASH])) {
return false;
}
$msg = $_SESSION[$this->FLASH];
unset($_SESSION[$this->FLASH]);
return $msg;
}
}
/**
* Check array is associative.
*
* @param array $arr
* @return boolean
*/
public static function isAssoc(array $arr)
{
if (array() === $arr) return false;
return array_keys($arr) !== range(0, count($arr) - 1);
}
/**
* Run query and return results.
*
* @param string $query
* @param array $params
* @return (PDOStatement|bool)[]
*/
public function query(string $query, array $params = []): array
{
if (empty($this->CONFIG["db"])) {
throw new Exception("No DB connection found. Please set a DB connection in the config array.");
}
$stmt = $this->pdo->prepare($query);
if (empty($params)) {
$exec = $stmt->execute();
} else if (self::isAssoc($params)) {
foreach ($params as $key => $value) {
$stmt->bindValue(($key[0] == ":" ? "" : ":") . $key, $value);
}
$exec = $stmt->execute();
} else {
$exec = $stmt->execute($params);
}
return [$stmt, $exec];
}
/**
* Return fetch all results.
*
* @param string $query
* @param array $params
* @return array
*/
public function fetchAll(string $query, array $params = []): array
{
if (empty($this->CONFIG["db"])) {
throw new Exception("No DB connection found. Please set a DB connection in the config array.");
}
[$stmt, $exec] = $this->query($query, $params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* Return fetch one result.
*
* @param string $query
* @param array $params
* @return array
*/
public function fetch(string $query, array $params = [])
{
if (empty($this->CONFIG["db"])) {
throw new Exception("No DB connection found. Please set a DB connection in the config array.");
}
[$stmt, $exec] = $this->query($query, $params);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
/**
* Get row count of query.
*
* @param string $query
* @param array $params
* @return int
*/
public function rowCount(string $query, array $params = []): int
{
if (empty($this->CONFIG["db"])) {
throw new Exception("No DB connection found. Please set a DB connection in the config array.");
}
[$stmt, $exec] = $this->query($query, $params);
return $stmt->rowCount();
}
public function view($file, $data = [])
{
$output = "";
$file = __DIR__ . "/" . $this->CONFIG["view"] . "/" . $file . ".php";
if (file_exists($file)) {
extract($data);
ob_start();
include($file);
$output = ob_get_clean();
} else {
return $this->callHttpResponse(404);
}
echo ($output);
die;
}
}
@yahyaemre
Copy link

güzel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment