Files
ave-cms/system/App/Frontend/PublicKernel.php
T
2026-07-27 12:58:44 +03:00

237 lines
7.4 KiB
PHP

<?php
/*
|--------------------------------------------------------------------------------------
| AVE.cms
|--------------------------------------------------------------------------------------
| @package AVE.cms
| @file system/App/Frontend/PublicKernel.php
| @author AVE.cms <support@ave-cms.ru>
| @copyright 2007-2026 (c) AVE.cms
| @link https://ave-cms.ru
| @version 3.3
*/
namespace App\Frontend;
defined('BASEPATH') || die('Direct access to this location is not allowed.');
use App\Common\Auth;
use App\Common\DebugChannel;
use App\Common\IpBlocker;
use App\Common\PublicModuleRuntime;
use App\Common\ReferrerLog;
use App\Common\RuntimeConstants;
use App\Common\TrafficAttribution;
use App\Common\WebMaintenance;
use App\Common\Lifecycle;
use App\Frontend\Debug\PublicDebugToolbar;
use App\Frontend\Media\ThumbnailGateway;
use App\Helpers\Debug;
/** Coordinates one native public HTTP request. */
class PublicKernel
{
public static function run()
{
\App\Helpers\Hooks::resetRuntime();
DebugChannel::reset();
PublicProfiler::reset();
self::rejectThumbnailQuery();
self::dispatchThumbnailPath();
ob_start();
require_once BASEPATH . '/system/vendor/MobileDetect/Mobile_Detect.php';
$MDetect = new \Mobile_Detect();
$initStart = microtime(true);
require_once BASEPATH . '/system/App/Frontend/PublicBootstrapEntry.php';
require_once BASEPATH . '/system/public.php';
Auth::publicBootstrap();
if (!SiteAccess::enforce()) {
return;
}
PublicDebugToolbar::boot();
Lifecycle::event('frontend.request.received', 'request', 'received', null, array(
'method' => isset($_SERVER['REQUEST_METHOD']) ? (string) $_SERVER['REQUEST_METHOD'] : 'GET',
'uri' => isset($_SERVER['REQUEST_URI']) ? (string) $_SERVER['REQUEST_URI'] : '/',
), null, array(), 'public_kernel');
Lifecycle::event('frontend.runtime.ready', 'runtime', 'ready', null, array(
'modules' => array_keys(PublicModuleRuntime::loaded()),
), null, array(), 'public_kernel');
$attribution = new TrafficAttribution();
$attribution->capture();
IpBlocker::enforce();
ReferrerLog::capture();
WebMaintenance::runIfDue();
if (PublicModuleRuntime::dispatchCurrent()) {
return;
}
self::normalizePageRequest();
PublicProfiler::record(array('INIT', 'END'), number_format(
Debug::elapsed($initStart),
3,
',',
' '
) . ' sec');
Debug::startTime('CODEEND');
$requestUri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
$pageRenderer = PublicPageLifecycle::resolve($requestUri);
PublicPageContext::resetPagination(isset($_REQUEST['id']) ? $_REQUEST['id'] : '');
$revisionId = self::revisionPreviewId();
if ($revisionId !== null) {
$documentId = PublicPageContext::documentId();
$token = isset($_GET['preview_token']) ? (string) $_GET['preview_token'] : '';
if ($revisionId <= 0 || $documentId <= 0
|| !DocumentRevisionPreview::validate($token, $documentId, $revisionId)) {
self::rejectRevisionPreview(403);
return;
}
$revisionRepository = new DocumentRevisionRepository();
$revision = $revisionRepository->findById($documentId, $revisionId);
if (!$revision) {
self::rejectRevisionPreview(404);
return;
}
$revisionFields = $revision['fields'];
DocumentFieldRepository::useRevision($documentId, $revisionFields);
$revisionDocument = isset($revision['document']) && is_array($revision['document']) ? $revision['document'] : array();
$currentDocument = PublicPageContext::document();
if ($currentDocument && !empty($revisionDocument)) {
foreach ($revisionDocument as $key => $value) { $currentDocument->{$key} = $value; }
PublicPageContext::setDocument($currentDocument);
}
$flds = (new DocumentFieldRepository())->all($documentId);
self::disablePublicCache();
header('X-AVE-Revision-Preview: ' . $revisionId);
header('Referrer-Policy: no-referrer');
header('X-Robots-Tag: noindex, nofollow');
}
$pageRenderer->render(PublicPageContext::documentId());
PublicPageLifecycle::prepareResponseStatus();
Debug::startTime('CONTENT');
$content = ob_get_clean();
self::startResponseBuffer();
Debug::$_document_content = $content;
Debug::startTime('EVALCONTENT');
PublicPageLifecycle::execute($content, get_defined_vars());
if ($revisionId !== null) {
DocumentFieldRepository::clearRevision(PublicPageContext::documentId());
}
PublicProfiler::record(array('DOCUMENT', 'EVALCONTENT'), Debug::endTime('EVALCONTENT'));
$render = ob_get_clean();
PublicPageLifecycle::finish($render, $pageRenderer);
}
protected static function revisionPreviewId()
{
$value = null;
if (isset($_GET['revision_preview'])) {
$value = (string) $_GET['revision_preview'];
} elseif (isset($_GET['revission'])) {
$value = (string) $_GET['revission'];
}
if ($value === null) {
return null;
}
return preg_match('/^[1-9][0-9]*$/', $value) ? (int) $value : 0;
}
protected static function rejectRevisionPreview($status)
{
self::disablePublicCache();
if (ob_get_level() > 0) {
ob_end_clean();
}
http_response_code((int) $status);
header('Content-Type: text/plain; charset=UTF-8');
header('Cache-Control: no-store, private');
header('X-Robots-Tag: noindex, nofollow');
echo (int) $status === 404 ? 'Ревизия не найдена.' : 'Предпросмотр ревизии недоступен.';
}
protected static function disablePublicCache()
{
if (!defined('PUBLIC_RESPONSE_NO_CACHE')) {
define('PUBLIC_RESPONSE_NO_CACHE', true);
}
if (function_exists('apache_setenv')) {
@apache_setenv('PUBLIC_NO_CACHE', '1');
}
$_SERVER['PUBLIC_NO_CACHE'] = '1';
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0', true);
}
protected static function rejectThumbnailQuery()
{
if (!isset($_GET['thumb']) && !isset($_POST['thumb']) && !isset($_REQUEST['thumb'])) {
return;
}
http_response_code(404);
header('Content-Type: text/plain; charset=UTF-8');
echo 'No image';
exit;
}
protected static function dispatchThumbnailPath()
{
RuntimeConstants::load();
$requestUri = isset($_SERVER['REQUEST_URI']) ? (string) $_SERVER['REQUEST_URI'] : '/';
$requestPath = parse_url($requestUri, PHP_URL_PATH);
$scriptName = isset($_SERVER['SCRIPT_NAME']) ? (string) $_SERVER['SCRIPT_NAME'] : '/index.php';
$basePath = rtrim(str_replace('\\', '/', dirname($scriptName)), '/');
$uploadPath = $basePath . '/' . trim(UPLOAD_DIR, '/') . '/';
if (strpos('/' . ltrim((string) $requestPath, '/'), $uploadPath) !== 0) {
return;
}
ThumbnailGateway::handle();
}
protected static function normalizePageRequest()
{
unset($_GET['module'], $_POST['module'], $_REQUEST['module']);
unset($_GET['action'], $_POST['action'], $_REQUEST['action']);
if (!PublicModuleRuntime::hasDeferredPage()) {
return;
}
$_GET['id'] = $_REQUEST['id'] = 1;
foreach (array('sysblock', 'request', 'alias_id') as $key) {
unset($_GET[$key], $_POST[$key], $_REQUEST[$key]);
}
}
protected static function startResponseBuffer()
{
$acceptEncoding = isset($_SERVER['HTTP_ACCEPT_ENCODING']) ? (string) $_SERVER['HTTP_ACCEPT_ENCODING'] : '';
if (strpos($acceptEncoding, 'gzip') !== false && defined('GZIP_COMPRESSION') && GZIP_COMPRESSION) {
ob_start('ob_gzhandler');
return;
}
ob_start();
}
}