Init
This commit is contained in:
8
.env.default
Normal file
8
.env.default
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
CONNECTIONS=
|
||||||
|
DB_DEFAULT_TYPE=mysql
|
||||||
|
DB_DEFAULT_HOST=localhost
|
||||||
|
DB_DEFAULT_PORT=3306
|
||||||
|
DB_DEFAULT_DATABASE=database
|
||||||
|
DB_DEFAULT_USER=root
|
||||||
|
DB_DEFAULT_PASSWORD=password
|
||||||
|
DEBUG=true
|
||||||
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
vendor/
|
||||||
|
.phpunit.result.cache
|
||||||
|
!var/cache/.gitkeep
|
||||||
|
var/cache/*
|
||||||
|
!var/log/.gitkeep
|
||||||
|
var/log/*
|
||||||
|
.env
|
||||||
|
.vscode
|
||||||
6
.phpcs.xml
Normal file
6
.phpcs.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<ruleset>
|
||||||
|
<rule ref="PSR12"/>
|
||||||
|
<file>src/</file>
|
||||||
|
<arg name="colors"/>
|
||||||
|
</ruleset>
|
||||||
45
Makefile
Normal file
45
Makefile
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
COMPOSER ?= $(shell which composer)
|
||||||
|
|
||||||
|
.PHONY: init
|
||||||
|
init:
|
||||||
|
$(RM) -r .git
|
||||||
|
git init
|
||||||
|
|
||||||
|
.PHONY: up
|
||||||
|
up: install-vendor
|
||||||
|
|
||||||
|
.PHONY: install-vendor
|
||||||
|
install-vendor:
|
||||||
|
$(PHP) $(COMPOSER) install
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean: clean-vendor clean-composerlock
|
||||||
|
|
||||||
|
.PHONY: clean-vendor
|
||||||
|
clean-vendor:
|
||||||
|
$(RM) -r ./vendor
|
||||||
|
|
||||||
|
.PHONY: clean-composerlock
|
||||||
|
clean-composerlock:
|
||||||
|
$(RM) composer.lock
|
||||||
|
|
||||||
|
.PHONY: unit-tests
|
||||||
|
unit-tests:
|
||||||
|
./vendor/bin/pest
|
||||||
|
|
||||||
|
.PHONY: unit-tests-coverage
|
||||||
|
unit-tests-coverage:
|
||||||
|
XDEBUG_MODE=coverage ./vendor/bin/pest --coverage
|
||||||
|
|
||||||
|
.PHONY: mutation
|
||||||
|
mutation:
|
||||||
|
./vendor/bin/infection --test-framework=pest --show-mutations
|
||||||
|
|
||||||
|
.PHONY: code-sniffer
|
||||||
|
code-sniffer:
|
||||||
|
./vendor/bin/phpcs
|
||||||
|
|
||||||
|
.PHONY: code-sniffer-fix
|
||||||
|
code-sniffer-fix:
|
||||||
|
./vendor/bin/phpcbf
|
||||||
|
|
||||||
42
composer.json
Normal file
42
composer.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"name": "kletellier/web",
|
||||||
|
"description": "Mini website template",
|
||||||
|
"type": "project",
|
||||||
|
"license": "MIT",
|
||||||
|
"require": {
|
||||||
|
"php": "~8.1",
|
||||||
|
"kletellier/pdowrapper": "dev-master",
|
||||||
|
"symfony/http-kernel": "^6.3",
|
||||||
|
"bramus/router": "~1.6",
|
||||||
|
"filp/whoops": "^2.15",
|
||||||
|
"symfony/var-dumper": "^6.3"
|
||||||
|
},
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.kletellier.xyz/greg/PdoWrapper.git"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require-dev": {
|
||||||
|
"squizlabs/php_codesniffer": "^3.4",
|
||||||
|
"pestphp/pest": "^1.21",
|
||||||
|
"infection/infection": "^0.26.15"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"App\\": "src"
|
||||||
|
},
|
||||||
|
"files": ["src/helpers.php"]
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"App\\tests\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"allow-plugins": {
|
||||||
|
"pestphp/pest-plugin": true,
|
||||||
|
"infection/extension-installer": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5180
composer.lock
generated
Normal file
5180
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
17
infection.json5
Normal file
17
infection.json5
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"$schema": "vendor/infection/infection/resources/schema.json",
|
||||||
|
"source": {
|
||||||
|
"directories": [
|
||||||
|
"src"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"phpUnit": {
|
||||||
|
"configDir": ""
|
||||||
|
},
|
||||||
|
"logs": {
|
||||||
|
"text": "./var/log/infection.log"
|
||||||
|
},
|
||||||
|
"mutators": {
|
||||||
|
"@default": true
|
||||||
|
}
|
||||||
|
}
|
||||||
18
phpunit.xml
Normal file
18
phpunit.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
colors="true"
|
||||||
|
cacheResultFile="./var/cache/"
|
||||||
|
>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Test Suite">
|
||||||
|
<directory suffix="Test.php">./tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<coverage processUncoveredFiles="true" cacheDirectory="./var/cache/">
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">./src</directory>
|
||||||
|
</include>
|
||||||
|
</coverage>
|
||||||
|
</phpunit>
|
||||||
9
public/.htaccess
Normal file
9
public/.htaccess
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
|
||||||
|
# Rewrite all other URLs to index.php/URL
|
||||||
|
RewriteRule ^(.*)$ index.php?url=$1 [QSA]
|
||||||
|
|
||||||
|
ErrorDocument 404 index.php
|
||||||
20
public/index.php
Normal file
20
public/index.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loading path constant
|
||||||
|
*/
|
||||||
|
define('DS', DIRECTORY_SEPARATOR);
|
||||||
|
define('ROOT', dirname(dirname(__FILE__)));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable autoload
|
||||||
|
*/
|
||||||
|
require_once ROOT . DS . 'vendor'. DS . 'autoload.php';
|
||||||
|
|
||||||
|
|
||||||
|
// start application
|
||||||
|
$app = new \App\Core\Kernel\Application();
|
||||||
|
|
||||||
|
// Handle Url and render Response
|
||||||
|
$app->handle();
|
||||||
3
routes/routes.php
Normal file
3
routes/routes.php
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$router->get('/', '\App\Controllers\Home@index');
|
||||||
0
src/.gitkeep
Normal file
0
src/.gitkeep
Normal file
14
src/Controllers/Home.php
Normal file
14
src/Controllers/Home.php
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
use App\Core\Controllers\Controller;
|
||||||
|
|
||||||
|
class Home extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$html = $this->renderView("index", []);
|
||||||
|
$this->render($html);
|
||||||
|
}
|
||||||
|
}
|
||||||
119
src/Core/Controllers/Controller.php
Normal file
119
src/Core/Controllers/Controller.php
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core\Controllers;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
abstract class Controller
|
||||||
|
{
|
||||||
|
protected $_cookies;
|
||||||
|
protected $_cookiestodelete;
|
||||||
|
protected Request $_request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller constructor
|
||||||
|
*
|
||||||
|
* @param String $controller controller name
|
||||||
|
* @param String $action action to execute
|
||||||
|
*/
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
$this->_cookies = array();
|
||||||
|
$this->_cookiestodelete = array();
|
||||||
|
$this->_request = Request::createFromGlobals();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render($content, $status = 200, $headers = array('Content-Type' => 'text/html')): void
|
||||||
|
{
|
||||||
|
$this->getResponse($content, $status, $headers)->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return instance of Symfony Response Component
|
||||||
|
* @return type
|
||||||
|
*/
|
||||||
|
private function getResponse($content, $status = 200, $headers = array('Content-Type' => 'text/html')): Response
|
||||||
|
{
|
||||||
|
$response = new Response($content, $status, $headers);
|
||||||
|
foreach ($this->_cookies as $cookie) {
|
||||||
|
$response->headers->setCookie($cookie);
|
||||||
|
}
|
||||||
|
foreach ($this->_cookiestodelete as $cookie) {
|
||||||
|
$response->headers->clearCookie($cookie);
|
||||||
|
}
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return instance of Symfony Request component
|
||||||
|
*/
|
||||||
|
public function request(): Request
|
||||||
|
{
|
||||||
|
return $this->_request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return Html from view
|
||||||
|
*/
|
||||||
|
public function renderView($view, $params = []): string
|
||||||
|
{
|
||||||
|
extract($params);
|
||||||
|
$path = ROOT . DS . "views" . DS . str_replace(".", DS, $view) . ".php";
|
||||||
|
ob_start();
|
||||||
|
try {
|
||||||
|
include $path;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
}
|
||||||
|
$buffer = ltrim(ob_get_clean());
|
||||||
|
return $buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add cookie to response
|
||||||
|
* @param type \Symfony\Component\HttpFoundation\Cookie $cookie
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addCookie(\Symfony\Component\HttpFoundation\Cookie $cookie)
|
||||||
|
{
|
||||||
|
array_push($this->_cookies, $cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cookie to remove in response
|
||||||
|
* @param string $cookie cookie name to delete
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function removeCookie($cookie)
|
||||||
|
{
|
||||||
|
array_push($this->_cookiestodelete, $cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a Json Response
|
||||||
|
*
|
||||||
|
* @param object $var object to be serialized in JSON
|
||||||
|
*/
|
||||||
|
public function renderJSON($var)
|
||||||
|
{
|
||||||
|
$response = new Response(json_encode($var));
|
||||||
|
$response->headers->set('Content-Type', 'application/json');
|
||||||
|
$response->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function redirecting($url)
|
||||||
|
{
|
||||||
|
$response = new \Symfony\Component\HttpFoundation\RedirectResponse($url);
|
||||||
|
foreach ($this->_cookies as $cookie) {
|
||||||
|
$response->headers->setCookie($cookie);
|
||||||
|
}
|
||||||
|
foreach ($this->_cookiestodelete as $cookie) {
|
||||||
|
$response->headers->clearCookie($cookie);
|
||||||
|
}
|
||||||
|
$response->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
function __destruct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
99
src/Core/Kernel/Application.php
Normal file
99
src/Core/Kernel/Application.php
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core\Kernel;
|
||||||
|
|
||||||
|
use App\Core\Routing\Router;
|
||||||
|
use Dotenv\Dotenv;
|
||||||
|
use Kletellier\PdoWrapper\Connection;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class Application
|
||||||
|
{
|
||||||
|
protected Router $router;
|
||||||
|
protected $_whoops;
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
// enable error reporting
|
||||||
|
$this->setReporting();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$dotenv = Dotenv::createImmutable(ROOT);
|
||||||
|
$dotenv->load();
|
||||||
|
} catch (\Throwable $th) {
|
||||||
|
e($th->getMessage());
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isDebug()) {
|
||||||
|
$this->_whoops = new \Whoops\Run();
|
||||||
|
$handler = new \Whoops\Handler\PrettyPageHandler();
|
||||||
|
$this->_whoops->pushHandler($handler);
|
||||||
|
$this->_whoops->register();
|
||||||
|
}
|
||||||
|
|
||||||
|
$db_config = $this->getDatabaseConfig();
|
||||||
|
|
||||||
|
if (!empty($db_config)) {
|
||||||
|
// Initialize DB system
|
||||||
|
Connection::init($db_config, array_keys($db_config));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init router
|
||||||
|
$this->router = new Router();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isDebug(): bool
|
||||||
|
{
|
||||||
|
$ret = true;
|
||||||
|
if (isset($_ENV["DEBUG"])) {
|
||||||
|
$ret = ($_ENV["DEBUG"] == "true");
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDatabaseConfig(): array
|
||||||
|
{
|
||||||
|
$ret = [];
|
||||||
|
$connections = isset($_ENV["CONNECTIONS"]) ? explode(",", $_ENV["CONNECTIONS"]) : [];
|
||||||
|
foreach ($connections as $connection) {
|
||||||
|
if ($connection !== "") {
|
||||||
|
$conn = [];
|
||||||
|
$key = (strtoupper(trim($connection)));
|
||||||
|
$conn["TYPE"] = isset($_ENV[$this->getKey("TYPE", $key)]) ? $_ENV[$this->getKey("TYPE", $key)] : null;
|
||||||
|
$conn["USER"] = isset($_ENV[$this->getKey("USER", $key)]) ? $_ENV[$this->getKey("USER", $key)] : null;
|
||||||
|
$conn["HOST"] = isset($_ENV[$this->getKey("HOST", $key)]) ? $_ENV[$this->getKey("HOST", $key)] : null;
|
||||||
|
$conn["PASSWORD"] = isset($_ENV[$this->getKey("PASSWORD", $key)]) ? $_ENV[$this->getKey("PASSWORD", $key)] : null;
|
||||||
|
$conn["DATABASE"] = isset($_ENV[$this->getKey("DATABASE", $key)]) ? $_ENV[$this->getKey("DATABASE", $key)] : null;
|
||||||
|
$conn["PORT"] = isset($_ENV[$this->getKey("PORT", $key)]) ? $_ENV[$this->getKey("PORT", $key)] : null;
|
||||||
|
$ret[$connection] = $conn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getKey(string $type, string $name): string
|
||||||
|
{
|
||||||
|
return "DB_" . $name . "_" . $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable/Disable error reporting to output buffer
|
||||||
|
*/
|
||||||
|
private function setReporting()
|
||||||
|
{
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('display_errors', 'On');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
ob_start(null, 0, PHP_OUTPUT_HANDLER_CLEANABLE | PHP_OUTPUT_HANDLER_FLUSHABLE);
|
||||||
|
try {
|
||||||
|
$this->router->run();
|
||||||
|
} catch (\Exception $ex) {
|
||||||
|
e($ex->getMessage());
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/Core/Routing/Router.php
Normal file
35
src/Core/Routing/Router.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core\Routing;
|
||||||
|
|
||||||
|
use Bramus\Router\Router as RouterRouter;
|
||||||
|
|
||||||
|
class Router
|
||||||
|
{
|
||||||
|
private $_router;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$this->_router->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function init()
|
||||||
|
{
|
||||||
|
$this->_router = new RouterRouter();
|
||||||
|
$this->loadFromFile(ROOT . DS . "routes");
|
||||||
|
$this->_router->set404("404 - Not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadFromFile($path)
|
||||||
|
{
|
||||||
|
if (is_dir($path)) {
|
||||||
|
$router = $this->_router;
|
||||||
|
require $path . DS . "routes.php";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/helpers.php
Normal file
15
src/helpers.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (! function_exists('e')) {
|
||||||
|
/**
|
||||||
|
* Encode HTML special characters in a string.
|
||||||
|
*
|
||||||
|
* @param string|null $value
|
||||||
|
* @param bool $doubleEncode
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function e($value, $doubleEncode = true)
|
||||||
|
{
|
||||||
|
return htmlspecialchars($value ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', $doubleEncode);
|
||||||
|
}
|
||||||
|
}
|
||||||
0
tests/Unit/.gitkeep
Normal file
0
tests/Unit/.gitkeep
Normal file
35
views/index.php
Normal file
35
views/index.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Success Page</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #4caf50;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
background: linear-gradient(to bottom, #4caf50, #388e3c 50%, #4caf50 100%);
|
||||||
|
background: linear-gradient(to right, #4caf50, #388e3c 50%, #4caf50 100%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="message-container">
|
||||||
|
<h1>"It works!"</h1>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user