Your IP : 18.118.208.127


Current Path : /var/www/www-root/data/www.catalog.monolith-realty.ru/bitrix/modules/main/lib/
Upload File :
Current File : /var/www/www-root/data/www.catalog.monolith-realty.ru/bitrix/modules/main/lib/application.php

<?php

/**
 * Bitrix Framework
 * @package bitrix
 * @subpackage main
 * @copyright 2001-2023 Bitrix
 */

namespace Bitrix\Main;

use Bitrix\Main\Config\Configuration;
use Bitrix\Main\DI\ServiceLocator;
use Bitrix\Main\Routing\CompileCache;
use Bitrix\Main\Routing\Route;
use Bitrix\Main\Routing\Router;
use Bitrix\Main\Routing\RoutingConfigurator;
use Bitrix\Main\Session\CompositeSessionManager;
use Bitrix\Main\Session\KernelSessionProxy;
use Bitrix\Main\Session\SessionConfigurationResolver;
use Bitrix\Main\Session\SessionInterface;
use Bitrix\Main\Session\Handlers\CookieSessionHandler;

/**
 * Base class for any application.
 */
abstract class Application
{
	const JOB_PRIORITY_NORMAL = 100;
	const JOB_PRIORITY_LOW = 50;

	/**
	 * @var Application
	 */
	protected static $instance;
	protected bool $initialized = false;
	protected bool $terminating = false;
	/**
	 * Execution context.
	 *
	 * @var Context | HttpContext
	 */
	protected $context;
	/** @var Router */
	protected $router;
	/** @var Route */
	protected $currentRoute;
	/**
	 * Pool of database connections.
	 * @var Data\ConnectionPool
	 */
	protected $connectionPool;
	/**
	 * Managed cache instance.
	 * @var \Bitrix\Main\Data\ManagedCache
	 */
	protected $managedCache;
	/**
	 * Tagged cache instance.
	 * @var \Bitrix\Main\Data\TaggedCache
	 */
	protected $taggedCache;
	/** @var SessionInterface */
	protected $session;
	/** @var SessionInterface */
	protected $kernelSession;
	/** @var CompositeSessionManager */
	protected $compositeSessionManager;
	/** @var Data\LocalStorage\SessionLocalStorageManager */
	protected $sessionLocalStorageManager;
	/*
	 * @var \SplPriorityQueue
	 */
	protected $backgroundJobs;
	/** @var License */
	protected $license;

	/**
	 * Creates new application instance.
	 */
	protected function __construct()
	{
		ServiceLocator::getInstance()->registerByGlobalSettings();
		$this->backgroundJobs = new \SplPriorityQueue();
		$this->initializeExceptionHandler();
		$this->initializeCache();
		$this->createDatabaseConnection();
	}

	/**
	 * Returns current instance of the Application.
	 *
	 * @return Application | HttpApplication
	 */
	public static function getInstance()
	{
		if (!isset(static::$instance))
		{
			static::$instance = new static();
		}
		return static::$instance;
	}

	/**
	 * @return bool
	 */
	public static function hasInstance()
	{
		return isset(static::$instance);
	}

	/**
	 * @deprecated
	 * Does nothing, will be removed soon.
	 */
	public function initializeBasicKernel()
	{
	}

	/**
	 * Does full kernel initialization. Should be called somewhere after initializeBasicKernel().
	 *
	 * @param array $params Parameters of the current request (depends on application type)
	 */
	public function initializeExtendedKernel(array $params)
	{
		$this->initializeContext($params);

		if (!$this->initialized)
		{
			$this->initializeSessions();
			$this->initializeSessionLocalStorage();

			$this->initialized = true;
		}
	}

	private function initializeSessions()
	{
		$resolver = new SessionConfigurationResolver(Configuration::getInstance());
		$resolver->resolve();

		$this->session = $resolver->getSession();
		$this->kernelSession = $resolver->getKernelSession();

		$this->compositeSessionManager = new CompositeSessionManager(
			$this->kernelSession,
			$this->session
		);
	}

	private function initializeSessionLocalStorage()
	{
		$cacheEngine = Data\Cache::createCacheEngine();
		if ($cacheEngine instanceof Data\LocalStorage\Storage\CacheEngineInterface)
		{
			$localSessionStorage = new Data\LocalStorage\Storage\CacheStorage($cacheEngine);
		}
		else
		{
			$localSessionStorage = new Data\LocalStorage\Storage\NativeSessionStorage(
				$this->getSession()
			);
		}

		$this->sessionLocalStorageManager = new Data\LocalStorage\SessionLocalStorageManager($localSessionStorage);
		$configLocalStorage = Config\Configuration::getValue("session_local_storage") ?: [];
		if (isset($configLocalStorage['ttl']))
		{
			$this->sessionLocalStorageManager->setTtl($configLocalStorage['ttl']);
		}
	}

	/**
	 * @return Router
	 */
	public function getRouter(): Router
	{
		if ($this->router === null)
		{
			$this->router = $this->initializeRouter();
		}

		return $this->router;
	}

	/**
	 * @param Router $router
	 */
	public function setRouter(Router $router): void
	{
		$this->router = $router;
	}

	/**
	 * @return Route
	 */
	public function getCurrentRoute(): Route
	{
		return $this->currentRoute;
	}

	public function hasCurrentRoute(): bool
	{
		return isset($this->currentRoute);
	}

	/**
	 * @param Route $currentRoute
	 */
	public function setCurrentRoute(Route $currentRoute): void
	{
		$this->currentRoute = $currentRoute;
	}

	protected function initializeRouter()
	{
		$routes = new RoutingConfigurator();
		$router = new Router();
		$routes->setRouter($router);

		$this->setRouter($router);

		// files with routes
		$files = [];

		// user files
		$routingConfig = Configuration::getInstance()->get('routing');
		$documentRoot = $this->context->getServer()->getDocumentRoot();

		if (!empty($routingConfig['config']))
		{
			$fileNames = $routingConfig['config'];

			foreach ($fileNames as $fileName)
			{
				foreach (['local', 'bitrix'] as $vendor)
				{
					$filename = $documentRoot . '/' . $vendor . '/routes/' . basename($fileName);

					if (file_exists($filename))
					{
						$files[] = $filename;
					}
				}
			}
		}

		// system files
		if (file_exists($documentRoot . '/bitrix/routes/web_bitrix.php'))
		{
			$files[] = $documentRoot . '/bitrix/routes/web_bitrix.php';
		}

		foreach ($files as $file)
		{
			$callback = include $file;
			$callback($routes);
		}

		$router->releaseRoutes();

		// cache for route compiled data
		CompileCache::handle($files, $router);

		return $router;
	}

	/**
	 * Initializes context of the current request.
	 * Should be implemented in subclass.
	 * @param array $params
	 */
	abstract protected function initializeContext(array $params);

	/**
	 * Starts request execution. Should be called after initialize.
	 * Should be implemented in subclass.
	 */
	abstract public function start();

	/**
	 * Runs controller and its action and sends response to the output.
	 *
	 * It's a stub method, and we can't mark it as abstract because there is compatibility.
	 * @return void
	 */
	public function run()
	{
	}

	/**
	 * Ends work of application.
	 * Sends response and then terminates execution.
	 * If there is no $response the method will use Context::$response.
	 *
	 * @param int $status
	 * @param Response|null $response
	 *
	 * @return void
	 */
	public function end($status = 0, Response $response = null)
	{
		if ($response === null)
		{
			//use default response
			$response = $this->context->getResponse();

			//it's possible to have open buffers
			$content = '';
			$n = ob_get_level();
			while (($c = ob_get_clean()) !== false && $n > 0)
			{
				$content .= $c;
				$n--;
			}

			if ($content <> '')
			{
				$response->appendContent($content);
			}
		}

		$this->handleResponseBeforeSend($response);

		//this is the last point of output - all output below will be ignored
		$response->send();

		$this->terminate($status);
	}

	protected function handleResponseBeforeSend(Response $response): void
	{
		$kernelSession = $this->getKernelSession();
		if (!($kernelSession instanceof KernelSessionProxy) && $kernelSession->isStarted())
		{
			//save session data in cookies
			$handler = $kernelSession->getSessionHandler();
			if ($handler instanceof CookieSessionHandler)
			{
				if ($response instanceof HttpResponse)
				{
					$handler->setResponse($response);
				}
			}
			$kernelSession->save();
		}
	}

	/**
	 * Terminates application by invoking exit().
	 * It's the right way to finish application.
	 *
	 * @param int $status
	 * @return void
	 */
	public function terminate($status = 0)
	{
		if ($this->terminating)
		{
			// recursion control
			return;
		}

		$this->terminating = true;

		//old kernel staff
		\CMain::RunFinalActionsInternal();

		//Release session
		session_write_close();

		$pool = $this->getConnectionPool();

		$pool->useMasterOnly(true);

		$this->runBackgroundJobs();

		$pool->useMasterOnly(false);

		Data\ManagedCache::finalize();

		$pool->disconnect();

		exit($status);
	}

	/**
	 * Exception handler can be initialized through the Config\Configuration (.settings.php file).
	 *
	 * 'exception_handling' => array(
	 *        'value' => array(
	 *            'debug' => true,        // output exception on screen
	 *            'handled_errors_types' => E_ALL & ~E_STRICT & ~E_NOTICE,    // catchable error types, printed to log
	 *            'exception_errors_types' => E_ALL & ~E_NOTICE & ~E_STRICT,  // error types from catchable which throws exceptions
	 *            'ignore_silence' => false,      // ignore @
	 *            'assertion_throws_exception' => true,       // assertion throws exception
	 *            'assertion_error_type' => 256,
	 *            'log' => array(
	 *              'class_name' => 'MyLog',        // custom log class, must extend ExceptionHandlerLog; can be omited, in this case default Diag\FileExceptionHandlerLog will be used
	 *              'extension' => 'MyLogExt',      // php extension, is used only with 'class_name'
	 *              'required_file' => 'modules/mylog.module/mylog.php'     // included file, is used only with 'class_name'
	 *                'settings' => array(        // any settings for 'class_name'
	 *                    'file' => 'bitrix/modules/error.log',
	 *                    'log_size' => 1000000,
	 *                ),
	 *            ),
	 *        ),
	 *        'readonly' => false,
	 *    ),
	 *
	 */
	protected function initializeExceptionHandler()
	{
		$exceptionHandler = new Diag\ExceptionHandler();

		$exceptionHandling = Config\Configuration::getValue("exception_handling");
		if ($exceptionHandling == null)
		{
			$exceptionHandling = [];
		}

		if (!isset($exceptionHandling["debug"]) || !is_bool($exceptionHandling["debug"]))
		{
			$exceptionHandling["debug"] = false;
		}
		$exceptionHandler->setDebugMode($exceptionHandling["debug"]);

		if (!empty($exceptionHandling['track_modules']) && is_array($exceptionHandling['track_modules']))
		{
			$exceptionHandler->setTrackModules($exceptionHandling['track_modules']);
		}

		if (isset($exceptionHandling["handled_errors_types"]) && is_int($exceptionHandling["handled_errors_types"]))
		{
			$exceptionHandler->setHandledErrorsTypes($exceptionHandling["handled_errors_types"]);
		}

		if (isset($exceptionHandling["exception_errors_types"]) && is_int($exceptionHandling["exception_errors_types"]))
		{
			$exceptionHandler->setExceptionErrorsTypes($exceptionHandling["exception_errors_types"]);
		}

		if (isset($exceptionHandling["ignore_silence"]) && is_bool($exceptionHandling["ignore_silence"]))
		{
			$exceptionHandler->setIgnoreSilence($exceptionHandling["ignore_silence"]);
		}

		if (isset($exceptionHandling["assertion_throws_exception"]) && is_bool($exceptionHandling["assertion_throws_exception"]))
		{
			$exceptionHandler->setAssertionThrowsException($exceptionHandling["assertion_throws_exception"]);
		}

		if (isset($exceptionHandling["assertion_error_type"]) && is_int($exceptionHandling["assertion_error_type"]))
		{
			$exceptionHandler->setAssertionErrorType($exceptionHandling["assertion_error_type"]);
		}

		$exceptionHandler->initialize(
			[$this, "createExceptionHandlerOutput"],
			[$this, "createExceptionHandlerLog"]
		);

		ServiceLocator::getInstance()->addInstance('exceptionHandler', $exceptionHandler);
	}

	public function createExceptionHandlerLog()
	{
		$exceptionHandling = Config\Configuration::getValue("exception_handling");

		if (!is_array($exceptionHandling) || !isset($exceptionHandling["log"]) || !is_array($exceptionHandling["log"]))
		{
			return null;
		}

		$options = $exceptionHandling["log"];

		$log = null;

		if (!empty($options["class_name"]))
		{
			if (!empty($options["extension"]) && !extension_loaded($options["extension"]))
			{
				return null;
			}

			if (!empty($options["required_file"]) && ($requiredFile = Loader::getLocal($options["required_file"])) !== false)
			{
				require_once($requiredFile);
			}

			$className = $options["class_name"];
			if (!class_exists($className))
			{
				return null;
			}

			$log = new $className();
		}
		elseif (isset($options["settings"]) && is_array($options["settings"]))
		{
			$log = new Diag\FileExceptionHandlerLog();
		}
		else
		{
			return null;
		}

		$log->initialize(
			isset($options["settings"]) && is_array($options["settings"]) ? $options["settings"] : []
		);

		return $log;
	}

	public function createExceptionHandlerOutput()
	{
		return new Diag\ExceptionHandlerOutput();
	}

	/**
	 * Creates database connection pool.
	 */
	protected function createDatabaseConnection()
	{
		$this->connectionPool = new Data\ConnectionPool();
	}

	protected function initializeCache()
	{
		//TODO: Should be transfered to where GET parameter is defined in future
		//magic parameters: show cache usage statistics
		$show_cache_stat = "";
		if (isset($_GET["show_cache_stat"]))
		{
			$show_cache_stat = (strtoupper($_GET["show_cache_stat"]) == "Y" ? "Y" : "");
			@setcookie("show_cache_stat", $show_cache_stat, false, "/");
		}
		elseif (isset($_COOKIE["show_cache_stat"]))
		{
			$show_cache_stat = $_COOKIE["show_cache_stat"];
		}
		Data\Cache::setShowCacheStat($show_cache_stat === "Y");

		if (isset($_GET["clear_cache_session"]))
		{
			Data\Cache::setClearCacheSession($_GET["clear_cache_session"] === 'Y');
		}
		if (isset($_GET["clear_cache"]))
		{
			Data\Cache::setClearCache($_GET["clear_cache"] === 'Y');
		}
	}

	/**
	 * @return \Bitrix\Main\Diag\ExceptionHandler
	 */
	public function getExceptionHandler()
	{
		return ServiceLocator::getInstance()->get('exceptionHandler');
	}

	/**
	 * Returns database connections pool object.
	 *
	 * @return Data\ConnectionPool
	 */
	public function getConnectionPool()
	{
		return $this->connectionPool;
	}

	/**
	 * Returns context of the current request.
	 *
	 * @return Context | HttpContext
	 */
	public function getContext()
	{
		return $this->context;
	}

	/**
	 * Modifies context of the current request.
	 *
	 * @param Context $context
	 */
	public function setContext(Context $context)
	{
		$this->context = $context;
	}

	public function getLicense(): License
	{
		if (!$this->license)
		{
			$this->license = new License();
		}

		return $this->license;
	}

	/**
	 * Static method returns database connection for the specified name.
	 * If name is empty - default connection is returned.
	 *
	 * @static
	 * @param string $name Name of database connection. If empty - default connection.
	 * @return Data\Connection|DB\Connection
	 */
	public static function getConnection($name = "")
	{
		$pool = Application::getInstance()->getConnectionPool();
		return $pool->getConnection($name);
	}

	/**
	 * Returns new instance of the Cache object.
	 *
	 * @return Data\Cache
	 */
	public function getCache()
	{
		return Data\Cache::createInstance();
	}

	/**
	 * Returns manager of the managed cache.
	 *
	 * @return Data\ManagedCache
	 */
	public function getManagedCache()
	{
		if ($this->managedCache == null)
		{
			$this->managedCache = new Data\ManagedCache();
		}

		return $this->managedCache;
	}

	/**
	 * Returns manager of the managed cache.
	 *
	 * @return Data\TaggedCache
	 */
	public function getTaggedCache()
	{
		if ($this->taggedCache == null)
		{
			$this->taggedCache = new Data\TaggedCache();
		}

		return $this->taggedCache;
	}

	final public function getSessionLocalStorageManager(): Data\LocalStorage\SessionLocalStorageManager
	{
		return $this->sessionLocalStorageManager;
	}

	final public function getLocalSession($name): Data\LocalStorage\SessionLocalStorage
	{
		return $this->sessionLocalStorageManager->get($name);
	}

	final public function getKernelSession(): SessionInterface
	{
		return $this->kernelSession;
	}

	final public function getSession(): SessionInterface
	{
		return $this->session;
	}

	final public function getCompositeSessionManager(): CompositeSessionManager
	{
		return $this->compositeSessionManager;
	}

	/**
	 * Returns UF manager.
	 *
	 * @return \CUserTypeManager
	 */
	public static function getUserTypeManager()
	{
		global $USER_FIELD_MANAGER;
		return $USER_FIELD_MANAGER;
	}

	/**
	 * Returns true id server is in utf-8 mode. False - otherwise.
	 *
	 * @return bool
	 */
	public static function isUtfMode()
	{
		static $isUtfMode = null;
		if ($isUtfMode === null)
		{
			$isUtfMode = Config\Configuration::getValue("utf_mode");
			if ($isUtfMode === null)
			{
				$isUtfMode = false;
			}
		}
		return $isUtfMode;
	}

	/**
	 * Returns server document root.
	 *
	 * @return null|string
	 */
	public static function getDocumentRoot()
	{
		static $documentRoot = null;
		if ($documentRoot != null)
		{
			return $documentRoot;
		}

		$context = Application::getInstance()->getContext();
		if ($context != null)
		{
			$server = $context->getServer();
			if ($server != null)
			{
				return $documentRoot = $server->getDocumentRoot();
			}
		}

		return Loader::getDocumentRoot();
	}

	/**
	 * Returns personal root directory (relative to document root)
	 *
	 * @return null|string
	 */
	public static function getPersonalRoot()
	{
		static $personalRoot = null;
		if ($personalRoot != null)
		{
			return $personalRoot;
		}

		$context = Application::getInstance()->getContext();
		if ($context != null)
		{
			$server = $context->getServer();
			if ($server != null)
			{
				return $personalRoot = $server->getPersonalRoot();
			}
		}

		return $_SERVER["BX_PERSONAL_ROOT"] ?? '/bitrix';
	}

	/**
	 * Resets accelerator if any.
	 */
	public static function resetAccelerator(string $filename = null)
	{
		if (defined("BX_NO_ACCELERATOR_RESET"))
		{
			return;
		}

		if (Config\Configuration::getValue("no_accelerator_reset"))
		{
			return;
		}

		if (function_exists("opcache_reset"))
		{
			if ($filename !== null)
			{
				opcache_invalidate($filename);
			}
			else
			{
				opcache_reset();
			}
		}
		elseif (function_exists("wincache_refresh_if_changed"))
		{
			wincache_refresh_if_changed();
		}
	}

	/**
	 * Adds a job to do after the response was sent.
	 * @param callable $job
	 * @param array $args
	 * @param int $priority
	 * @return $this
	 */
	public function addBackgroundJob(callable $job, array $args = [], $priority = self::JOB_PRIORITY_NORMAL)
	{
		$this->backgroundJobs->insert([$job, $args], $priority);

		return $this;
	}

	protected function runBackgroundJobs()
	{
		$lastException = null;
		$exceptionHandler = $this->getExceptionHandler();

		//jobs can be added from jobs
		while ($this->backgroundJobs->valid())
		{
			//clear the queue
			$jobs = [];
			foreach ($this->backgroundJobs as $job)
			{
				$jobs[] = $job;
			}

			//do job
			foreach ($jobs as $job)
			{
				try
				{
					call_user_func_array($job[0], $job[1]);
				}
				catch (\Throwable $exception)
				{
					$lastException = $exception;
					$exceptionHandler->writeToLog($exception);
				}
			}
		}

		if ($lastException !== null)
		{
			throw $lastException;
		}
	}

	/**
	 * Returns true if the application is fully initialized.
	 * @return bool
	 */
	public function isInitialized()
	{
		return $this->initialized;
	}
}