Bitrix-D7  20.5.0
main/lib/application.php
См. документацию.
1 <?php
2 /**
3  * Bitrix Framework
4  * @package bitrix
5  * @subpackage main
6  * @copyright 2001-2012 Bitrix
7  */
8 namespace Bitrix\Main;
9 
10 use Bitrix\Main\Config\Configuration;
14 use Bitrix\Main\IO;
21 
22 /**
23  * Base class for any application.
24  */
25 abstract class Application
26 {
27  const JOB_PRIORITY_NORMAL = 100;
28  const JOB_PRIORITY_LOW = 50;
29 
30  /**
31  * @var Application
32  */
33  protected static $instance = null;
34 
35  protected $isBasicKernelInitialized = false;
36  protected $isExtendedKernelInitialized = false;
37 
38  /**
39  * Execution context.
40  *
41  * @var Context
42  */
43  protected $context;
44 
45  /** @var Router */
46  protected $router;
47 
48  /** @var Route */
49  protected $currentRoute;
50 
51  /**
52  * Pool of database connections.
53  * @var Data\ConnectionPool
54  */
55  protected $connectionPool;
56 
57  /**
58  * Managed cache instance.
59  * @var \Bitrix\Main\Data\ManagedCache
60  */
61  protected $managedCache;
62 
63  /**
64  * Tagged cache instance.
65  * @var \Bitrix\Main\Data\TaggedCache
66  */
67  protected $taggedCache;
68 
69  /** @var SessionInterface */
70  protected $session;
71  /** @var SessionInterface */
72  protected $kernelSession;
73  /** @var CompositeSessionManager */
75  /** @var Data\LocalStorage\SessionLocalStorageManager */
77 
78  /*
79  * @var \SplPriorityQueue
80  */
81  protected $backgroundJobs;
82 
83  /**
84  * Creates new application instance.
85  */
86  protected function __construct()
87  {
88  $this->backgroundJobs = new \SplPriorityQueue();
89  }
90 
91  /**
92  * Returns current instance of the Application.
93  *
94  * @return Application
95  */
96  public static function getInstance()
97  {
98  if (!isset(static::$instance))
99  static::$instance = new static();
100 
101  return static::$instance;
102  }
103 
104  /**
105  * Does minimally possible kernel initialization.
106  */
107  public function initializeBasicKernel()
108  {
109  if ($this->isBasicKernelInitialized)
110  return;
111  $this->isBasicKernelInitialized = true;
112 
113  ServiceLocator::getInstance()->registerByGlobalSettings();
115  $this->initializeCache();
116  $this->createDatabaseConnection();
117  }
118 
119  /**
120  * Does full kernel initialization. Should be called somewhere after initializeBasicKernel().
121  *
122  * @param array $params Parameters of the current request (depends on application type)
123  */
124  public function initializeExtendedKernel(array $params)
125  {
126  if ($this->isExtendedKernelInitialized)
127  return;
128  $this->isExtendedKernelInitialized = true;
129 
130  $this->initializeSessions();
131  $this->initializeContext($params);
132  $this->initializeSessionLocalStorage();
133  }
134 
135  private function initializeSessions()
136  {
138  $resolver->resolve();
139 
140  $this->session = $resolver->getSession();
141  $this->kernelSession = $resolver->getKernelSession();
142 
143  $this->compositeSessionManager = new CompositeSessionManager(
144  $this->kernelSession,
145  $this->session
146  );
147  }
148 
149  private function initializeSessionLocalStorage()
150  {
151  $cacheEngine = Data\Cache::createCacheEngine();
152  if ($cacheEngine instanceof Data\LocalStorage\Storage\CacheEngineInterface)
153  {
154  $localSessionStorage = new Data\LocalStorage\Storage\CacheStorage($cacheEngine);
155  }
156  else
157  {
158  $localSessionStorage = new Data\LocalStorage\Storage\NativeSessionStorage(
159  $this->getSession()
160  );
161  }
162 
163  $this->sessionLocalStorageManager = new Data\LocalStorage\SessionLocalStorageManager($localSessionStorage);
164  $configLocalStorage = Config\Configuration::getValue("session_local_storage") ?: [];
165  if (isset($configLocalStorage['ttl']))
166  {
167  $this->sessionLocalStorageManager->setTtl($configLocalStorage['ttl']);
168  }
169  }
170 
171  /**
172  * @return Router
173  */
174  public function getRouter(): Router
175  {
176  return $this->router;
177  }
178 
179  /**
180  * @param Router $router
181  */
182  public function setRouter(Router $router): void
183  {
184  $this->router = $router;
185  }
186 
187  /**
188  * @return Route
189  */
190  public function getCurrentRoute(): Route
191  {
192  return $this->currentRoute;
193  }
194 
195  /**
196  * @param Route $currentRoute
197  */
198  public function setCurrentRoute(Route $currentRoute): void
199  {
200  $this->currentRoute = $currentRoute;
201  }
202 
203  /**
204  * Initializes context of the current request.
205  * Should be implemented in subclass.
206  * @param array $params
207  */
208  abstract protected function initializeContext(array $params);
209 
210  /**
211  * Starts request execution. Should be called after initialize.
212  * Should be implemented in subclass.
213  */
214  abstract public function start();
215 
216  /**
217  * Runs controller and its action and sends response to the output.
218  *
219  * It's a stub method and we can't mark it as abstract because there is compatibility.
220  * @return void
221  */
222  public function run()
223  {
224  }
225 
226  /**
227  * Ends work of application.
228  * Sends response and then terminates execution.
229  * If there is no $response the method will use Context::$response.
230  *
231  * @param int $status
232  * @param Response|null $response
233  *
234  * @return void
235  */
236  public function end($status = 0, Response $response = null)
237  {
238  if($response === null)
239  {
240  //use default response
241  $response = $this->context->getResponse();
242 
243  //it's possible to have open buffers
244  $content = '';
245  while(($c = ob_get_clean()) !== false)
246  {
247  $content .= $c;
248  }
249 
250  if($content <> '')
251  {
252  $response->appendContent($content);
253  }
254  }
255 
256  $this->handleResponseBeforeSend($response);
257  //this is the last point of output - all output below will be ignored
258  $response->send();
259 
260  $this->terminate($status);
261  }
262 
263  protected function handleResponseBeforeSend(Response $response): void
264  {
265  $kernelSession = $this->getKernelSession();
266  if (!($kernelSession instanceof KernelSessionProxy) && $kernelSession->isStarted())
267  {
268  //save session data in cookies
269  $kernelSession->getSessionHandler()->setResponse($response);
270  $kernelSession->save();
271  }
272  }
273 
274  /**
275  * Terminates application by invoking exit().
276  * It's the right way to finish application.
277  *
278  * @param int $status
279  * @return void
280  */
281  public function terminate($status = 0)
282  {
283  global $DB;
284 
285  //old kernel staff
286  \CMain::RunFinalActionsInternal();
287 
288  //Release session
289  session_write_close();
290 
291  $this->getConnectionPool()->useMasterOnly(true);
292 
293  $this->runBackgroundJobs();
294 
295  $this->getConnectionPool()->useMasterOnly(false);
296 
298 
299  //todo: migrate to the d7 connection
300  $DB->Disconnect();
301 
302  exit($status);
303  }
304 
305  /**
306  * Exception handler can be initialized through the Config\Configuration (.settings.php file).
307  *
308  * 'exception_handling' => array(
309  * 'value' => array(
310  * 'debug' => true, // output exception on screen
311  * 'handled_errors_types' => E_ALL & ~E_STRICT & ~E_NOTICE, // catchable error types, printed to log
312  * 'exception_errors_types' => E_ALL & ~E_NOTICE & ~E_STRICT, // error types from catchable which throws exceptions
313  * 'ignore_silence' => false, // ignore @
314  * 'assertion_throws_exception' => true, // assertion throws exception
315  * 'assertion_error_type' => 256,
316  * 'log' => array(
317  * 'class_name' => 'MyLog', // custom log class, must extends ExceptionHandlerLog; can be omited, in this case default Diag\FileExceptionHandlerLog will be used
318  * 'extension' => 'MyLogExt', // php extension, is used only with 'class_name'
319  * 'required_file' => 'modules/mylog.module/mylog.php' // included file, is used only with 'class_name'
320  * 'settings' => array( // any settings for 'class_name'
321  * 'file' => 'bitrix/modules/error.log',
322  * 'log_size' => 1000000,
323  * ),
324  * ),
325  * ),
326  * 'readonly' => false,
327  * ),
328  *
329  */
330  protected function initializeExceptionHandler()
331  {
332  $exceptionHandler = new Diag\ExceptionHandler();
333 
334  $exceptionHandling = Config\Configuration::getValue("exception_handling");
335  if ($exceptionHandling == null)
336  $exceptionHandling = array();
337 
338  if (!isset($exceptionHandling["debug"]) || !is_bool($exceptionHandling["debug"]))
339  $exceptionHandling["debug"] = false;
340  $exceptionHandler->setDebugMode($exceptionHandling["debug"]);
341 
342  if (isset($exceptionHandling["handled_errors_types"]) && is_int($exceptionHandling["handled_errors_types"]))
343  $exceptionHandler->setHandledErrorsTypes($exceptionHandling["handled_errors_types"]);
344 
345  if (isset($exceptionHandling["exception_errors_types"]) && is_int($exceptionHandling["exception_errors_types"]))
346  $exceptionHandler->setExceptionErrorsTypes($exceptionHandling["exception_errors_types"]);
347 
348  if (isset($exceptionHandling["ignore_silence"]) && is_bool($exceptionHandling["ignore_silence"]))
349  $exceptionHandler->setIgnoreSilence($exceptionHandling["ignore_silence"]);
350 
351  if (isset($exceptionHandling["assertion_throws_exception"]) && is_bool($exceptionHandling["assertion_throws_exception"]))
352  $exceptionHandler->setAssertionThrowsException($exceptionHandling["assertion_throws_exception"]);
353 
354  if (isset($exceptionHandling["assertion_error_type"]) && is_int($exceptionHandling["assertion_error_type"]))
355  $exceptionHandler->setAssertionErrorType($exceptionHandling["assertion_error_type"]);
356 
357  $exceptionHandler->initialize(
358  array($this, "createExceptionHandlerOutput"),
359  array($this, "createExceptionHandlerLog")
360  );
361 
362  ServiceLocator::getInstance()->addInstance('exceptionHandler', $exceptionHandler);
363  }
364 
365  public function createExceptionHandlerLog()
366  {
367  $exceptionHandling = Config\Configuration::getValue("exception_handling");
368  if ($exceptionHandling === null || !is_array($exceptionHandling) || !isset($exceptionHandling["log"]) || !is_array($exceptionHandling["log"]))
369  return null;
370 
371  $options = $exceptionHandling["log"];
372 
373  $log = null;
374 
375  if (isset($options["class_name"]) && !empty($options["class_name"]))
376  {
377  if (isset($options["extension"]) && !empty($options["extension"]) && !extension_loaded($options["extension"]))
378  return null;
379 
380  if (isset($options["required_file"]) && !empty($options["required_file"]) && ($requiredFile = Loader::getLocal($options["required_file"])) !== false)
381  require_once($requiredFile);
382 
383  $className = $options["class_name"];
384  if (!class_exists($className))
385  return null;
386 
387  $log = new $className();
388  }
389  elseif (isset($options["settings"]) && is_array($options["settings"]))
390  {
391  $log = new Diag\FileExceptionHandlerLog();
392  }
393  else
394  {
395  return null;
396  }
397 
398  $log->initialize(
399  isset($options["settings"]) && is_array($options["settings"]) ? $options["settings"] : array()
400  );
401 
402  return $log;
403  }
404 
406  {
407  return new Diag\ExceptionHandlerOutput();
408  }
409 
410  /**
411  * Creates database connection pool.
412  */
413  protected function createDatabaseConnection()
414  {
415  $this->connectionPool = new Data\ConnectionPool();
416  }
417 
418  protected function initializeCache()
419  {
420  //TODO: Should be transfered to where GET parameter is defined in future
421  //magic parameters: show cache usage statistics
422  $show_cache_stat = "";
423  if (isset($_GET["show_cache_stat"]))
424  {
425  $show_cache_stat = (mb_strtoupper($_GET["show_cache_stat"]) == "Y" ? "Y" : "");
426  @setcookie("show_cache_stat", $show_cache_stat, false, "/");
427  }
428  elseif (isset($_COOKIE["show_cache_stat"]))
429  {
430  $show_cache_stat = $_COOKIE["show_cache_stat"];
431  }
432  Data\Cache::setShowCacheStat($show_cache_stat === "Y");
433 
434  if (isset($_GET["clear_cache_session"]))
435  Data\Cache::setClearCacheSession($_GET["clear_cache_session"] === 'Y');
436  if (isset($_GET["clear_cache"]))
437  Data\Cache::setClearCache($_GET["clear_cache"] === 'Y');
438  }
439 
440  /**
441  * @return \Bitrix\Main\Diag\ExceptionHandler
442  */
443  public function getExceptionHandler()
444  {
445  return ServiceLocator::getInstance()->get('exceptionHandler');
446  }
447 
448  /**
449  * Returns database connections pool object.
450  *
451  * @return Data\ConnectionPool
452  */
453  public function getConnectionPool()
454  {
455  return $this->connectionPool;
456  }
457 
458  /**
459  * Returns context of the current request.
460  *
461  * @return Context
462  */
463  public function getContext()
464  {
465  return $this->context;
466  }
467 
468  /**
469  * Modifies context of the current request.
470  *
471  * @param Context $context
472  */
473  public function setContext(Context $context)
474  {
475  $this->context = $context;
476  }
477 
478  /**
479  * Static method returns database connection for the specified name.
480  * If name is empty - default connection is returned.
481  *
482  * @static
483  * @param string $name Name of database connection. If empty - default connection.
484  * @return Data\Connection|DB\Connection
485  */
486  public static function getConnection($name = "")
487  {
488  $pool = Application::getInstance()->getConnectionPool();
489  return $pool->getConnection($name);
490  }
491 
492  /**
493  * Returns new instance of the Cache object.
494  *
495  * @return Data\Cache
496  */
497  public function getCache()
498  {
500  }
501 
502  /**
503  * Returns manager of the managed cache.
504  *
505  * @return Data\ManagedCache
506  */
507  public function getManagedCache()
508  {
509  if ($this->managedCache == null)
510  {
511  $this->managedCache = new Data\ManagedCache();
512  }
513 
514  return $this->managedCache;
515  }
516 
517  /**
518  * Returns manager of the managed cache.
519  *
520  * @return Data\TaggedCache
521  */
522  public function getTaggedCache()
523  {
524  if ($this->taggedCache == null)
525  {
526  $this->taggedCache = new Data\TaggedCache();
527  }
528 
529  return $this->taggedCache;
530  }
531 
532  final public function getSessionLocalStorageManager(): Data\LocalStorage\SessionLocalStorageManager
533  {
535  }
536 
537  final public function getLocalSession($name): Data\LocalStorage\SessionLocalStorage
538  {
539  return $this->sessionLocalStorageManager->get($name);
540  }
541 
542  final public function getKernelSession(): SessionInterface
543  {
544  return $this->kernelSession;
545  }
546 
547  final public function getSession(): SessionInterface
548  {
549  return $this->session;
550  }
551 
553  {
555  }
556 
557  /**
558  * Returns UF manager.
559  *
560  * @return \CUserTypeManager
561  */
562  public static function getUserTypeManager()
563  {
564  global $USER_FIELD_MANAGER;
565  return $USER_FIELD_MANAGER;
566  }
567 
568  /**
569  * Returns true id server is in utf-8 mode. False - otherwise.
570  *
571  * @return bool
572  */
573  public static function isUtfMode()
574  {
575  static $isUtfMode = null;
576  if ($isUtfMode === null)
577  {
578  $isUtfMode = Config\Configuration::getValue("utf_mode");
579  if ($isUtfMode === null)
580  $isUtfMode = false;
581  }
582  return $isUtfMode;
583  }
584 
585  /**
586  * Returns server document root.
587  *
588  * @return null|string
589  */
590  public static function getDocumentRoot()
591  {
592  static $documentRoot = null;
593  if ($documentRoot != null)
594  return $documentRoot;
595 
596  $context = Application::getInstance()->getContext();
597  if ($context != null)
598  {
599  $server = $context->getServer();
600  if ($server != null)
601  return $documentRoot = $server->getDocumentRoot();
602  }
603 
604  return Loader::getDocumentRoot();
605  }
606 
607  /**
608  * Returns personal root directory (relative to document root)
609  *
610  * @return null|string
611  */
612  public static function getPersonalRoot()
613  {
614  static $personalRoot = null;
615  if ($personalRoot != null)
616  return $personalRoot;
617 
618  $context = Application::getInstance()->getContext();
619  if ($context != null)
620  {
621  $server = $context->getServer();
622  if ($server != null)
623  return $personalRoot = $server->getPersonalRoot();
624  }
625 
626  return isset($_SERVER["BX_PERSONAL_ROOT"]) ? $_SERVER["BX_PERSONAL_ROOT"] : "/bitrix";
627  }
628 
629  /**
630  * Resets accelerator if any.
631  */
632  public static function resetAccelerator()
633  {
634  if (defined("BX_NO_ACCELERATOR_RESET"))
635  return;
636 
637  $fl = Config\Configuration::getValue("no_accelerator_reset");
638  if ($fl)
639  return;
640 
641  if (function_exists("accelerator_reset"))
642  accelerator_reset();
643  elseif (function_exists("wincache_refresh_if_changed"))
644  wincache_refresh_if_changed();
645  }
646 
647  /**
648  * Adds a job to do after the response was sent.
649  * @param callable $job
650  * @param array $args
651  * @param int $priority
652  * @return $this
653  */
654  public function addBackgroundJob(callable $job, array $args = [], $priority = self::JOB_PRIORITY_NORMAL)
655  {
656  $this->backgroundJobs->insert([$job, $args], $priority);
657 
658  return $this;
659  }
660 
661  protected function runBackgroundJobs()
662  {
663  $lastException = null;
664  $exceptionHandler = $this->getExceptionHandler();
665 
666  //jobs can be added from jobs
667  while($this->backgroundJobs->valid())
668  {
669  //clear the queue
670  $jobs = [];
671  foreach ($this->backgroundJobs as $job)
672  {
673  $jobs[] = $job;
674  }
675 
676  //do jobs
677  foreach ($jobs as $job)
678  {
679  try
680  {
681  call_user_func_array($job[0], $job[1]);
682  }
683  catch (\Throwable $exception)
684  {
685  $lastException = $exception;
686  $exceptionHandler->writeToLog($exception);
687  }
688  }
689  }
690 
691  if ($lastException !== null)
692  {
693  throw $lastException;
694  }
695  }
696 
697  public function isExtendedKernelInitialized(): bool
698  {
700  }
701 }
Base class for any application.
__construct()
Creates new application instance.
handleResponseBeforeSend(Response $response)
setContext(Context $context)
Modifies context of the current request.
getManagedCache()
Returns manager of the managed cache.
setCurrentRoute(Route $currentRoute)
static getDocumentRoot()
Returns server document root.
initializeExceptionHandler()
Exception handler can be initialized through the Config\Configuration (.settings.php file).
createDatabaseConnection()
Creates database connection pool.
getContext()
Returns context of the current request.
initializeBasicKernel()
Does minimally possible kernel initialization.
addBackgroundJob(callable $job, array $args=[], $priority=self::JOB_PRIORITY_NORMAL)
Adds a job to do after the response was sent.
static getUserTypeManager()
Returns UF manager.
getConnectionPool()
Returns database connections pool object.
terminate($status=0)
Terminates application by invoking exit().
static isUtfMode()
Returns true id server is in utf-8 mode.
getTaggedCache()
Returns manager of the managed cache.
static resetAccelerator()
Resets accelerator if any.
getCache()
Returns new instance of the Cache object.
initializeContext(array $params)
Initializes context of the current request.
static getInstance()
Returns current instance of the Application.
static getPersonalRoot()
Returns personal root directory (relative to document root)
static getConnection($name="")
Static method returns database connection for the specified name.
end($status=0, Response $response=null)
Ends work of application.
initializeExtendedKernel(array $params)
Does full kernel initialization.
start()
Starts request execution.
run()
Runs controller and its action and sends response to the output.
Context of current request.
static createInstance($params=[])
static setClearCacheSession($clearCacheSession)
A privileged user wants to skip cache on this session.
static setClearCache($clearCache)
A privileged user wants to skip cache on this hit.
static setShowCacheStat($showCacheStat)
static createCacheEngine($params=[])
Connection pool is a connections holder.
static getDocumentRoot()
Returns document root.
static getLocal($path, $root=null)
Checks if file exists in /local or /bitrix directories.