Линтовка
This commit is contained in:
@@ -725,7 +725,7 @@ $finder = (new Finder())
|
||||
->notPath($excludeNames); // исключаем файлы
|
||||
|
||||
return (new Config())
|
||||
// спорная фигня, пока не
|
||||
// спорная фигня, пока не активирую
|
||||
// ->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect());
|
||||
->setFinder($finder)
|
||||
->setRules($rules) // ставим правила
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
@@ -42,6 +43,7 @@ class ApiController extends BasicController
|
||||
if ($playlist['isOnline'] === true) {
|
||||
unset($playlist['content']);
|
||||
}
|
||||
|
||||
return $this->responseJson($response, 200, $playlist);
|
||||
} catch (PlaylistNotFoundException $e) {
|
||||
return $this->responseJsonError($response, 404, $e);
|
||||
@@ -65,7 +67,7 @@ class ApiController extends BasicController
|
||||
return $response->withStatus(404);
|
||||
}
|
||||
|
||||
$filePath = cache_path("qr-codes/$code.jpg");
|
||||
$filePath = cache_path("qr-codes/{$code}.jpg");
|
||||
if (file_exists($filePath)) {
|
||||
$raw = file_get_contents($filePath);
|
||||
} else {
|
||||
@@ -74,13 +76,14 @@ class ApiController extends BasicController
|
||||
'outputType' => QRCode::OUTPUT_IMAGE_JPG,
|
||||
'eccLevel' => QRCode::ECC_L,
|
||||
]);
|
||||
$data = config('app.mirror_url') ? mirror_url("$code.m3u") : base_url("$code.m3u");
|
||||
$data = config('app.mirror_url') ? mirror_url("{$code}.m3u") : base_url("{$code}.m3u");
|
||||
$raw = new QRCode($options)->render($data, $filePath);
|
||||
$raw = base64_decode(str_replace('data:image/jpg;base64,', '', $raw));
|
||||
}
|
||||
|
||||
$mime = mime_content_type($filePath);
|
||||
$response->getBody()->write($raw);
|
||||
|
||||
return $response->withStatus(200)
|
||||
->withHeader('Content-Type', $mime);
|
||||
}
|
||||
@@ -116,10 +119,11 @@ class ApiController extends BasicController
|
||||
function getSize(string $directory): int
|
||||
{
|
||||
$size = 0;
|
||||
foreach (glob($directory . '/*') as $path){
|
||||
foreach (glob($directory . '/*') as $path) {
|
||||
is_file($path) && $size += filesize($path);
|
||||
is_dir($path) && $size += getSize($path);
|
||||
}
|
||||
|
||||
return $size;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
@@ -56,6 +57,7 @@ class BasicController
|
||||
$json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
$response->getBody()->write($json);
|
||||
|
||||
return $response->withStatus($status)
|
||||
->withHeader('Content-Type', 'application/json');
|
||||
}
|
||||
@@ -99,6 +101,7 @@ class BasicController
|
||||
array $data = [],
|
||||
): ResponseInterface {
|
||||
$view = Twig::fromRequest($request);
|
||||
|
||||
return $view->render($response, $template, $data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
@@ -42,7 +43,7 @@ class WebController extends BasicController
|
||||
$pageSize = config('app.page_size');
|
||||
|
||||
if ($pageSize > 0) {
|
||||
$pageCurrent = (int)($request->getAttributes()['page'] ?? $request->getQueryParams()['page'] ?? 1);
|
||||
$pageCurrent = (int) ($request->getAttributes()['page'] ?? $request->getQueryParams()['page'] ?? 1);
|
||||
$pageCount = ceil($count / $pageSize);
|
||||
$offset = max(0, ($pageCurrent - 1) * $pageSize);
|
||||
$ini = array_slice($ini, $offset, $pageSize, true);
|
||||
@@ -76,6 +77,7 @@ class WebController extends BasicController
|
||||
|
||||
try {
|
||||
$playlist = ini()->getPlaylist($code);
|
||||
|
||||
return $response->withHeader('Location', $playlist['url']);
|
||||
} catch (Throwable) {
|
||||
return $this->notFound($request, $response);
|
||||
@@ -99,6 +101,7 @@ class WebController extends BasicController
|
||||
|
||||
try {
|
||||
$playlist = ini()->getPlaylist($code);
|
||||
|
||||
return $this->view($request, $response, 'details.twig', ['playlist' => $playlist]);
|
||||
} catch (PlaylistNotFoundException) {
|
||||
return $this->notFound($request, $response);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
@@ -72,6 +73,7 @@ class IniFile
|
||||
{
|
||||
empty($this->playlists) && $this->load();
|
||||
$data = redis()->get($code);
|
||||
|
||||
return $this->initPlaylist($code, $data);
|
||||
}
|
||||
|
||||
@@ -96,11 +98,11 @@ class IniFile
|
||||
protected function initPlaylist(string $code, array|false $data): array
|
||||
{
|
||||
if ($data === false) {
|
||||
$raw = $this->playlists[$code]
|
||||
$raw = $this->playlists[$code]
|
||||
?? throw new PlaylistNotFoundException($code);
|
||||
$data = [
|
||||
'code' => $code,
|
||||
'name' => $raw['name'] ?? "Плейлист #$code",
|
||||
'name' => $raw['name'] ?? "Плейлист #{$code}",
|
||||
'description' => $raw['desc'] ?? null,
|
||||
'url' => $raw['pls'],
|
||||
'source' => $raw['src'] ?? null,
|
||||
@@ -182,7 +184,7 @@ class IniFile
|
||||
|
||||
return array_any(
|
||||
$badAttributes,
|
||||
static fn (string $badAttribute) => preg_match_all("/$badAttribute/", $string) >= 1,
|
||||
static fn (string $badAttribute) => preg_match_all("/{$badAttribute}/", $string) >= 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
@@ -30,11 +31,6 @@ final class Kernel
|
||||
*/
|
||||
public const string VERSION = '1.0.0';
|
||||
|
||||
/**
|
||||
* @var Kernel
|
||||
*/
|
||||
private static Kernel $instance;
|
||||
|
||||
/**
|
||||
* @var App
|
||||
*/
|
||||
@@ -55,6 +51,11 @@ final class Kernel
|
||||
*/
|
||||
protected ?Redis $cache = null;
|
||||
|
||||
/**
|
||||
* @var Kernel
|
||||
*/
|
||||
private static Kernel $instance;
|
||||
|
||||
/**
|
||||
* Закрытый конструктор
|
||||
*
|
||||
@@ -75,11 +76,73 @@ final class Kernel
|
||||
*
|
||||
* @return Kernel
|
||||
*/
|
||||
public static function instance(): Kernel
|
||||
public static function instance(): self
|
||||
{
|
||||
return self::$instance ??= new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает объект подключения к Redis
|
||||
*
|
||||
* @return Redis
|
||||
* @see https://github.com/phpredis/phpredis/?tab=readme-ov-file
|
||||
*/
|
||||
public function redis(): Redis
|
||||
{
|
||||
if (!empty($this->cache)) {
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
$options = [
|
||||
'host' => $this->config['cache']['host'],
|
||||
'port' => (int) $this->config['cache']['port'],
|
||||
];
|
||||
|
||||
if (!empty($this->config['cache']['password'])) {
|
||||
$options['auth'] = $this->config['cache']['password'];
|
||||
}
|
||||
|
||||
$this->cache = new Redis($options);
|
||||
$this->cache->select((int) $this->config['cache']['db']);
|
||||
$this->cache->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_JSON);
|
||||
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает значение из конфига
|
||||
*
|
||||
* @param string $key Ключ в формате "config.key"
|
||||
* @param mixed|null $default Значение по умолчанию
|
||||
* @return mixed
|
||||
*/
|
||||
public function config(string $key, mixed $default = null): mixed
|
||||
{
|
||||
$parts = explode('.', $key);
|
||||
|
||||
return $this->config[$parts[0]][$parts[1]] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает объект приложения
|
||||
*
|
||||
* @return App
|
||||
*/
|
||||
public function app(): App
|
||||
{
|
||||
return $this->app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает объект ini-файла
|
||||
*
|
||||
* @return IniFile
|
||||
*/
|
||||
public function ini(): IniFile
|
||||
{
|
||||
return $this->iniFile ??= new IniFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Загружает файл .env или .env.$env
|
||||
*
|
||||
@@ -88,12 +151,13 @@ final class Kernel
|
||||
*/
|
||||
protected function loadDotEnvFile(string $env = ''): array
|
||||
{
|
||||
$filename = empty($env) ? '.env' : ".env.$env";
|
||||
$filename = empty($env) ? '.env' : ".env.{$env}";
|
||||
if (!file_exists(root_path($filename))) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$dotenv = Dotenv::createMutable(root_path(), $filename);
|
||||
|
||||
return $dotenv->safeLoad();
|
||||
}
|
||||
|
||||
@@ -138,7 +202,7 @@ final class Kernel
|
||||
default => throw new InvalidArgumentException(sprintf('Неверный HTTP метод %s', $method))
|
||||
};
|
||||
|
||||
$definition = $this->app->$func($route['path'], $route['handler']);
|
||||
$definition = $this->app->{$func}($route['path'], $route['handler']);
|
||||
}
|
||||
|
||||
if (!empty($route['name'])) {
|
||||
@@ -163,65 +227,4 @@ final class Kernel
|
||||
$twig->addExtension(new DebugExtension());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает объект подключения к Redis
|
||||
*
|
||||
* @return Redis
|
||||
* @see https://github.com/phpredis/phpredis/?tab=readme-ov-file
|
||||
*/
|
||||
public function redis(): Redis
|
||||
{
|
||||
if (!empty($this->cache)) {
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
$options = [
|
||||
'host' => $this->config['cache']['host'],
|
||||
'port' => (int)$this->config['cache']['port'],
|
||||
];
|
||||
|
||||
if (!empty($this->config['cache']['password'])) {
|
||||
$options['auth'] = $this->config['cache']['password'];
|
||||
}
|
||||
|
||||
$this->cache = new Redis($options);
|
||||
$this->cache->select((int)$this->config['cache']['db']);
|
||||
$this->cache->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_JSON);
|
||||
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает значение из конфига
|
||||
*
|
||||
* @param string $key Ключ в формате "config.key"
|
||||
* @param mixed|null $default Значение по умолчанию
|
||||
* @return mixed
|
||||
*/
|
||||
public function config(string $key, mixed $default = null): mixed
|
||||
{
|
||||
$parts = explode('.', $key);
|
||||
return $this->config[$parts[0]][$parts[1]] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает объект приложения
|
||||
*
|
||||
* @return App
|
||||
*/
|
||||
public function app(): App
|
||||
{
|
||||
return $this->app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает объект ini-файла
|
||||
*
|
||||
* @return IniFile
|
||||
*/
|
||||
public function ini(): IniFile
|
||||
{
|
||||
return $this->iniFile ??= new IniFile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
@@ -29,7 +30,36 @@ class StatisticsService
|
||||
$this->channels = $this->getAllChannels();
|
||||
}
|
||||
|
||||
protected function getPlaylistsByField(string $field, int|string|bool|null $value): array
|
||||
/**
|
||||
* Обрабатывает команду /stats
|
||||
*
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get(): array
|
||||
{
|
||||
return [
|
||||
'playlists' => [
|
||||
'all' => count($this->playlists),
|
||||
'online' => count($this->getPlaylistsByField('isOnline', true)),
|
||||
'offline' => count($this->getPlaylistsByField('isOnline', false)),
|
||||
'unknown' => count($this->getPlaylistsByField('isOnline', null)),
|
||||
'adult' => count($this->getPlaylistsByTag('adult')),
|
||||
'hasCatchup' => count($this->getPlaylistsByField('hasCatchup', true)),
|
||||
'hasTvg' => count($this->getPlaylistsByField('hasTvg', true)),
|
||||
'groupped' => count($this->getPlaylistsWithGroups()),
|
||||
'latest' => $this->getLatestPlaylist(),
|
||||
],
|
||||
'channels' => [
|
||||
'all' => $this->getAllChannelsCount(),
|
||||
'online' => count($this->getChannelsByField('isOnline', true)),
|
||||
'offline' => count($this->getChannelsByField('isOnline', false)),
|
||||
'adult' => count($this->getChannelsByTag('adult')),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getPlaylistsByField(string $field, bool|int|string|null $value): array
|
||||
{
|
||||
return array_filter(
|
||||
$this->playlists,
|
||||
@@ -85,7 +115,7 @@ class StatisticsService
|
||||
return count($this->channels);
|
||||
}
|
||||
|
||||
protected function getChannelsByField(string $field, int|string|bool|null $value): array
|
||||
protected function getChannelsByField(string $field, bool|int|string|null $value): array
|
||||
{
|
||||
return array_filter(
|
||||
$this->channels,
|
||||
@@ -100,33 +130,4 @@ class StatisticsService
|
||||
static fn (array $channel) => in_array($tag, $channel['tags']),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает команду /stats
|
||||
*
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get(): array
|
||||
{
|
||||
return [
|
||||
'playlists' => [
|
||||
'all' => count($this->playlists),
|
||||
'online' => count($this->getPlaylistsByField('isOnline', true)),
|
||||
'offline' => count($this->getPlaylistsByField('isOnline', false)),
|
||||
'unknown' => count($this->getPlaylistsByField('isOnline', null)),
|
||||
'adult' => count($this->getPlaylistsByTag('adult')),
|
||||
'hasCatchup' => count($this->getPlaylistsByField('hasCatchup', true)),
|
||||
'hasTvg' => count($this->getPlaylistsByField('hasTvg', true)),
|
||||
'groupped' => count($this->getPlaylistsWithGroups()),
|
||||
'latest' => $this->getLatestPlaylist(),
|
||||
],
|
||||
'channels' => [
|
||||
'all' => $this->getAllChannelsCount(),
|
||||
'online' => count($this->getChannelsByField('isOnline', true)),
|
||||
'offline' => count($this->getChannelsByField('isOnline', false)),
|
||||
'adult' => count($this->getChannelsByTag('adult')),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
@@ -98,7 +99,7 @@ class TwigExtention extends AbstractExtension
|
||||
*/
|
||||
public function toDate(?float $timestamp, string $format = 'd.m.Y H:i:s'): string
|
||||
{
|
||||
return $timestamp === null ? '' : date($format, (int)$timestamp);
|
||||
return $timestamp === null ? '' : date($format, (int) $timestamp);
|
||||
}
|
||||
|
||||
public function arrayValues($value, ...$args)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
@@ -9,9 +10,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Errors;
|
||||
|
||||
use Psr\Http\Message\{
|
||||
ResponseInterface,
|
||||
ServerRequestInterface};
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Slim\Handlers\ErrorHandler as SlimErrorHandler;
|
||||
use Throwable;
|
||||
@@ -21,33 +21,6 @@ use Throwable;
|
||||
*/
|
||||
class ErrorHandler extends SlimErrorHandler
|
||||
{
|
||||
/**
|
||||
* Логирует ошибку и отдаёт JSON-ответ с необходимым содержимым
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param Throwable $exception
|
||||
* @param bool $displayErrorDetails
|
||||
* @param bool $logErrors
|
||||
* @param bool $logErrorDetails
|
||||
* @param LoggerInterface|null $logger
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function __invoke(
|
||||
ServerRequestInterface $request,
|
||||
Throwable $exception,
|
||||
bool $displayErrorDetails,
|
||||
bool $logErrors,
|
||||
bool $logErrorDetails,
|
||||
?LoggerInterface $logger = null
|
||||
): ResponseInterface {
|
||||
$payload = $this->payload($exception, $displayErrorDetails);
|
||||
|
||||
$response = app()->getResponseFactory()->createResponse();
|
||||
$response->getBody()->write(json_encode($payload, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает структуру исключения для контекста
|
||||
*
|
||||
@@ -63,7 +36,7 @@ class ErrorHandler extends SlimErrorHandler
|
||||
'class' => $e::class,
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'trace' => $e->getTrace()
|
||||
'trace' => $e->getTrace(),
|
||||
];
|
||||
|
||||
return $result;
|
||||
@@ -94,4 +67,31 @@ class ErrorHandler extends SlimErrorHandler
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Логирует ошибку и отдаёт JSON-ответ с необходимым содержимым
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param Throwable $exception
|
||||
* @param bool $displayErrorDetails
|
||||
* @param bool $logErrors
|
||||
* @param bool $logErrorDetails
|
||||
* @param LoggerInterface|null $logger
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function __invoke(
|
||||
ServerRequestInterface $request,
|
||||
Throwable $exception,
|
||||
bool $displayErrorDetails,
|
||||
bool $logErrors,
|
||||
bool $logErrorDetails,
|
||||
?LoggerInterface $logger = null
|
||||
): ResponseInterface {
|
||||
$payload = $this->payload($exception, $displayErrorDetails);
|
||||
|
||||
$response = app()->getResponseFactory()->createResponse();
|
||||
$response->getBody()->write(json_encode($payload, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
@@ -15,6 +16,6 @@ class InvalidTelegramSecretException extends Exception
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct("Ошибка валидации запроса от Telegram Bot API");
|
||||
parent::__construct('Ошибка валидации запроса от Telegram Bot API');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
@@ -15,6 +16,6 @@ class PlaylistNotFoundException extends Exception
|
||||
{
|
||||
public function __construct(string $code)
|
||||
{
|
||||
parent::__construct("Плейлист '$code' не найден");
|
||||
parent::__construct("Плейлист '{$code}' не найден");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
@@ -17,8 +18,8 @@ if (!function_exists('root_path')) {
|
||||
/**
|
||||
* Возвращает абсолютный путь к корневой директории приложения.
|
||||
*
|
||||
* @param string $path Относительный путь для добавления к корневому.
|
||||
* @return string Абсолютный путь.
|
||||
* @param string $path относительный путь для добавления к корневому
|
||||
* @return string абсолютный путь
|
||||
*/
|
||||
function root_path(string $path = ''): string
|
||||
{
|
||||
@@ -32,12 +33,12 @@ if (!function_exists('config_path')) {
|
||||
/**
|
||||
* Возвращает абсолютный путь к директории конфигурации приложения.
|
||||
*
|
||||
* @param string $path Относительный путь для добавления к директории конфигурации.
|
||||
* @return string Абсолютный путь.
|
||||
* @param string $path относительный путь для добавления к директории конфигурации
|
||||
* @return string абсолютный путь
|
||||
*/
|
||||
function config_path(string $path = ''): string
|
||||
{
|
||||
return root_path("config/$path");
|
||||
return root_path("config/{$path}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,12 +46,12 @@ if (!function_exists('cache_path')) {
|
||||
/**
|
||||
* Возвращает абсолютный путь к директории кэша приложения.
|
||||
*
|
||||
* @param string $path Относительный путь для добавления к директории кэша.
|
||||
* @return string Абсолютный путь.
|
||||
* @param string $path относительный путь для добавления к директории кэша
|
||||
* @return string абсолютный путь
|
||||
*/
|
||||
function cache_path(string $path = ''): string
|
||||
{
|
||||
return root_path("cache/$path");
|
||||
return root_path("cache/{$path}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +59,8 @@ if (!function_exists('base_url')) {
|
||||
/**
|
||||
* Возвращает базовый URL приложения.
|
||||
*
|
||||
* @param string $route Дополнительный маршрут, который будет добавлен к базовому URL.
|
||||
* @return string Полный URL.
|
||||
* @param string $route дополнительный маршрут, который будет добавлен к базовому URL
|
||||
* @return string полный URL
|
||||
*/
|
||||
function base_url(string $route = ''): string
|
||||
{
|
||||
@@ -71,7 +72,7 @@ if (!function_exists('kernel')) {
|
||||
/**
|
||||
* Возвращает синглтон-экземпляр ядра приложения.
|
||||
*
|
||||
* @return Kernel Экземпляр ядра приложения.
|
||||
* @return Kernel экземпляр ядра приложения
|
||||
*/
|
||||
function kernel(): Kernel
|
||||
{
|
||||
@@ -83,7 +84,7 @@ if (!function_exists('app')) {
|
||||
/**
|
||||
* Возвращает синглтон-экземпляр Slim-приложения.
|
||||
*
|
||||
* @return App Экземпляр Slim-приложения.
|
||||
* @return App экземпляр Slim-приложения
|
||||
*/
|
||||
function app(): App
|
||||
{
|
||||
@@ -95,9 +96,9 @@ if (!function_exists('config')) {
|
||||
/**
|
||||
* Возвращает значение из конфигурации приложения.
|
||||
*
|
||||
* @param string $key Ключ конфигурации.
|
||||
* @param mixed $default Значение по умолчанию, если ключ не найден.
|
||||
* @return mixed Значение конфигурации или значение по умолчанию.
|
||||
* @param string $key ключ конфигурации
|
||||
* @param mixed $default значение по умолчанию, если ключ не найден
|
||||
* @return mixed значение конфигурации или значение по умолчанию
|
||||
*/
|
||||
function config(string $key, mixed $default = null): mixed
|
||||
{
|
||||
@@ -109,7 +110,7 @@ if (!function_exists('redis')) {
|
||||
/**
|
||||
* Возвращает синглтон-экземпляр Redis-клиента.
|
||||
*
|
||||
* @return Redis Экземпляр Redis-клиента.
|
||||
* @return Redis экземпляр Redis-клиента
|
||||
*/
|
||||
function redis(): Redis
|
||||
{
|
||||
@@ -121,7 +122,7 @@ if (!function_exists('ini')) {
|
||||
/**
|
||||
* Возвращает синглтон-экземпляр IniFile.
|
||||
*
|
||||
* @return IniFile Экземпляр IniFile.
|
||||
* @return IniFile экземпляр IniFile
|
||||
*/
|
||||
function ini(): IniFile
|
||||
{
|
||||
@@ -273,11 +274,11 @@ if (!function_exists('env')) {
|
||||
/**
|
||||
* Возвращает значение переменной окружения.
|
||||
*
|
||||
* @param string $key Ключ переменной окружения.
|
||||
* @param mixed $default Значение по умолчанию, если переменная не найдена.
|
||||
* @param bool $required Бросать исключение, если переменная обязательна и не найдена.
|
||||
* @return mixed Значение переменной окружения или значение по умолчанию.
|
||||
* @throws InvalidArgumentException Если переменная обязательна и не найдена.
|
||||
* @param string $key ключ переменной окружения
|
||||
* @param mixed $default значение по умолчанию, если переменная не найдена
|
||||
* @param bool $required бросать исключение, если переменная обязательна и не найдена
|
||||
* @return mixed значение переменной окружения или значение по умолчанию
|
||||
* @throws InvalidArgumentException если переменная обязательна и не найдена
|
||||
*/
|
||||
function env(string $key, mixed $default = null, bool $required = false): mixed
|
||||
{
|
||||
@@ -311,7 +312,7 @@ if (!function_exists('here')) {
|
||||
* @param bool $asArray массив или строка в формате `<file|class>:<func>():<line>`
|
||||
* @return string|array
|
||||
*/
|
||||
function here(bool $asArray = false): string|array
|
||||
function here(bool $asArray = false): array|string
|
||||
{
|
||||
$trace = debug_backtrace(!DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
|
||||
@@ -386,7 +387,7 @@ if (!function_exists('as_data_url')) {
|
||||
*/
|
||||
function as_data_url(string $data, string $mimeType = 'text/plain'): string
|
||||
{
|
||||
return "data://$mimeType,$data";
|
||||
return "data://{$mimeType},{$data}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,7 +474,7 @@ if (!function_exists('number_format_local')) {
|
||||
* @return string Отформатированное число
|
||||
*/
|
||||
function number_format_local(
|
||||
int|float $number,
|
||||
float|int $number,
|
||||
int $decimals = 0,
|
||||
string $decPoint = '.',
|
||||
string $thousandsSep = ' ',
|
||||
@@ -494,7 +495,7 @@ if (!function_exists('format_bytes')) {
|
||||
{
|
||||
$units = ['Б', 'КБ', 'МБ', 'ГБ', 'ТБ'];
|
||||
|
||||
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
|
||||
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; ++$i) {
|
||||
$bytes /= 1024;
|
||||
}
|
||||
|
||||
@@ -581,11 +582,12 @@ if (!function_exists('get_noun_form')) {
|
||||
|
||||
if ($lastDigit === 1) {
|
||||
return $form1;
|
||||
} elseif ($lastDigit >= 2 && $lastDigit <= 4) {
|
||||
return $form2;
|
||||
} else {
|
||||
return $form5;
|
||||
}
|
||||
if ($lastDigit >= 2 && $lastDigit <= 4) {
|
||||
return $form2;
|
||||
}
|
||||
|
||||
return $form5;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,7 +607,7 @@ if (!function_exists('random_string')) {
|
||||
$result = '';
|
||||
$max = strlen($chars) - 1;
|
||||
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
for ($i = 0; $i < $length; ++$i) {
|
||||
$result .= $chars[random_int(0, $max)];
|
||||
}
|
||||
|
||||
@@ -702,7 +704,7 @@ if (!function_exists('recast')) {
|
||||
function recast(string $className, stdClass &$object): mixed
|
||||
{
|
||||
if (!class_exists($className)) {
|
||||
throw new InvalidArgumentException("Class not found: $className");
|
||||
throw new InvalidArgumentException("Class not found: {$className}");
|
||||
}
|
||||
|
||||
$new = new $className();
|
||||
@@ -769,7 +771,7 @@ if (!function_exists('mb_count_chars')) {
|
||||
for ($i = 0; $i < $length; ++$i) {
|
||||
$char = mb_substr($string, $i, 1, 'UTF-8');
|
||||
!array_key_exists($char, $unique) && $unique[$char] = 0;
|
||||
$unique[$char]++;
|
||||
++$unique[$char];
|
||||
}
|
||||
|
||||
return $unique;
|
||||
@@ -853,7 +855,7 @@ if (!function_exists('array_last')) {
|
||||
return $array[$lastKey];
|
||||
}
|
||||
|
||||
for ($i = count($keys) - 1; $i >= 0; $i--) {
|
||||
for ($i = count($keys) - 1; $i >= 0; --$i) {
|
||||
$key = $keys[$i];
|
||||
if ($callback($array[$key], $key)) {
|
||||
return $array[$key];
|
||||
@@ -916,7 +918,7 @@ if (!function_exists('array_get')) {
|
||||
* @param mixed $default Значение по умолчанию
|
||||
* @return mixed Значение из массива или значение по умолчанию
|
||||
*/
|
||||
function array_get(array $array, string|int $key, mixed $default = null): mixed
|
||||
function array_get(array $array, int|string $key, mixed $default = null): mixed
|
||||
{
|
||||
return $array[$key] ?? $default;
|
||||
}
|
||||
@@ -1118,7 +1120,7 @@ if (!function_exists('array_recursive_diff')) {
|
||||
$aReturn[$key] = $aRecursiveDiff;
|
||||
}
|
||||
} else {
|
||||
if ($value != $b[$key]) {
|
||||
if ($value !== $b[$key]) {
|
||||
$aReturn[$key] = $value;
|
||||
}
|
||||
}
|
||||
@@ -1400,7 +1402,7 @@ if (!function_exists('flatten')) {
|
||||
*
|
||||
* @param array $tree Дерево (например, результат функции tree())
|
||||
* @param string $branching Ключ ноды, под которым находится массив с дочерними нодами
|
||||
* @param null|string $leafProperty Ключ ноды, значение коего определяет является ли каждая нода родителем
|
||||
* @param string|null $leafProperty Ключ ноды, значение коего определяет является ли каждая нода родителем
|
||||
* @return array Плоский список
|
||||
*/
|
||||
function flatten(
|
||||
@@ -1459,8 +1461,8 @@ if (!function_exists('clear_tree')) {
|
||||
*
|
||||
* @param array $node Нода, которая должна быть обработана
|
||||
* @param string $branching Ключ ноды, под которым находится массив с дочерними нодами
|
||||
* @param null|string $leafProperty Ключ ноды, значение коего определяет является ли каждая нода родителем
|
||||
* @return null|array Обработанная нода с хотя бы одним потомком либо null
|
||||
* @param string|null $leafProperty Ключ ноды, значение коего определяет является ли каждая нода родителем
|
||||
* @return array|null Обработанная нода с хотя бы одним потомком либо null
|
||||
*/
|
||||
function clear_tree(
|
||||
array $node,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
@@ -11,7 +13,6 @@ use App\Controllers\BotController;
|
||||
use App\Controllers\WebController;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Web routes
|
||||
@@ -99,4 +100,3 @@ return [
|
||||
'name' => 'not-found',
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
|
||||
21
linter
21
linter
@@ -7,15 +7,12 @@
|
||||
|
||||
# shellcheck disable=SC2015
|
||||
|
||||
# set -x
|
||||
# set -o pipefail
|
||||
|
||||
########################################################
|
||||
# Служебные исходные переменные
|
||||
########################################################
|
||||
|
||||
# имя контейнера
|
||||
CONTAINER="m3u-su-web"
|
||||
CONTAINER="iptv-web"
|
||||
|
||||
# команда для запуска
|
||||
COMMAND="$1"; shift
|
||||
@@ -30,7 +27,6 @@ IS_FROM_GIT="$(env | grep -c "GIT_EDITOR=:")"
|
||||
[[ -f /.dockerenv ]] && IS_FROM_CONTAINER=1 || IS_FROM_CONTAINER=0
|
||||
|
||||
# признак режима отладки
|
||||
[[ $LINTER_DEBUG == 1 ]] && DEBUG_MODE=1
|
||||
[[ $LINTER_DEBUG -gt 1 ]] && set -x
|
||||
|
||||
########################################################
|
||||
@@ -42,13 +38,11 @@ LINTER_COLORS=${LINTER_COLORS:-$CAN_USE_COLORS}
|
||||
[[ "$LINTER_COLORS" == 1 ]] && FRESET="$(tput sgr0)" || FRESET=''
|
||||
[[ "$LINTER_COLORS" == 1 ]] && FBOLD="$(tput bold)" || FBOLD=''
|
||||
[[ "$LINTER_COLORS" == 1 ]] && FDIM="$(tput dim)" || FDIM=''
|
||||
[[ "$LINTER_COLORS" == 1 ]] && FBLACK="$(tput setaf 0)" || FBLACK=''
|
||||
[[ "$LINTER_COLORS" == 1 ]] && FRED="$(tput setaf 1)" || FRED=''
|
||||
[[ "$LINTER_COLORS" == 1 ]] && FWHITE="$(tput setaf 7)" || FWHITE=''
|
||||
[[ "$LINTER_COLORS" == 1 ]] && FGREEN="$(tput setaf 2)" || FGREEN=''
|
||||
[[ "$LINTER_COLORS" == 1 ]] && FBRED="$(tput setab 1)" || FBRED=''
|
||||
[[ "$LINTER_COLORS" == 1 ]] && FBYELLOW="$(tput setab 3)" || FBYELLOW=''
|
||||
[[ "$LINTER_COLORS" == 1 ]] && FBLYELLOW="$(tput setab 11)" || FBLYELLOW=''
|
||||
|
||||
print() {
|
||||
echo -e "$*${FRESET}"
|
||||
@@ -162,6 +156,18 @@ install() {
|
||||
error "Pre-commit хук НЕ установлен"
|
||||
}
|
||||
|
||||
# Удаляет pre-commit git хук
|
||||
remove() {
|
||||
status
|
||||
[[ -d ./.git/hooks ]] || {
|
||||
print "Не найден репозиторий '$(pwd)', пропускаю"
|
||||
exit
|
||||
}
|
||||
rm -f ./.git/hooks/pre-commit && \
|
||||
success "Pre-commit hook удалён" || \
|
||||
error "Pre-commit хук НЕ удалён"
|
||||
}
|
||||
|
||||
# Запускает проверку код-стайла по всему проекту или только изменённым файлам
|
||||
style() {
|
||||
title "[php-cs-fixer] Запущена проверка код-стайла"
|
||||
@@ -413,6 +419,7 @@ fi
|
||||
case "$COMMAND" in
|
||||
h|help ) help "$1" ;;
|
||||
i|install ) install ;;
|
||||
r|remove ) remove ;;
|
||||
s|style ) style "$@" ;;
|
||||
f|fix ) fix "$@" ;;
|
||||
p|phpcs ) phpcs "$@" ;;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025, Антон Аксенов
|
||||
* This file is part of m3u.su project
|
||||
|
||||
Reference in New Issue
Block a user