Этот коммит содержится в:
Igor V Belousov 2019-11-05 01:14:53 +03:00
Коммит 499374305a
29 изменённых файлов: 1004 добавлений и 0 удалений

2
.gitignore поставляемый Обычный файл
Просмотреть файл

@ -0,0 +1,2 @@
.idea/*
.php_cs.cache

37
docker-compose.yml Обычный файл
Просмотреть файл

@ -0,0 +1,37 @@
version: '3.3'
services:
mysql:
image: mysql:latest
restart: always
volumes:
- database:/var/lib/mysql
environment:
MYSQL_DATABASE: 'db'
MYSQL_USER: 'user'
MYSQL_PASSWORD: 'password'
MYSQL_ROOT_PASSWORD: 'rootPassword'
ports:
- 3306:3306
app:
build:
context: ./docker
dockerfile: php-fpm.docker
volumes:
- "./public/:/app/public"
- "./src/:/app/src"
depends_on:
- mysql
nginx:
build:
context: ./docker
dockerfile: nginx.docker
volumes:
- "./public/:/app/public"
ports:
- 80:80
depends_on:
- app
volumes:
database:

5
docker/nginx.docker Обычный файл
Просмотреть файл

@ -0,0 +1,5 @@
FROM nginx:alpine
COPY ./nginx_default.conf /etc/nginx/conf.d/default.conf
WORKDIR /app

18
docker/nginx_default.conf Обычный файл
Просмотреть файл

@ -0,0 +1,18 @@
server {
listen 80;
index index.php;
root /app/public;
location / {
try_files $uri /index.php?$args;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

7
docker/php-fpm.docker Обычный файл
Просмотреть файл

@ -0,0 +1,7 @@
FROM php:fpm-alpine
RUN set -ex \
&& apk --no-cache add postgresql-dev libpq freetype libpng libjpeg-turbo freetype-dev libpng-dev libjpeg-turbo-dev libzip zlib-dev libzip-dev \
&& docker-php-ext-install pdo pdo_mysql gd zip
WORKDIR /app

349
public/assets/css/normalize.css поставляемый Обычный файл
Просмотреть файл

@ -0,0 +1,349 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}

0
public/assets/css/style.css Обычный файл
Просмотреть файл

Двоичные данные
public/assets/img/favicon/android-chrome-192x192.png Обычный файл

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.8 KiB

Двоичные данные
public/assets/img/favicon/android-chrome-384x384.png Обычный файл

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.8 KiB

Двоичные данные
public/assets/img/favicon/apple-touch-icon.png Обычный файл

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.8 KiB

Двоичные данные
public/assets/img/favicon/favicon-16x16.png Обычный файл

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 602 B

Двоичные данные
public/assets/img/favicon/favicon-32x32.png Обычный файл

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 851 B

Двоичные данные
public/assets/img/favicon/mstile-150x150.png Обычный файл

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.7 KiB

Просмотреть файл

@ -0,0 +1 @@
<svg version="1" xmlns="http://www.w3.org/2000/svg" width="574.667" height="574.667" viewBox="0 0 431.000000 431.000000"><path d="M70.2 40.3L65 45.6l.2 139.9.3 139.9 5 1.4c2.8.7 7.3 2 10 2.7 5.4 1.6 48.1 13 84.5 22.6 12.4 3.3 24.8 6.6 27.5 7.4 2.8.8 16 4.4 29.5 8 13.5 3.6 46.3 12.4 72.9 19.5 26.7 7.2 48.8 13 49.3 13 .4 0 3-2.3 5.8-5.1l5-5V250c0-132.3-.1-139.9-1.7-140.4-1.6-.5-27-7.3-96.3-25.7-12.4-3.3-40-10.8-61.5-16.5-21.4-5.8-44.2-11.9-50.5-13.6-15.9-4.2-66-17.7-68-18.3-1-.3-3.5 1.4-6.8 4.8z"/></svg>

После

Ширина:  |  Высота:  |  Размер: 507 B

9
public/browserconfig.xml Обычный файл
Просмотреть файл

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/assets/favicon/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

Двоичные данные
public/favicon.ico Обычный файл

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 15 KiB

4
public/index.php Обычный файл
Просмотреть файл

@ -0,0 +1,4 @@
<?php
include dirname(__DIR__) . '/src/App.php';
new App();

19
public/site.webmanifest Обычный файл
Просмотреть файл

@ -0,0 +1,19 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/assets/favicon/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/assets/favicon/android-chrome-384x384.png",
"sizes": "384x384",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

87
src/App.php Обычный файл
Просмотреть файл

@ -0,0 +1,87 @@
<?php
use MyApp\Core\Config;
use MyApp\Core\Router;
class App
{
private $prefix = 'MyApp\\';
private $classDirs = [
'Controller\\' => 'controllers/',
'Core\\' => 'core/',
'Model\\' => 'models/',
'View\\' => 'views/',
];
private $basedir = __DIR__;
/**
* @var Config
*/
public $config;
public function __construct()
{
global $app;
$app = $this;
spl_autoload_register([$this, 'autoload']);
$this->config = (new Config())->setDbName('db')
->setDbPort(3306)
->setDbUser('user')
->setDbPassword('password')
->setSrcDir($this->basedir)
->setTimezone('Europe/Moscow')
->setClassDirs($this->classDirs)
->setControllersDir($this->basedir.'/controllers/')
->setControllersNamespace('\\MyApp\\Controller\\')
->setSiteName('BadPing. Nagios better');
new Router();
}
/**
* Autoload function.
*
* @param string $className
*/
public function autoload(string $className)
{
if (0 !== strpos($className, $this->prefix)) {
return;
}
$fileName = substr($className, strlen($this->prefix));
$fileName = str_replace(array_keys($this->classDirs), array_values($this->classDirs), $fileName);
$fileName = $this->basedir.'/'.str_replace('\\', '/', $fileName).'.php';
if (file_exists($fileName)) {
require $fileName;
}
}
/**
* Parse tags from method comments.
*
* @param string $comment
* @param string $tags
*
* @return array|null
*/
public function parseTagsFromComment(string $comment, string $tags = 'RouteRegExp|HttpMethod')
{
if (preg_match_all("/^\\ *\\* \\@(?<type>$tags)\\ =\\ \"(?<value>.*)\\\".*\$/m", $comment, $matches)) {
$out = [];
foreach ($matches['type'] as $key => $type) {
if ('HttpMethod' == $type) {
$out[$type] = explode(',', $matches['value'][$key]);
} else {
$out[$type] = $matches['value'][$key];
}
}
return $out;
}
return null;
}
}

17
src/controllers/DefaultController.php Обычный файл
Просмотреть файл

@ -0,0 +1,17 @@
<?php
namespace MyApp\Controller;
use MyApp\Core\Controller;
use MyApp\View\DefaultView as View;
class DefaultController extends Controller
{
/**
* @RouteRegExp = "|^/$|"
*/
public function index()
{
(new View())->index();
}
}

19
src/controllers/ErrorsController.php Обычный файл
Просмотреть файл

@ -0,0 +1,19 @@
<?php
namespace MyApp\Controller;
use MyApp\Core\Controller;
class ErrorsController extends Controller
{
public function get404()
{
http_response_code(404);
echo 404;
}
public function post404()
{
echo 404;
}
}

235
src/core/Config.php Обычный файл
Просмотреть файл

@ -0,0 +1,235 @@
<?php
namespace MyApp\Core;
class Config
{
/**
* @var string
*/
private $dbName;
/**
* @var int
*/
private $dbPort;
/**
* @var string
*/
private $dbUser;
/**
* @var string
*/
private $dbPassword;
/**
* @var array
*/
private $classDirs;
/**
* @var string
*/
private $srcDir;
/**
* @var string
*/
private $controllersDir;
/**
* @var string
*/
private $controllersNamespace;
/**
* @var string
*/
private $siteName;
/**
* @return string
*/
public function getDbName(): string
{
return $this->dbName;
}
/**
* @param string $dbName
*
* @return Config
*/
public function setDbName(string $dbName)
{
$this->dbName = $dbName;
return $this;
}
/**
* @return int
*/
public function getDbPort(): int
{
return $this->dbPort;
}
/**
* @param int $dbPort
*
* @return Config
*/
public function setDbPort(int $dbPort)
{
$this->dbPort = $dbPort;
return $this;
}
/**
* @return string
*/
public function getDbUser(): string
{
return $this->dbUser;
}
/**
* @param string $dbUser
*
* @return Config
*/
public function setDbUser(string $dbUser)
{
$this->dbUser = $dbUser;
return $this;
}
/**
* @return string
*/
public function getDbPassword(): string
{
return $this->dbPassword;
}
/**
* @param string $dbPassword
*
* @return Config
*/
public function setDbPassword(string $dbPassword)
{
$this->dbPassword = $dbPassword;
return $this;
}
/**
* @param string $timezone
*
* @return $this
*/
public function setTimezone(string $timezone)
{
date_default_timezone_set($timezone);
return $this;
}
/**
* @return array
*/
public function getClassDirs(): array
{
return $this->classDirs;
}
/**
* @param array $classDirs
*
* @return Config
*/
public function setClassDirs(array $classDirs)
{
$this->classDirs = $classDirs;
return $this;
}
/**
* @return string
*/
public function getSrcDir(): string
{
return $this->srcDir;
}
/**
* @param string $srcDir
*
* @return Config
*/
public function setSrcDir(string $srcDir)
{
$this->srcDir = $srcDir;
return $this;
}
/**
* @return string
*/
public function getControllersDir(): string
{
return $this->controllersDir;
}
/**
* @param string $controllersDir
*
* @return Config
*/
public function setControllersDir(string $controllersDir)
{
$this->controllersDir = $controllersDir;
return $this;
}
/**
* @return string
*/
public function getControllersNamespace(): string
{
return $this->controllersNamespace;
}
/**
* @param string $controllersNamespace
*
* @return Config
*/
public function setControllersNamespace(string $controllersNamespace)
{
$this->controllersNamespace = $controllersNamespace;
return $this;
}
/**
* @return string
*/
public function getSiteName(): string
{
return $this->siteName;
}
/**
* @param string $siteName
*
* @return Config
*/
public function setSiteName(string $siteName)
{
$this->siteName = $siteName;
return $this;
}
}

7
src/core/Controller.php Обычный файл
Просмотреть файл

@ -0,0 +1,7 @@
<?php
namespace MyApp\Core;
class Controller
{
}

7
src/core/Model.php Обычный файл
Просмотреть файл

@ -0,0 +1,7 @@
<?php
namespace MyApp\Core;
class Model
{
}

103
src/core/Router.php Обычный файл
Просмотреть файл

@ -0,0 +1,103 @@
<?php
namespace MyApp\Core;
use ReflectionMethod;
/**
* Class Router.
*/
class Router
{
/**
* @var array
*/
private $routes = [];
public function __construct()
{
$this->getRoutesFromControllers();
$this->addRoute('/^.*$/', 'MyApp\\Controller\\ErrorsController', 'get404', ['GET']);
$this->addRoute('/^.*$/', 'MyApp\\Controller\\ErrorsController', 'post404', ['POST', 'PUT', 'DELETE']);
$this->run();
}
/**
* Get Routes from a comment on method in Controllers.
*/
private function getRoutesFromControllers()
{
/* @var \App $app */
global $app;
/* @var Config $config */
$config = $app->config;
$controllersDir = $config->getControllersDir();
$controllersNamespace = $config->getControllersNamespace();
$files = scandir($controllersDir);
foreach ($files as $file) {
$className = strstr($file, '.php', true);
$fileName = $controllersDir.$file;
if (is_file($fileName) && false !== $className) {
try {
$refClass = new \ReflectionClass($controllersNamespace.$className);
$methods = $refClass->getMethods(ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method) {
$tags = $app->parseTagsFromComment($method->getDocComment());
if (is_array($tags)) {
if (array_key_exists('RouteRegExp', $tags)) {
if (!array_key_exists('HttpMethod', $tags)) {
$tags['HttpMethod'] = ['GET'];
}
$this->addRoute($tags['RouteRegExp'], $method->class, $method->name, $tags['HttpMethod']);
}
}
}
} catch (\ReflectionException $e) {
var_dump($e);
}
}
}
}
/**
* Add route to array.
*
* @param string $regExp
* @param string $className
* @param string $classMethod
* @param array $httpMethods
*/
private function addRoute(string $regExp, string $className, string $classMethod, array $httpMethods)
{
foreach ($httpMethods as $httpMethod) {
$this->routes[strtoupper($httpMethod)][] = [$regExp, $className, $classMethod];
}
}
/**
* Run parse request.
*/
private function run()
{
if (array_key_exists($_SERVER['REQUEST_METHOD'], $this->routes)) {
foreach ($this->routes[$_SERVER['REQUEST_METHOD']] as $route_array) {
if (preg_match($route_array[0], $_SERVER['REQUEST_URI'], $matches)) {
$obj = new $route_array[1]();
$param_arr = [];
foreach ($matches as $key => $value) {
if (!is_int($key)) {
$param_arr[] = $value;
}
}
call_user_func_array([$obj, $route_array[2]], $param_arr);
break;
}
}
}
}
}

44
src/core/View.php Обычный файл
Просмотреть файл

@ -0,0 +1,44 @@
<?php
namespace MyApp\Core;
class View
{
/**
* @var string
*/
public $title = '';
public function json($data)
{
}
public function render($template)
{
/* @var \App $app */
global $app;
$fileName = $app->config->getSrcDir().'/templates/'.$template.'.tpl.php';
if (file_exists($fileName)) {
ob_start();
require $fileName;
ob_end_flush();
}
}
/**
* @param string $title
*
* @return View
*/
public function setTitle(string $title)
{
/* @var \App $app */
global $app;
$this->title = $title.' &mdash; '.$app->config->getSiteName();
return $this;
}
}

3
src/templates/default/index.tpl.php Обычный файл
Просмотреть файл

@ -0,0 +1,3 @@
<?php
require __DIR__.'/../shared/head.php';

18
src/templates/shared/head.php Обычный файл
Просмотреть файл

@ -0,0 +1,18 @@
<!doctype html>
<html lang="ru-RU">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="apple-touch-icon" sizes="180x180" href="/assets/img/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicon/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="mask-icon" href="/assets/img/favicon/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="/assets/css/normalize.css">
<link rel="stylesheet" href="/assets/css/style.css">
<title><?= $this->title; ?></title>
</head>

13
src/views/DefaultView.php Обычный файл
Просмотреть файл

@ -0,0 +1,13 @@
<?php
namespace MyApp\View;
use MyApp\Core\View;
class DefaultView extends View
{
public function index()
{
$this->setTitle('Index')->render('default/index');
}
}