Bitrix-D7  20.0.0
main/lib/loader.php
См. документацию.
1 <?php
2 namespace Bitrix\Main;
3 
4 /**
5  * Class Loader loads required files, classes and modules. It is the only class which is included directly.
6  * @package Bitrix\Main
7  */
8 final class Loader
9 {
10  /**
11  * Can be used to prevent loading all modules except main and fileman
12  */
13  const SAFE_MODE = false;
14 
15  const BITRIX_HOLDER = "bitrix";
16  const LOCAL_HOLDER = "local";
17 
18  private static $safeModeModules = array("main", "fileman");
19 
20  private static $arLoadedModules = array("main" => true);
21  private static $arSemiloadedModules = array();
22  private static $arLoadedModulesHolders = array("main" => self::BITRIX_HOLDER);
23  private static $arSharewareModules = array();
24 
25  /** @var array Additional autoload handlers */
26  private static $additionalHandlers = [];
27 
28  /**
29  * Custom autoload paths.
30  * @var array [namespace => path]
31  */
32  private static $customNamespaces = [];
33 
34  /**
35  * Returned by includeSharewareModule() if module is not found
36  */
37  const MODULE_NOT_FOUND = 0;
38  /**
39  * Returned by includeSharewareModule() if module is installed
40  */
41  const MODULE_INSTALLED = 1;
42  /**
43  * Returned by includeSharewareModule() if module works in demo mode
44  */
45  const MODULE_DEMO = 2;
46  /**
47  * Returned by includeSharewareModule() if the trial period is expired
48  */
50 
51  private static $arAutoLoadClasses = array();
52 
53  private static $isAutoLoadOn = true;
54 
55  /**
56  * @var bool Controls throwing exception by requireModule method
57  */
58  private static $requireThrowException = true;
59 
60  const ALPHA_LOWER = "qwertyuioplkjhgfdsazxcvbnm";
61  const ALPHA_UPPER = "QWERTYUIOPLKJHGFDSAZXCVBNM";
62 
63  /**
64  * Includes module by its name
65  *
66  * @param string $moduleName Name of the included module
67  * @return bool Returns true if module was included successfully, otherwise returns false
68  * @throws LoaderException
69  */
70  public static function includeModule($moduleName)
71  {
72  if (!is_string($moduleName) || $moduleName == "")
73  throw new LoaderException("Empty module name");
74  if (preg_match("#[^a-zA-Z0-9._]#", $moduleName))
75  throw new LoaderException(sprintf("Module name '%s' is not correct", $moduleName));
76 
77  $moduleName = strtr($moduleName, static::ALPHA_UPPER, static::ALPHA_LOWER);
78 
79  if (self::SAFE_MODE)
80  {
81  if (!in_array($moduleName, self::$safeModeModules))
82  return false;
83  }
84 
85  if (isset(self::$arLoadedModules[$moduleName]))
86  return self::$arLoadedModules[$moduleName];
87 
88  if (isset(self::$arSemiloadedModules[$moduleName]))
89  trigger_error("Module '".$moduleName."' is in loading progress", E_USER_WARNING);
90 
91  $arInstalledModules = ModuleManager::getInstalledModules();
92  if (!isset($arInstalledModules[$moduleName]))
93  return self::$arLoadedModules[$moduleName] = false;
94 
95  $documentRoot = static::getDocumentRoot();
96 
97  $moduleHolder = self::LOCAL_HOLDER;
98  $pathToInclude = $documentRoot."/".$moduleHolder."/modules/".$moduleName."/include.php";
99  if (!file_exists($pathToInclude))
100  {
101  $moduleHolder = self::BITRIX_HOLDER;
102  $pathToInclude = $documentRoot."/".$moduleHolder."/modules/".$moduleName."/include.php";
103  if (!file_exists($pathToInclude))
104  return self::$arLoadedModules[$moduleName] = false;
105  }
106 
107  self::$arLoadedModulesHolders[$moduleName] = $moduleHolder;
108  self::$arSemiloadedModules[$moduleName] = true;
109 
110  $res = self::includeModuleInternal($pathToInclude);
111 
112  unset(self::$arSemiloadedModules[$moduleName]);
113  if ($res === false)
114  return self::$arLoadedModules[$moduleName] = false;
115 
116  return self::$arLoadedModules[$moduleName] = true;
117  }
118 
119  /**
120  * Includes module by its name, throws an exception in case of failure
121  *
122  * @param $moduleName
123  *
124  * @return bool
125  * @throws LoaderException
126  */
127  public static function requireModule($moduleName)
128  {
129  $included = static::includeModule($moduleName);
130 
131  if (!$included && static::$requireThrowException)
132  {
133  throw new LoaderException("Required module `{$moduleName}` was not found");
134  }
135 
136  return $included;
137  }
138 
139  private static function includeModuleInternal($path)
140  {
141  /** @noinspection PhpUnusedLocalVariableInspection */
142  global $DB, $MESS;
143  return include_once($path);
144  }
145 
146  /**
147  * Includes shareware module by its name.
148  * Module must initialize constant <module name>_DEMO = Y in include.php to define demo mode.
149  * include.php must return false to define trial period expiration.
150  * Constants is used because it is easy to obfuscate them.
151  *
152  * @param string $moduleName Name of the included module
153  * @return int One of the following constant: Loader::MODULE_NOT_FOUND, Loader::MODULE_INSTALLED, Loader::MODULE_DEMO, Loader::MODULE_DEMO_EXPIRED
154  */
155  public static function includeSharewareModule($moduleName)
156  {
157  if (isset(self::$arSharewareModules[$moduleName]))
158  return self::$arSharewareModules[$moduleName];
159 
160  $moduleNameTmp = str_replace(".", "_", $moduleName);
161 
162  if (self::includeModule($moduleName))
163  {
164  if (defined($moduleNameTmp."_DEMO") && constant($moduleNameTmp."_DEMO") == "Y")
165  self::$arSharewareModules[$moduleName] = self::MODULE_DEMO;
166  else
167  self::$arSharewareModules[$moduleName] = self::MODULE_INSTALLED;
168 
169  return self::$arSharewareModules[$moduleName];
170  }
171 
172  if (defined($moduleNameTmp."_DEMO") && constant($moduleNameTmp."_DEMO") == "Y")
173  return self::$arSharewareModules[$moduleName] = self::MODULE_DEMO_EXPIRED;
174 
175  return self::$arSharewareModules[$moduleName] = self::MODULE_NOT_FOUND;
176  }
177 
178  public static function clearModuleCache($moduleName)
179  {
180  if (!is_string($moduleName) || $moduleName == "")
181  throw new LoaderException("Empty module name");
182 
183  if($moduleName !== "main")
184  {
185  unset(static::$arLoadedModules[$moduleName]);
186  unset(static::$arLoadedModulesHolders[$moduleName]);
187  }
188 
189  if (isset(static::$arSharewareModules[$moduleName]))
190  unset(static::$arSharewareModules[$moduleName]);
191  }
192 
193  /**
194  * Returns document root
195  *
196  * @return string Document root
197  */
198  public static function getDocumentRoot()
199  {
200  static $documentRoot = null;
201  if ($documentRoot === null)
202  $documentRoot = rtrim($_SERVER["DOCUMENT_ROOT"], "/\\");
203  return $documentRoot;
204  }
205 
206  public static function switchAutoLoad($value = true)
207  {
208  static::$isAutoLoadOn = $value;
209  }
210 
211  /**
212  * Registers classes for auto loading.
213  * All the frequently used classes should be registered for auto loading (performance).
214  * It is not necessary to register rarely used classes. They can be found and loaded dynamically.
215  *
216  * @param string $moduleName Name of the module. Can be null if classes are not part of any module
217  * @param array $arClasses Array of classes with class names as keys and paths as values.
218  * @throws LoaderException
219  */
220  public static function registerAutoLoadClasses($moduleName, array $arClasses)
221  {
222  if (empty($arClasses))
223  return;
224 
225  if (($moduleName !== null) && empty($moduleName))
226  {
227  throw new LoaderException(sprintf("Module name '%s' is not correct", $moduleName));
228  }
229 
230  if (!static::$isAutoLoadOn)
231  {
232  if (!is_null($moduleName) && !isset(static::$arLoadedModulesHolders[$moduleName]))
233  throw new LoaderException(sprintf("Holder of module '%s' is not found", $moduleName));
234 
235  $documentRoot = static::getDocumentRoot();
236 
237  if (!is_null($moduleName))
238  {
239  foreach ($arClasses as $value)
240  {
241  if (file_exists($documentRoot."/".self::$arLoadedModulesHolders[$moduleName]."/modules/".$moduleName."/".$value))
242  require_once($documentRoot."/".self::$arLoadedModulesHolders[$moduleName]."/modules/".$moduleName."/".$value);
243  }
244  }
245  else
246  {
247  foreach ($arClasses as $value)
248  {
249  if (($includePath = self::getLocal($value, $documentRoot)) !== false)
250  require_once($includePath);
251  }
252  }
253  }
254  else
255  {
256  foreach ($arClasses as $key => $value)
257  {
258  $class = ltrim($key, "\\");
259  self::$arAutoLoadClasses[strtr($class, static::ALPHA_UPPER, static::ALPHA_LOWER)] = array(
260  "module" => $moduleName,
261  "file" => $value
262  );
263  }
264  }
265  }
266 
267  /**
268  * Registers namespaces with custom paths.
269  * e.g. ('Bitrix\Main\Dev', 'main', 'dev/lib')
270  *
271  * @param string $namespace
272  * @param string $module
273  * @param string $path
274  */
275  public static function registerNamespace($namespace, $module, $path)
276  {
277  $namespace = rtrim($namespace, '\\');
278  $path = rtrim($path, '/\\');
279 
280  $path = static::getDocumentRoot()."/".static::$arLoadedModulesHolders[$module]."/modules/".$module.'/'.$path;
281 
282  static::$customNamespaces[$namespace] = $path;
283  }
284 
285  public static function registerHandler(callable $handler)
286  {
287  static::$additionalHandlers[] = $handler;
288  }
289 
290  public static function isAutoLoadClassRegistered($className)
291  {
292  $className = trim(ltrim($className, "\\"));
293  if ($className == '')
294  return false;
295 
296  $className = strtr($className, static::ALPHA_UPPER, static::ALPHA_LOWER);
297 
298  return isset(self::$arAutoLoadClasses[$className]);
299  }
300 
301  /**
302  * \Bitrix\Main\IO\File -> /main/lib/io/file.php
303  * \Bitrix\IBlock\Type -> /iblock/lib/type.php
304  * \Bitrix\IBlock\Section\Type -> /iblock/lib/section/type.php
305  * \QSoft\Catalog\Tools\File -> /qsoft.catalog/lib/tools/file.php
306  *
307  * @param $className
308  */
309  public static function autoLoad($className)
310  {
311  $file = ltrim($className, "\\"); // fix web env
312  $file = strtr($file, static::ALPHA_UPPER, static::ALPHA_LOWER);
313 
314  static $documentRoot = null;
315  if ($documentRoot === null)
316  $documentRoot = static::getDocumentRoot();
317 
318  if (isset(self::$arAutoLoadClasses[$file]))
319  {
320  $pathInfo = self::$arAutoLoadClasses[$file];
321  if ($pathInfo["module"] != "")
322  {
323  $m = $pathInfo["module"];
324  $h = isset(self::$arLoadedModulesHolders[$m]) ? self::$arLoadedModulesHolders[$m] : 'bitrix';
325  include_once($documentRoot."/".$h."/modules/".$m."/" .$pathInfo["file"]);
326  }
327  else
328  {
329  require_once($documentRoot.$pathInfo["file"]);
330  }
331 
332  if (class_exists($className))
333  {
334  return;
335  }
336  }
337 
338  if (preg_match("#[^\\\\/a-zA-Z0-9_]#", $file))
339  return;
340 
341  $tryFiles = [$file];
342 
343  if (substr($file, -5) == "table")
344  {
345  // old *Table stored in reserved files
346  $tryFiles[] = substr($file, 0, -5);
347  }
348 
349  foreach ($tryFiles as $file)
350  {
351  $file = str_replace('\\', '/', $file);
352  $arFile = explode("/", $file);
353 
354  if ($arFile[0] === "bitrix")
355  {
356  array_shift($arFile);
357 
358  if (empty($arFile))
359  break;
360 
361  $module = array_shift($arFile);
362  if ($module == null || empty($arFile))
363  break;
364  }
365  else
366  {
367  $module1 = array_shift($arFile);
368  $module2 = array_shift($arFile);
369 
370  if ($module1 == null || $module2 == null || empty($arFile))
371  {
372  break;
373  }
374 
375  $module = $module1.".".$module2;
376  }
377 
378  if (!isset(self::$arLoadedModulesHolders[$module]))
379  break;
380 
381  $filePath = $documentRoot."/".self::$arLoadedModulesHolders[$module]."/modules/".$module."/lib/".implode("/", $arFile).".php";
382 
383  if (file_exists($filePath))
384  {
385  require_once $filePath;
386  break;
387  }
388  else
389  {
390  // try namespaces with custom path
391  foreach (static::$customNamespaces as $namespace => $namespacePath)
392  {
393  if (strpos($className, $namespace) === 0)
394  {
395  // found
396  $fileParts = explode("/", $file);
397 
398  // cut base namespace
399  for ($i=0; $i <= substr_count($namespace, '\\'); $i++)
400  {
401  array_shift($fileParts);
402  }
403 
404  // final path
405  $filePath = $namespacePath.'/'.implode("/", $fileParts).".php";
406 
407  if (file_exists($filePath))
408  {
409  require_once $filePath;
410  break 2;
411  }
412  }
413  }
414  }
415  }
416 
417  // still not found, check additional handlers
418  if (!class_exists($className) && !empty(static::$additionalHandlers))
419  {
420  foreach (static::$additionalHandlers as $handler)
421  {
422  call_user_func($handler, $className);
423 
424  if (class_exists($className))
425  {
426  break;
427  }
428  }
429  }
430  }
431 
432  /**
433  * Checks if file exists in /local or /bitrix directories
434  *
435  * @param string $path File path relative to /local/ or /bitrix/
436  * @param string $root Server document root, default static::getDocumentRoot()
437  * @return string|bool Returns combined path or false if the file does not exist in both dirs
438  */
439  public static function getLocal($path, $root = null)
440  {
441  if ($root === null)
442  $root = static::getDocumentRoot();
443 
444  if (file_exists($root."/local/".$path))
445  return $root."/local/".$path;
446  elseif (file_exists($root."/bitrix/".$path))
447  return $root."/bitrix/".$path;
448  else
449  return false;
450  }
451 
452  /**
453  * Checks if file exists in personal directory.
454  * If $_SERVER["BX_PERSONAL_ROOT"] is not set than personal directory is equal to /bitrix/
455  *
456  * @param string $path File path relative to personal directory
457  * @return string|bool Returns combined path or false if the file does not exist
458  */
459  public static function getPersonal($path)
460  {
461  $root = static::getDocumentRoot();
462  $personal = isset($_SERVER["BX_PERSONAL_ROOT"]) ? $_SERVER["BX_PERSONAL_ROOT"] : "";
463 
464  if (!empty($personal) && file_exists($root.$personal."/".$path))
465  return $root.$personal."/".$path;
466 
467  return self::getLocal($path, $root);
468  }
469 
470  /**
471  * Changes requireModule behavior
472  *
473  * @param bool $requireThrowException
474  */
475  public static function setRequireThrowException($requireThrowException)
476  {
477  self::$requireThrowException = (bool) $requireThrowException;
478  }
479 }
480 
481 class LoaderException extends \Exception
482 {
483  public function __construct($message = "", $code = 0, \Exception $previous = null)
484  {
485  parent::__construct($message, $code, $previous);
486  }
487 }
488 
489 if (!function_exists("__autoload"))
490 {
491  if (function_exists('spl_autoload_register'))
492  {
493  \spl_autoload_register([Loader::class, 'autoLoad']);
494  }
495  else
496  {
497  function __autoload($className)
498  {
499  Loader::autoLoad($className);
500  }
501  }
502 
504 }
505 else
506 {
507  Loader::switchAutoLoad(false);
508 }
Bitrix\Main\Loader\switchAutoLoad
static switchAutoLoad($value=true)
Definition: main/lib/loader.php:206
Bitrix\Main\Loader\MODULE_NOT_FOUND
const MODULE_NOT_FOUND
Returned by includeSharewareModule() if module is not found.
Definition: main/lib/loader.php:37
Bitrix\Main\Loader\requireModule
static requireModule($moduleName)
Includes module by its name, throws an exception in case of failure.
Definition: main/lib/loader.php:127
Bitrix\Main\LoaderException
Definition: main/lib/loader.php:481
Bitrix\Main\Loader\registerAutoLoadClasses
static registerAutoLoadClasses($moduleName, array $arClasses)
Registers classes for auto loading.
Definition: main/lib/loader.php:220
Bitrix\Main\Loader\ALPHA_LOWER
const ALPHA_LOWER
Definition: main/lib/loader.php:60
Bitrix\Main\Loader\MODULE_INSTALLED
const MODULE_INSTALLED
Returned by includeSharewareModule() if module is installed.
Definition: main/lib/loader.php:41
Bitrix\Main\Loader\MODULE_DEMO
const MODULE_DEMO
Returned by includeSharewareModule() if module works in demo mode.
Definition: main/lib/loader.php:45
Bitrix\Main\Loader\clearModuleCache
static clearModuleCache($moduleName)
Definition: main/lib/loader.php:178
Bitrix\Main\Loader\getPersonal
static getPersonal($path)
Checks if file exists in personal directory.
Definition: main/lib/loader.php:459
Bitrix\Main
Bitrix\Main\ModuleManager\getInstalledModules
static getInstalledModules()
Definition: modulemanager.php:8
Bitrix\Main\Loader\setRequireThrowException
static setRequireThrowException($requireThrowException)
Changes requireModule behavior.
Definition: main/lib/loader.php:475
Bitrix\Main\Loader\autoLoad
static autoLoad($className)
\Bitrix\Main\IO\File -> /main/lib/io/file.php \Bitrix\IBlock\Type -> /iblock/lib/type....
Definition: main/lib/loader.php:309
Bitrix\Main\Loader\includeSharewareModule
static includeSharewareModule($moduleName)
Includes shareware module by its name.
Definition: main/lib/loader.php:155
Bitrix\Main\Loader\MODULE_DEMO_EXPIRED
const MODULE_DEMO_EXPIRED
Returned by includeSharewareModule() if the trial period is expired.
Definition: main/lib/loader.php:49
Bitrix\Main\Loader\isAutoLoadClassRegistered
static isAutoLoadClassRegistered($className)
Definition: main/lib/loader.php:290
Bitrix\Sender\Connector\__construct
__construct(Base $connector)
Constructor.
Definition: resultview.php:40
Bitrix\Main\Loader\ALPHA_UPPER
const ALPHA_UPPER
Definition: main/lib/loader.php:61
Bitrix\Main\LoaderException\__construct
__construct($message="", $code=0, \Exception $previous=null)
Definition: main/lib/loader.php:483
Bitrix\Main\Loader\LOCAL_HOLDER
const LOCAL_HOLDER
Definition: main/lib/loader.php:16
Bitrix\Main\Loader\registerHandler
static registerHandler(callable $handler)
Definition: main/lib/loader.php:285
Bitrix\Main\Loader\getDocumentRoot
static getDocumentRoot()
Returns document root.
Definition: main/lib/loader.php:198
Bitrix\Main\Loader\getLocal
static getLocal($path, $root=null)
Checks if file exists in /local or /bitrix directories.
Definition: main/lib/loader.php:439
Bitrix\Main\Loader\BITRIX_HOLDER
const BITRIX_HOLDER
Definition: main/lib/loader.php:15
Bitrix\Main\Loader
Definition: main/lib/loader.php:8
Bitrix\Main\Loader\registerNamespace
static registerNamespace($namespace, $module, $path)
Registers namespaces with custom paths.
Definition: main/lib/loader.php:275
Bitrix\Main\Loader\SAFE_MODE
const SAFE_MODE
Can be used to prevent loading all modules except main and fileman.
Definition: main/lib/loader.php:13
Bitrix\Main\Loader\includeModule
static includeModule($moduleName)
Includes module by its name.
Definition: main/lib/loader.php:70