Init
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.idea/
|
||||||
|
/vendor
|
||||||
|
composer.lock
|
||||||
22
composer.json
Normal file
22
composer.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "rmphp/kernel",
|
||||||
|
"license": "proprietary",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Yuri Zuev",
|
||||||
|
"email": "y_zuev@mail.ru"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": "^8.1",
|
||||||
|
"psr/log": "^3.0.0",
|
||||||
|
"psr/container": "^1.0",
|
||||||
|
"rmphp/foundation": "1.0.x-dev"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Rmphp\\Kernel\\": "src/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
207
src/App.php
Normal file
207
src/App.php
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace Rmphp\Kernel;
|
||||||
|
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Rmphp\Foundation\Exceptions\AppError;
|
||||||
|
use Rmphp\Foundation\Exceptions\AppException;
|
||||||
|
use Rmphp\Foundation\RouterInterface;
|
||||||
|
use Rmphp\Foundation\TemplateInterface;
|
||||||
|
use Rmphp\Foundation\MatchObject;
|
||||||
|
|
||||||
|
|
||||||
|
class App extends Main {
|
||||||
|
|
||||||
|
private string $baseDir;
|
||||||
|
private array $appRoutes = [];
|
||||||
|
private RouterInterface $router;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->baseDir = dirname(__DIR__, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function handler(ServerRequestInterface $request, ResponseInterface $response) : ResponseInterface {
|
||||||
|
try{
|
||||||
|
$this->init($request, $response);
|
||||||
|
$this->syslogger()->log("routes", "", $this->appRoutes);
|
||||||
|
|
||||||
|
foreach ($this->appRoutes as $appRouteKey => $appHandler){
|
||||||
|
|
||||||
|
if(!$appHandler instanceof MatchObject) continue;
|
||||||
|
$response = null;
|
||||||
|
|
||||||
|
if(!empty($appHandler->className)){
|
||||||
|
if(!class_exists($appHandler->className)) {
|
||||||
|
$this->syslogger()->log("handlers", "Err - Class ".$appHandler->className." is not exists");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$controllers[$appRouteKey] = new $appHandler->className;
|
||||||
|
$log = "OK - Class ".$appHandler->className;
|
||||||
|
|
||||||
|
if(!empty($appHandler->methodName)){
|
||||||
|
if(!method_exists($appHandler->className, $appHandler->methodName)) {
|
||||||
|
$this->syslogger()->log("handlers", "Err - Method ".$appHandler->className."/".$appHandler->methodName." is not exists");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$response = (!empty($appHandler->params)) ? $controllers[$appRouteKey]->{$appHandler->methodName}(...$appHandler->params) : $controllers[$appRouteKey]->{$appHandler->methodName}();
|
||||||
|
$log = "OK - Method ".$appHandler->className."/".$appHandler->methodName;
|
||||||
|
}
|
||||||
|
$this->syslogger()->log("handlers", $log);
|
||||||
|
/**
|
||||||
|
* 1. Если на этапе итерации уже получен ответ ResponseInterface - досрочно отдаем результат в эмиттер
|
||||||
|
*/
|
||||||
|
if($response instanceof ResponseInterface) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
elseif($response === false) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 2. Если итерации закончились и задан обьект Content им создаем результат для эмиттера
|
||||||
|
*/
|
||||||
|
if($this->template() && !empty($this->template()->getResponse())){
|
||||||
|
$body = $this->globals()->response()->getBody();
|
||||||
|
$body->write($this->template()->getResponse());
|
||||||
|
$body->rewind();
|
||||||
|
return $this->globals()->response()->withBody($body);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 3. Отдаем пустой результат если не определен шаблонизатор
|
||||||
|
*/
|
||||||
|
return $this->defaultPage(404);
|
||||||
|
}
|
||||||
|
catch (AppException $appException){
|
||||||
|
if($this->logger()) $this->logger()->warning($appException->getMessage()." on ".$appException->getFile().":".$appException->getLine());
|
||||||
|
$this->syslogger()->warning("AppException: ".$appException->getMessage());
|
||||||
|
}
|
||||||
|
catch (\Exception $exception) {
|
||||||
|
if($this->logger()) $this->logger()->warning($exception->getMessage()." on ".$exception->getFile().":".$exception->getLine());
|
||||||
|
$this->syslogger()->warning("Exception: ".$exception->getMessage()." : ".$exception->getFile()." : ".$exception->getLine());
|
||||||
|
}
|
||||||
|
catch (AppError $appError){
|
||||||
|
if($this->logger()) $this->logger()->warning($appError->getMessage()." on ".$appError->getFile().":".$appError->getLine());
|
||||||
|
$this->syslogger()->error("Error: ".$appError->getMessage()." : ".$appError->getFile()." : ".$appError->getLine());
|
||||||
|
}
|
||||||
|
catch (\Error $error) {
|
||||||
|
if($this->logger()) $this->logger()->error($error->getMessage()." on ".$error->getFile().":".$error->getLine());
|
||||||
|
$this->syslogger()->error("Error: ".$error->getMessage()." : ".$error->getFile()." : ".$error->getLine());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 4. Отдаем ошибку без шаблона
|
||||||
|
*/
|
||||||
|
return $this->defaultPage(501);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $code
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
private function defaultPage(int $code) : ResponseInterface{
|
||||||
|
if(is_file($this->baseDir.'/'.getenv("PAGE".$code))){
|
||||||
|
$body = $this->globals()->response()->getBody();
|
||||||
|
$body->write(file_get_contents($this->baseDir.'/'.getenv("PAGE".$code)));
|
||||||
|
$body->rewind();
|
||||||
|
return $this->globals()->response()->withBody($body)->withStatus($code);
|
||||||
|
}
|
||||||
|
return $this->globals()->response()->withStatus($code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
* @return void
|
||||||
|
* @throws AppException
|
||||||
|
*/
|
||||||
|
private function init(ServerRequestInterface $request, ResponseInterface $response) : void {
|
||||||
|
|
||||||
|
$this->setGlobals($request, $response);
|
||||||
|
|
||||||
|
// init factories
|
||||||
|
if(is_file($this->baseDir."/".getenv("APP_COMPONENTS_FILE"))){
|
||||||
|
$components = include_once $this->baseDir."/".getenv("APP_COMPONENTS_FILE");
|
||||||
|
if(!empty($components) && is_array($components)){
|
||||||
|
foreach ($components as $componentName => $componentValue){
|
||||||
|
if(empty($componentValue)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
elseif(is_object($componentValue)){
|
||||||
|
$componentObject = $componentValue;
|
||||||
|
}
|
||||||
|
elseif(!file_exists($this->baseDir.'/'.$componentValue) || !is_object($componentObject = require $this->baseDir.'/'.$componentValue)){
|
||||||
|
throw AppException::invalidObject($componentValue);
|
||||||
|
}
|
||||||
|
switch (true){
|
||||||
|
case ($componentObject instanceof ContainerInterface): $this->setContainer($componentObject); break;
|
||||||
|
case ($componentObject instanceof TemplateInterface): $this->setTemplate($componentObject); break;
|
||||||
|
case ($componentObject instanceof LoggerInterface): $this->setLogger($componentObject); break;
|
||||||
|
case ($componentObject instanceof RouterInterface): $this->router = $componentObject; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// app nodes
|
||||||
|
if(is_file($this->baseDir."/".getenv("APP_NODES_FILE"))){
|
||||||
|
$nodes = include_once $this->baseDir."/".getenv("APP_NODES_FILE");
|
||||||
|
}
|
||||||
|
for ($appNodeNum = 1; getenv("APP_NODE".$appNodeNum); $appNodeNum++){
|
||||||
|
$nodesCollection[] = json_decode(getenv("APP_NODE".$appNodeNum), true);
|
||||||
|
}
|
||||||
|
if(isset($nodesCollection)) $nodes = $nodesCollection;
|
||||||
|
|
||||||
|
if(empty($nodes) || !is_array($nodes)) throw AppException::emptyAppNodes();
|
||||||
|
$this->getActions($nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $appNodes
|
||||||
|
*/
|
||||||
|
private function getActions(array $appNodes) : void {
|
||||||
|
foreach ($appNodes as $appNode){
|
||||||
|
|
||||||
|
// по умолчанию точка монтирования ровна корню
|
||||||
|
$mountKey = (!empty($appNode['key'])) ? $appNode['key'] : '/';
|
||||||
|
|
||||||
|
// если url начинается не с точки монтирования смотрим далее
|
||||||
|
if (0 !== (strpos($this->globals()->request()->getUri()->getPath(), $mountKey))) continue;
|
||||||
|
|
||||||
|
if(!empty($appNode['action'])){
|
||||||
|
$className = $appNode['action'];
|
||||||
|
$methodName = $appNode['method'];
|
||||||
|
$params = (!empty($appNode['params']) && is_string($appNode['params'])) ? explode(",",str_replace(" ", "", $appNode['params'])) : [];
|
||||||
|
$this->appRoutes[] = new MatchObject($className, $methodName, $params);
|
||||||
|
}
|
||||||
|
elseif(!empty($appNode['router']) && file_exists($this->baseDir."/".$appNode['router'])){
|
||||||
|
|
||||||
|
if(empty($this->router)) throw AppError::invalidRequiredObject("Application config without router");
|
||||||
|
$this->router->setStartPoint($mountKey);
|
||||||
|
|
||||||
|
if(pathinfo($this->baseDir."/".$appNode['router'])['extension'] == "php") {
|
||||||
|
$this->router->withRules(include_once $this->baseDir."/".$appNode['router']);
|
||||||
|
}
|
||||||
|
elseif(pathinfo($this->baseDir."/".$appNode['router'])['extension'] == "json") {
|
||||||
|
$this->router->withRules(json_decode(file_get_contents($this->baseDir."/".$appNode['router']), true));
|
||||||
|
}
|
||||||
|
elseif(pathinfo($this->baseDir."/".$appNode['router'])['extension'] == "yaml") {
|
||||||
|
$this->router->withRules(yaml_parse_file($this->baseDir."/".$appNode['router']));
|
||||||
|
}
|
||||||
|
|
||||||
|
$routes = $this->router->match($this->globals()->request()) ?? [];
|
||||||
|
foreach ($routes as $route){
|
||||||
|
$this->appRoutes[] = $route;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
261
src/Globals.php
Normal file
261
src/Globals.php
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Rmphp\Kernel;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class Globals {
|
||||||
|
|
||||||
|
private ServerRequestInterface $request;
|
||||||
|
private ResponseInterface $response;
|
||||||
|
private Session $session;
|
||||||
|
|
||||||
|
const INT = "INT";
|
||||||
|
const STRING = "STRING";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
*/
|
||||||
|
public function __construct(ServerRequestInterface $request, ResponseInterface $response) {
|
||||||
|
$this->request = $request;
|
||||||
|
$this->response = $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ServerRequestInterface
|
||||||
|
*/
|
||||||
|
public function request() : ServerRequestInterface {
|
||||||
|
return $this->request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function response() : ResponseInterface {
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @return ServerRequestInterface
|
||||||
|
*/
|
||||||
|
public function setReqest(ServerRequestInterface $request) : ServerRequestInterface {
|
||||||
|
$this->request = $request;
|
||||||
|
return $this->request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function setResponse(ResponseInterface $response) : ResponseInterface {
|
||||||
|
$this->response = $response;
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isGet(string $name = "") : bool {
|
||||||
|
return (!empty($name)) ? isset($this->request->getQueryParams()[$name]) : !empty($this->request->getQueryParams());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isPost(string $name = "") : bool {
|
||||||
|
return (!empty($name)) ? isset($this->request->getParsedBody()[$name]) : !empty($this->request->getParsedBody());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isCookie(string $name = "") : bool {
|
||||||
|
return (!empty($name)) ? isset($this->request->getCookieParams()[$name]) : !empty($this->request->getCookieParams());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isSession(string $name = "") : bool {
|
||||||
|
if(!class_exists(Session::class)) return false;
|
||||||
|
if(!isset($this->session)) $this->session = new Session();
|
||||||
|
return (!empty($name)) ? isset($this->session->getSession()[$name]) : !empty($this->session->getSession());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isFile(string $name = "") : bool {
|
||||||
|
return (!empty($name)) ? isset($this->request->getUploadedFiles()[$name]) : !empty($this->request->getUploadedFiles());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isStream() : bool {
|
||||||
|
return !empty($this->request->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param string $type
|
||||||
|
* @return array|int|string
|
||||||
|
*/
|
||||||
|
public function get(string $name = "", string $type = "") {
|
||||||
|
return $this->onGlobal($this->request->getQueryParams(), $name, $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param string $type
|
||||||
|
* @return array|int|string
|
||||||
|
*/
|
||||||
|
public function post(string $name = "", string $type = "") {
|
||||||
|
return $this->onGlobal($this->request->getParsedBody(), $name, $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param string $type
|
||||||
|
* @return array|int|string
|
||||||
|
*/
|
||||||
|
public function cookie(string $name = "", string $type = "") {
|
||||||
|
return $this->onGlobal($this->request->getCookieParams(), $name, $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param string $type
|
||||||
|
* @return array|int|string
|
||||||
|
*/
|
||||||
|
public function session(string $name = "", string $type = "") {
|
||||||
|
if(!class_exists(Session::class)) return null;
|
||||||
|
if(!isset($this->session)) $this->session = new Session();
|
||||||
|
return $this->onGlobal($this->session->getSession(), $name, $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @return array|int|string
|
||||||
|
*/
|
||||||
|
public function files(string $name = "") {
|
||||||
|
return $this->onGlobal($this->request->getUploadedFiles(), $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function stream() {
|
||||||
|
return !empty($this->request->getBody()->getContents()) ? $this->request->getBody()->getContents(): null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param string $value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addHeader(string $name, string $value) : void {
|
||||||
|
$this->setResponse($this->response->withAddedHeader($name, $value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param $value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setSession(string $name, $value = null) : void {
|
||||||
|
if(class_exists(Session::class)) {
|
||||||
|
if(!isset($this->session)) $this->session = new Session();
|
||||||
|
$this->session->setSession($name, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param string $value
|
||||||
|
* @param int $expires
|
||||||
|
* @param string $path
|
||||||
|
* @param string $domain
|
||||||
|
* @param bool $secure
|
||||||
|
* @param bool $httponly
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setCookie(string $name, string $value="", int $expires = 0, string $path = "", string $domain = "", bool $secure = false, bool $httponly = false) : void {
|
||||||
|
$cookie = [];
|
||||||
|
$cookie[] = $name."=".((!empty($value)) ? $value : "deleted");
|
||||||
|
if($expires != 0) {
|
||||||
|
$cookie[] = ($expires>time()) ? "expires=".date("D, d-M-Y H:i:s", $expires)." GMT; Max-Age=".($expires-time()) : "expires=".date("D, d-M-Y H:i:s", 0)." GMT; Max-Age=0";
|
||||||
|
}
|
||||||
|
if(!empty($path)) $cookie[] = "path=".$path;
|
||||||
|
if(!empty($domain)) $cookie[] = "domain=".$domain;
|
||||||
|
if($secure) $cookie[] = "Secure";
|
||||||
|
if($httponly) $cookie[] = "HttpOnly";
|
||||||
|
$this->addHeader("Set-Cookie", implode("; ", $cookie));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $name
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function clearSession(string $name = null) : void{
|
||||||
|
if(class_exists(Session::class)) {
|
||||||
|
if(!isset($this->session)) $this->session = new Session();
|
||||||
|
$this->session->clearSession($name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param string $path
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function clearCookie(string $name, string $path = "") : void {
|
||||||
|
$cookie = $name."=deleted; expires=".date("D, d-M-Y H:i:s", 0)." GMT; Max-Age=0";
|
||||||
|
if(!empty($path)) $cookie.="; path=".$path;
|
||||||
|
$this->addHeader("Set-Cookie", $cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $var
|
||||||
|
* @param string $name
|
||||||
|
* @param string $type
|
||||||
|
* @return array|int|string
|
||||||
|
*/
|
||||||
|
private function onGlobal(array $var, string $name, string $type = "") {
|
||||||
|
$name = strtolower($name);
|
||||||
|
if (!empty($name))
|
||||||
|
{
|
||||||
|
if (!isset($var[$name])) return null;
|
||||||
|
|
||||||
|
if (empty($type)) {
|
||||||
|
return $var[$name];
|
||||||
|
}
|
||||||
|
elseif ($type == self::STRING) {
|
||||||
|
return (!empty($var[$name])) ? (string)$var[$name] : null;
|
||||||
|
}
|
||||||
|
elseif ($type == self::INT) {
|
||||||
|
return (!empty((int)$var[$name]) || $var[$name]==0) ? (int)$var[$name] : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $var;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
144
src/Logger.php
Normal file
144
src/Logger.php
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Rmphp\Kernel;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
class Logger implements LoggerInterface {
|
||||||
|
|
||||||
|
public const DEBUG = 'DEBUG';
|
||||||
|
public const INFO = 'INFO';
|
||||||
|
public const NOTICE = 'NOTICE';
|
||||||
|
public const WARNING = 'WARNING';
|
||||||
|
public const ERROR = 'ERROR';
|
||||||
|
public const CRITICAL = 'CRITICAL';
|
||||||
|
public const ALERT = 'ALERT';
|
||||||
|
public const EMERGENCY = 'EMERGENCY';
|
||||||
|
|
||||||
|
private static array $logs = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Levels numbers defined in RFC 5424
|
||||||
|
*/
|
||||||
|
private const RFC_5424_LEVELS = [
|
||||||
|
7 => self::DEBUG,
|
||||||
|
6 => self::INFO,
|
||||||
|
5 => self::NOTICE,
|
||||||
|
4 => self::WARNING,
|
||||||
|
3 => self::ERROR,
|
||||||
|
2 => self::CRITICAL,
|
||||||
|
1 => self::ALERT,
|
||||||
|
0 => self::EMERGENCY,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Stringable|string $message
|
||||||
|
* @param array $context
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function emergency(\Stringable|string $message, array $context=[]): void {
|
||||||
|
$this->log(self::EMERGENCY, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Stringable|string $message
|
||||||
|
* @param array $context
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function alert(\Stringable|string $message, array $context=[]): void {
|
||||||
|
$this->log(self::ALERT, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Stringable|string $message
|
||||||
|
* @param array $context
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function critical(\Stringable|string $message, array $context=[]): void {
|
||||||
|
$this->log(self::CRITICAL, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Stringable|string $message
|
||||||
|
* @param array $context
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function error(\Stringable|string $message, array $context=[]): void {
|
||||||
|
$this->log(self::ERROR, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Stringable|string $message
|
||||||
|
* @param array $context
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function warning(\Stringable|string $message, array $context=[]): void {
|
||||||
|
$this->log(self::WARNING, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Stringable|string $message
|
||||||
|
* @param array $context
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function notice(\Stringable|string $message, array $context=[]): void {
|
||||||
|
$this->log(self::NOTICE, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Stringable|string $message
|
||||||
|
* @param array $context
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function info(\Stringable|string $message, array $context=[]): void {
|
||||||
|
$this->log(self::INFO, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Stringable|string $message
|
||||||
|
* @param array $context
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function debug(\Stringable|string $message, array $context=[]): void {
|
||||||
|
$this->log(self::DEBUG, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $level
|
||||||
|
* @param \Stringable|string $message
|
||||||
|
* @param array $context
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function log($level, \Stringable|string $message, array $context=[]): void {
|
||||||
|
if(is_numeric($level) && isset(self::RFC_5424_LEVELS[$level])){
|
||||||
|
$level = self::RFC_5424_LEVELS[$level];
|
||||||
|
}
|
||||||
|
if(!empty((string)$message))$in[] = $message;
|
||||||
|
if(!empty($context))$in[] = $context;
|
||||||
|
self::$logs[$level][] = (count($in)==1) ? $in[0] : $in;;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $level
|
||||||
|
* @param mixed $context
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function dump($level, mixed $context) : void {
|
||||||
|
self::$logs[$level][] = $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getLogs($level = null) : array {
|
||||||
|
if(is_numeric($level) && isset(self::RFC_5424_LEVELS[$level])){
|
||||||
|
$level = self::RFC_5424_LEVELS[$level];
|
||||||
|
}
|
||||||
|
$out = [];
|
||||||
|
foreach (self::$logs as $key => $logField){
|
||||||
|
$out[$key] = (count($logField)==1) ? $logField[0] : $logField;
|
||||||
|
}
|
||||||
|
return isset($level) ? $out[$level] : $out;
|
||||||
|
}
|
||||||
|
}
|
||||||
97
src/Main.php
Normal file
97
src/Main.php
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Rmphp\Kernel;
|
||||||
|
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Rmphp\Foundation\TemplateInterface;
|
||||||
|
|
||||||
|
class Main {
|
||||||
|
|
||||||
|
private static ?Logger $syslogger = null;
|
||||||
|
private static ?Globals $globals = null;
|
||||||
|
private static ?ContainerInterface $container = null;
|
||||||
|
private static ?TemplateInterface $template = null;
|
||||||
|
private static ?LoggerInterface $logger = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Logger
|
||||||
|
*/
|
||||||
|
final public function syslogger() : Logger {
|
||||||
|
if(!isset(self::$syslogger) && class_exists(Logger::class)) {
|
||||||
|
self::$syslogger = new Logger();
|
||||||
|
}
|
||||||
|
return self::$syslogger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function setGlobals(ServerRequestInterface $request, ResponseInterface $response) : void {
|
||||||
|
if(!isset(self::$globals) && class_exists(Globals::class)) {
|
||||||
|
self::$globals = new Globals($request, $response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ContainerInterface $container
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function setContainer(ContainerInterface $container) : void {
|
||||||
|
self::$container = $container;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TemplateInterface $template
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function setTemplate(TemplateInterface $template) : void {
|
||||||
|
self::$template = $template;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LoggerInterface $logger
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function setLogger(LoggerInterface $logger) : void {
|
||||||
|
self::$logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Globals|null
|
||||||
|
*/
|
||||||
|
final public function globals() : ?Globals {
|
||||||
|
return self::$globals;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ContainerInterface|null
|
||||||
|
*/
|
||||||
|
final public function container() : ?ContainerInterface {
|
||||||
|
if(empty(self::$container)) $this->syslogger()->warning("Application config without countainer");
|
||||||
|
return self::$container;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return TemplateInterface|null
|
||||||
|
*/
|
||||||
|
final public function template() : ?TemplateInterface {
|
||||||
|
if(empty(self::$template)) $this->syslogger()->warning("Application config without template");
|
||||||
|
return self::$template;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return LoggerInterface|null
|
||||||
|
*/
|
||||||
|
final public function logger() : ?LoggerInterface {
|
||||||
|
if(empty(self::$logger)){
|
||||||
|
$this->syslogger()->warning("Application config without logger");
|
||||||
|
}
|
||||||
|
return self::$logger;
|
||||||
|
}
|
||||||
|
}
|
||||||
117
src/ResponseEmitter.php
Normal file
117
src/ResponseEmitter.php
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Rmphp\Kernel;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
class ResponseEmitter {
|
||||||
|
|
||||||
|
private int $responseChunkSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ResponseEmitter constructor.
|
||||||
|
* @param int $responseChunkSize
|
||||||
|
*/
|
||||||
|
public function __construct(int $responseChunkSize = 4096)
|
||||||
|
{
|
||||||
|
$this->responseChunkSize = $responseChunkSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
*/
|
||||||
|
public function emit(ResponseInterface $response): void
|
||||||
|
{
|
||||||
|
$isEmpty = $this->isResponseEmpty($response);
|
||||||
|
if (headers_sent() === false) {
|
||||||
|
$this->emitStatusLine($response);
|
||||||
|
$this->emitHeaders($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$isEmpty) {
|
||||||
|
$this->emitBody($response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
*/
|
||||||
|
private function emitHeaders(ResponseInterface $response): void
|
||||||
|
{
|
||||||
|
foreach ($response->getHeaders() as $name => $values) {
|
||||||
|
$first = strtolower($name) !== 'set-cookie';
|
||||||
|
foreach ($values as $value) {
|
||||||
|
$header = sprintf('%s: %s', $name, $value);
|
||||||
|
header($header, $first);
|
||||||
|
$first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
*/
|
||||||
|
private function emitStatusLine(ResponseInterface $response): void
|
||||||
|
{
|
||||||
|
$statusLine = sprintf(
|
||||||
|
'HTTP/%s %s %s',
|
||||||
|
$response->getProtocolVersion(),
|
||||||
|
$response->getStatusCode(),
|
||||||
|
$response->getReasonPhrase()
|
||||||
|
);
|
||||||
|
header($statusLine, true, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
*/
|
||||||
|
private function emitBody(ResponseInterface $response): void
|
||||||
|
{
|
||||||
|
$body = $response->getBody();
|
||||||
|
if ($body->isSeekable()) {
|
||||||
|
$body->rewind();
|
||||||
|
}
|
||||||
|
|
||||||
|
$amountToRead = (int) $response->getHeaderLine('Content-Length');
|
||||||
|
if (!$amountToRead) {
|
||||||
|
$amountToRead = $body->getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($amountToRead) {
|
||||||
|
while ($amountToRead > 0 && !$body->eof()) {
|
||||||
|
$length = min($this->responseChunkSize, $amountToRead);
|
||||||
|
$data = $body->read($length);
|
||||||
|
echo $data;
|
||||||
|
|
||||||
|
$amountToRead -= strlen($data);
|
||||||
|
|
||||||
|
if (connection_status() !== CONNECTION_NORMAL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (!$body->eof()) {
|
||||||
|
echo $body->read($this->responseChunkSize);
|
||||||
|
if (connection_status() !== CONNECTION_NORMAL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isResponseEmpty(ResponseInterface $response): bool
|
||||||
|
{
|
||||||
|
if (in_array($response->getStatusCode(), [204, 205, 304], true)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$stream = $response->getBody();
|
||||||
|
$seekable = $stream->isSeekable();
|
||||||
|
if ($seekable) {
|
||||||
|
$stream->rewind();
|
||||||
|
}
|
||||||
|
return $seekable ? $stream->read(1) === '' : $stream->eof();
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/Session.php
Normal file
39
src/Session.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Rmphp\Kernel;
|
||||||
|
|
||||||
|
|
||||||
|
class Session {
|
||||||
|
|
||||||
|
public function __construct(string $name = "usi") {
|
||||||
|
if(session_status() == PHP_SESSION_NONE) {
|
||||||
|
session_name($name);
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getSession() : array {
|
||||||
|
return $_SESSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param $value
|
||||||
|
*/
|
||||||
|
public function setSession(string $name, $value = null) : void {
|
||||||
|
$_SESSION[$name] = $value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $name
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function clearSession(string $name = null) : void {
|
||||||
|
if (isset($name)) unset($_SESSION[$name]);
|
||||||
|
else $_SESSION = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
140
src/Utils.php
Normal file
140
src/Utils.php
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Rmphp\Kernel;
|
||||||
|
|
||||||
|
|
||||||
|
class Utils {
|
||||||
|
|
||||||
|
private static int $idcount = 0;
|
||||||
|
private static string $objid;
|
||||||
|
private static int $deep = 0;
|
||||||
|
|
||||||
|
private static function generateCSSBlock() : void {
|
||||||
|
?>
|
||||||
|
<style>
|
||||||
|
.hide-<?=self::$objid?>{display: none}
|
||||||
|
.s-<?=self::$objid?>-h{padding: 5px 0; box-sizing: border-box; background: #0F0F0A; font: 11px Tahoma; color:#868746; font-weight: bold; border-bottom: 1px solid #323334;}
|
||||||
|
.s-<?=self::$objid?>-h span{cursor: pointer;}
|
||||||
|
.s-<?=self::$objid?>-b{border-left: 1px solid #606060; border-bottom: 0; padding: 0 0 0 20px; margin: 3px 0 0 0; position: relative}
|
||||||
|
.s-<?=self::$objid?>-t{display: table; border-collapse: collapse;}
|
||||||
|
.s-<?=self::$objid?>-tr{background: #000000; display: table-row; border-bottom: 1px solid #323334;}
|
||||||
|
.s-<?=self::$objid?>-tс{display:table-cell; padding: 5px 25px 5px 0; font: 11px Tahoma; color:#a2a399; font-weight: bold}
|
||||||
|
.s-<?=self::$objid?>-tс:nth-child(1){min-width:10%;}
|
||||||
|
.s-<?=self::$objid?>-tс:nth-child(2){min-width:10%;}
|
||||||
|
.s-<?=self::$objid?>-tс:nth-child(3){width:100%;}
|
||||||
|
.s-<?=self::$objid?>-trs{background: #000000; display: table-row;}
|
||||||
|
.s-<?=self::$objid?>-tсs:nth-child(1){display:table-cell; padding: 0; font: 11px Tahoma; color:#a2a399; font-weight: bold}
|
||||||
|
.s-<?=self::$objid?>-sc{font: 11px Tahoma; color:#a2a399; font-weight: bold}
|
||||||
|
</style>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function generateJSBlock() : void{
|
||||||
|
?>
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.querySelectorAll('.s-<?=self::$objid?>-h>span').forEach(i => {
|
||||||
|
if(document.getElementById('b-'+i.id)) {
|
||||||
|
i.querySelector('span').innerHTML = (document.getElementById('b-'+i.id).classList.contains("hide-<?=self::$objid?>")) ? ' ►' : ' ▼'
|
||||||
|
i.addEventListener('click', ()=>{
|
||||||
|
document.getElementById('b-'+i.id).classList.toggle("hide-<?=self::$objid?>");
|
||||||
|
i.querySelector('span').innerHTML = (document.getElementById('b-'+i.id).classList.contains("hide-<?=self::$objid?>")) ? ' ►' : ' ▼'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function generateStartLine(): void {
|
||||||
|
?>
|
||||||
|
<div style="position:relative; box-sizing: border-box; width:100%; padding:10px; background: #000000; z-index: 10000; overflow: auto;">
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function fromatValue(mixed $val) : mixed {
|
||||||
|
if(is_bool($val)) return (!empty($val)) ? 'true' : 'false';
|
||||||
|
if(is_int($val)) return htmlspecialchars($val);
|
||||||
|
if(is_float($val)) return htmlspecialchars($val);
|
||||||
|
if(is_string($val)) return htmlspecialchars($val);
|
||||||
|
if(!isset($val)) return '-';
|
||||||
|
return $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function generateHTMLBlock ($data, $title = "array"){
|
||||||
|
if(is_array($data) || is_object($data)){
|
||||||
|
self::$deep++;
|
||||||
|
$id = 'id'.self::$objid.self::$idcount++;
|
||||||
|
$display = true;
|
||||||
|
if(is_array((array)$data) && count((array)$data) > 10) $display = false;
|
||||||
|
if (in_array(self::$deep,[3,5])) $display = false;
|
||||||
|
?>
|
||||||
|
<div class="s-<?=self::$objid?>-h">
|
||||||
|
<span id="<?=$id?>"><?=str_replace(mb_chr(0), "-", $title)?> (<?=count((array)$data)?>) <span> </span></span>
|
||||||
|
</div>
|
||||||
|
<?php if(count((array)$data)): ?>
|
||||||
|
<div id="b-<?=$id?>" class="s-<?=self::$objid?>-b <?=((!$display)?'hide-'.self::$objid:'')?>">
|
||||||
|
<table class="s-<?=self::$objid?>-t">
|
||||||
|
<?php foreach ((array)$data as $key => $val) : ?>
|
||||||
|
<?php if(is_array($val) || is_object($val)):?>
|
||||||
|
<tr class="s-<?=self::$objid?>-trs">
|
||||||
|
<td class="s-<?=self::$objid?>-tсs" colspan="3"><?php self::generateHTMLBlock($val, "[".$key."]");?></td>
|
||||||
|
</tr>
|
||||||
|
<?php else: ?>
|
||||||
|
<tr class="s-<?=self::$objid?>-tr">
|
||||||
|
<td class="s-<?=self::$objid?>-tс">[<?=htmlspecialchars($key)?>]</td>
|
||||||
|
<td class="s-<?=self::$objid?>-tс"><?=gettype($val)?></td>
|
||||||
|
<td class="s-<?=self::$objid?>-tс"><?=self::fromatValue($val)?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif;?>
|
||||||
|
<?php endforeach;?>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php endif;
|
||||||
|
self::$deep--;
|
||||||
|
} else {
|
||||||
|
?>
|
||||||
|
<div class="s-<?=self::$objid?>-sc">
|
||||||
|
<div>(<?=gettype($data)?>) <?=self::fromatValue($data)?></div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function addShutdownInfo(array $exData = []) : void {
|
||||||
|
register_shutdown_function(function() use ($exData) {
|
||||||
|
$finish = array_sum(explode(' ', microtime()));
|
||||||
|
$info[] = error_get_last();
|
||||||
|
$info[] = "Время генерации: ".substr((string)($finish-$_SERVER['REQUEST_TIME_FLOAT']), 0, 10)." сек.";
|
||||||
|
$info[] = "Объем памяти: ".round((memory_get_usage()),2)." байт.";
|
||||||
|
$info[] = "Выделено памяти в пике: ".round((memory_get_peak_usage()),2)." байт.";
|
||||||
|
$exData['info'] = array_diff($info, array(null));
|
||||||
|
|
||||||
|
if(in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)){
|
||||||
|
var_dump($exData);
|
||||||
|
} else {
|
||||||
|
self::$objid = substr(md5(time()),0,5);
|
||||||
|
self::generateCSSBlock();
|
||||||
|
self::generateStartLine();
|
||||||
|
self::generateHTMLBlock($exData, "dump info");
|
||||||
|
echo '</div>';
|
||||||
|
self::generateJSBlock();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function dd(mixed $exData) : void {
|
||||||
|
register_shutdown_function(function() use ($exData) {
|
||||||
|
if(in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)){
|
||||||
|
var_dump($exData);
|
||||||
|
} else {
|
||||||
|
self::$objid = substr(md5(time()),0,5);
|
||||||
|
self::generateCSSBlock();
|
||||||
|
self::generateStartLine();
|
||||||
|
self::generateHTMLBlock($exData);
|
||||||
|
echo '</div>';
|
||||||
|
self::generateJSBlock();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user