Files
web-site/Internal/package-zerocore/Kernel.php
T
2026-07-01 16:44:17 +03:00

462 lines
14 KiB
PHP

<?php namespace ZN;
/**
* ZN PHP Web Framework
*
* "Simplicity is the ultimate sophistication." ~ Da Vinci
*
* @package ZN
* @license MIT [http://opensource.org/licenses/MIT]
* @author Ozan UYKUN [ozan@znframework.com]
*/
use ReflectionClass;
use ReflectionMethod;
use ZN\ErrorHandling\Errors;
use ZN\ErrorHandling\Exceptions;
use ZN\Inclusion\Project\Theme;
use ZN\Inclusion\Project\Masterpage;
class Kernel
{
/**
* The system initializes the kernel.
*
* @param void
*
* @return void
*/
public static function start()
{
# The system starts the load time.
define('START_BENCHMARK', microtime(true));
# Enables the ob_gzhandler method if it is turned on.
define('HTACCESS_CONFIG', Config::get('Htaccess'));
# OB process is starting.
Buffering::start();
# Enables route filters.
Singleton::class('ZN\Routing\Route')->filter();
# Session process is starting.
Storage::start();
# Sends defined header information.
Base::headers(PROJECT_CONFIG['headers'] ?? []);
# Activates the project mode.
In::projectMode();
# Sets the timezone.
if( IS::timeZone($timezone = (PROJECT_CONFIG['timezone'] ?? '')) )
{
date_default_timezone_set($timezone);
}
# Enables defined ini configurations.
if( $iniset = Config::get('Ini') )
{
Config::iniset($iniset);
}
# The software apache and htaccess allow
# the .htaccess file to be rearranged according to the changes
# if the file is open for writing.
if( IS::software() === 'apache' )
{
Htaccess::create();
}
# Enables processing of changes to the robots.txt file if it is open.
if( Config::robots('createFile') === true )
{
Robots::create();
}
# Sets the system's language.
# The lang configuration must be set to true
# in Config/Services.php file to enable this condition.
if( Lang::current() )
{
Lang::setByURI(); // @codeCoverageIgnore
}
# If the setting is active, it loads the startup files.
if( ($starting = Config::get('Starting'))['autoload']['status'] === true )
{
self::startingFileLoader($starting);
}
# If the project mode restoration is set, restoration is started.
if( PROJECT_MODE === 'restoration' )
{
Restoration::mode();
}
# It checks for invalid requests.
In::invalidRequest('disallowMethods', true);
In::invalidRequest('allowMethods', false);
# Configures the startup controller setting.
In::startingConfig('constructors');
}
/**
* Run the system kernel.
*
* @param void
*
* @return void
*/
public static function run()
{
# The kernel is starting.
self::start();
$parameters = CURRENT_CFPARAMETERS;
$function = CURRENT_CFUNCTION;
$openFunction = CURRENT_COPEN_PAGE;
$page = CURRENT_CNAMESPACE . CURRENT_CONTROLLER;
// @codeCoverageIgnoreStart
# If an invalid parameter is entered, it will redirect to the opening method.
if( self::methodNotExists($page, $function) )
{
array_unshift($parameters, $function);
$function = $openFunction;
# If the request is invalid, it will be redirected.
if( self::methodNotExists($page, $function) )
{
self::invalidControllerPath($page, $function);
}
}
// @codeCoverageIgnoreEnd
# Define real current parameters.
define('CURRENT_CPARAMETERS', $parameters);
# The view path is being controlled so that the view can be loaded automatically.
self::viewPathFinder($function, $viewPath, $wizardPath);
# Resolving dependency injections.
# The controller is being called.
self::callController($page, $function, CURRENT_CPARAMETERS);
# The view is automatically loading.
self::viewAutoload($wizardPath, $viewPath);
# The operation of the system core is completes.
self::end();
}
/**
* Protected if method not exists
*
* @codeCoverageIgnore
*/
protected static function methodNotExists($page, $function)
{
return ! method_exists($page, $function) || ! (new ReflectionMethod($page, $function))->isPublic();
}
/**
* Protected invalid controller path
*
* @codeCoverageIgnore
*/
protected static function invalidControllerPath(&$controller, &$function)
{
# Run direct show 404 page.
if( $runWithoutRedirect = Config::get('Routing', 'runWithoutRedirect') )
{
$controllerAndMethod = explode('/', $runWithoutRedirect);
if( ! empty($controllerAndMethod[0]) )
{
$info = Autoloader::getClassFileInfo(PROJECT_CONTROLLER_NAMESPACE . $controllerAndMethod[0]);
define('RUN_WITHOUT_REDIRECT_CONTROLLER', pathinfo($info['path'], PATHINFO_FILENAME));
$controller = PROJECT_CONTROLLER_NAMESPACE . RUN_WITHOUT_REDIRECT_CONTROLLER;
}
if( ! empty($controllerAndMethod[1]) )
{
$function = $controllerAndMethod[1];
}
}
# Redirect show 404 page
else if( $show404 = Config::get('Routing', 'show404') )
{
Helper::report('InvalidRequest', "Invalid request made to {$controller}/{$function} page!");
Response::redirect($show404);
}
# Invalid operation function
else
{
throw new Exception(self::getLang('invalidOpenFunction'));
}
}
/**
* Protected call controller
*
* [added]5.7.7
*/
protected static function callController($page, $function, $parameters)
{
# The reflection of the active controller is being taken.
$reflector = new ReflectionClass($page);
# The active controller's construct method is being resolved.
$controller = new $page(...In::resolvingDependencyInjections($reflector, $page, '__construct'));
# The active controller's current method is being resolved.
$parameters = (In::resolvingDependencyInjections($reflector, $page, $function, $getReturnType) ?: $parameters);
# If the return value of the controller method is set to void,
# ajax control is performed.
if( $getReturnType === 'void' )
{
Request::isAjax() or exit; // @codeCoverageIgnore
}
# The parameters of the active controller method are being resolved.
$controller->$function(...$parameters);
}
/**
* View path finder
*
* @param string $function
* @param string &$viewpath
* @param string &$wizardPath
*
* @return void
*/
public static function viewPathFinder($function, &$viewPath, &$wizardPath)
{
# Retrieves the automatic upload setting from the configuration file.
$viewNameType = Config::get('Starting', 'viewNameType') ?: 'directory';
# The automatic load type: file
if( $viewNameType === 'file' )
{
# Setting the automatic view upload type in the file standard.
$viewFunction = $function === CURRENT_COPEN_PAGE ? NULL : '-' . $function; // @codeCoverageIgnore
# Views/controller-method.php
$viewDir = self::viewPathCreator($viewFunction); // @codeCoverageIgnore
}
# The automatic load type: directory
else
{
# Setting the automatic view upload type in the directory standard.
$viewFunction = $function === CURRENT_COPEN_PAGE ? CURRENT_COPEN_PAGE : $function;
# Views/controller/method.php
$viewDir = self::viewPathCreator('/' . $viewFunction);
}
$viewPath = $viewDir . '.php';
$wizardPath = $viewDir . '.wizard.php';
}
/**
* Autoload view.
*
* @param string $wizardPath
* @param string $viewPath
*
* @return void
*/
public static function viewAutoload($wizardPath, $viewPath)
{
# 5.3.62[added]|5.3.77|5.6.0[edited]
if( Config::get('Starting', 'ajaxCodeContinue') === false && Request::isAjax() )
{
return; // @codeCoverageIgnore
}
# Loading can not be performed because the appropriate view page is not found.
$usableView = '';
# It is checked whether the file to be automatically loaded has been included before.
if( ! IS::import($viewPath) && ! IS::import($wizardPath) )
{
# First, it is tried to load the file with the wizard extension.
if( is_file($wizardPath) )
{
$usableView = self::viewLoader($wizardPath);
}
// @codeCoverageIgnoreStart
# If the file can not be loaded, the attempt is made to load the file with the standard extension.
elseif( is_file($viewPath) )
{
$usableView = self::viewLoader($viewPath);
}
// @codeCoverageIgnoreEnd
}
# It is checked whether data is sent to the masterpage.
# If data transmission is done, the masterpage is activated.
if( ! empty($masterpageData = In::$masterpage) )
{
$inData = array_merge(...$masterpageData);
}
else
{
$inData = []; // @codeCoverageIgnore
}
# Merge sent data.
$data = array_merge($inData, Masterpage::$data);
# if sent data via Masterpage. Enables MasterPage.
if( ($data['masterpage'] ?? NULL) === true || ! empty($data) )
{
(new Inclusion\Masterpage)->headData($data)->bodyContent($usableView)->use($data);
}
// @codeCoverageIgnoreStart
# Otherwise, it prints without using the masterpage.
elseif( ! empty($usableView) )
{
echo $usableView;
}
// @codeCoverageIgnoreEnd
}
/**
* End kernel.
*
* @param void
*
* @return void
*/
public static function end()
{
In::startingConfig('destructors');
if( PROJECT_CONFIG['log']['createFile'] === true && $errorLast = Errors::last() )
{
$lang = Lang::default('ZN\ErrorHandling\ErrorHandlingDefaultLanguage')::select('Templates');
$message = $lang['line'] .':'.$errorLast['line'].', '.
$lang['file'] .':'.$errorLast['file'].', '.
$lang['message'].':'.$errorLast['message'];
Helper::report('GeneralError', $message);
}
if( PROJECT_MODE !== 'publication' )
{
Exceptions::restore(); Errors::restore();
}
# The system finishes the load time.
define('FINISH_BENCHMARK', microtime(true));
# Creates a table that calculates the operating performance of the system.
# To open this table, follow the steps below.
In::benchmarkReport();
# The buffer is being turned off.
Buffering::end();
}
/**
* Protected get lang
*
* @codeCoverageIgnore
*/
protected static function getLang(string $type)
{
return Lang::default('ZN\CoreDefaultLanguage')::select('Kernel')['kernel:'.$type];
}
/**
* Protected Starting File Loader
*
* @param array $starting
*
* @return void
*/
protected static function startingFileLoader($starting)
{
if( empty(AUTOLOAD_DIR) )
{
return false; // @codeCoverageIgnore
}
# It is specified whether the subfile scanning can be done or not.
$autoloadRecursive = $starting['autoload']['recursive'];
# The files to be automatically loaded are merged at startup.
# External & Project Files
$startingAutoload = (array_merge
(
Filesystem::getRecursiveFiles(AUTOLOAD_DIR , $autoloadRecursive),
Filesystem::getRecursiveFiles(EXTERNAL_AUTOLOAD_DIR, $autoloadRecursive)
));
// @codeCoverageIgnoreStart
# The file upload process is starting.
if( ! empty($startingAutoload) ) foreach( $startingAutoload as $file )
{
if( Filesystem::getExtension($file) === 'php' )
{
if( is_file($file) )
{
Base::import($file);
}
}
}
// @codeCoverageIgnoreEnd
}
/**
* Protected View Path Creator
*
* 5.7.0[updated]
*
* @param string $fix
*
* @return string
*/
protected static function viewPathCreator($fix)
{
$currentController = defined('RUN_WITHOUT_REDIRECT_CONTROLLER')
? RUN_WITHOUT_REDIRECT_CONTROLLER // @codeCoverageIgnore
: CURRENT_CONTROLLER;
$view = $currentController . $fix;
# If the Theme::active() method is in effect,
# the path information is rearranged.
if( ($active = Theme::$active) !== NULL )
{
if( is_dir(PAGES_DIR . $active) )
{
$view = $active . $view;
}
}
return PAGES_DIR . $view;
}
/**
* Protected View Loader
*
* @param string $path
*
* @return mixed
*/
protected static function viewLoader($path)
{
return Inclusion\View::use(str_replace(PAGES_DIR, '', $path ?? ''), NULL, true);
}
}