This commit is contained in:
User
2023-05-29 02:03:21 +03:00
commit 5763db2b54
10 changed files with 1034 additions and 0 deletions

207
src/App.php Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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?>")) ? '&nbsp;&#9658;' : '&nbsp;&#9660;'
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?>")) ? '&nbsp;&#9658;' : '&nbsp;&#9660;'
})
}
})
</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>&nbsp;</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;
}
}