vendor/contao/core-bundle/src/Resources/contao/library/Contao/Environment.php line 60

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of Contao.
  4. *
  5. * (c) Leo Feyer
  6. *
  7. * @license LGPL-3.0-or-later
  8. */
  9. namespace Contao;
  10. /**
  11. * Reads the environment variables
  12. *
  13. * The class returns the environment variables (which are stored in the PHP
  14. * $_SERVER array) independent of the operating system.
  15. *
  16. * Usage:
  17. *
  18. * echo Environment::get('scriptName');
  19. * echo Environment::get('requestUri');
  20. */
  21. class Environment
  22. {
  23. /**
  24. * Object instance (Singleton)
  25. * @var Environment
  26. */
  27. protected static $objInstance;
  28. /**
  29. * The SAPI name
  30. * @var string
  31. */
  32. protected static $strSapi = \PHP_SAPI;
  33. /**
  34. * Cache
  35. * @var array
  36. */
  37. protected static $arrCache = array();
  38. /**
  39. * Return an environment variable
  40. *
  41. * @param string $strKey The variable name
  42. *
  43. * @return mixed The variable value
  44. */
  45. public static function get($strKey)
  46. {
  47. if (isset(static::$arrCache[$strKey]))
  48. {
  49. return static::$arrCache[$strKey];
  50. }
  51. if (\in_array($strKey, get_class_methods(self::class)))
  52. {
  53. static::$arrCache[$strKey] = static::$strKey();
  54. }
  55. else
  56. {
  57. $arrChunks = preg_split('/([A-Z][a-z]*)/', $strKey, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
  58. $strServerKey = strtoupper(implode('_', $arrChunks));
  59. static::$arrCache[$strKey] = $_SERVER[$strServerKey] ?? null;
  60. }
  61. return static::$arrCache[$strKey];
  62. }
  63. /**
  64. * Set an environment variable
  65. *
  66. * @param string $strKey The variable name
  67. * @param mixed $varValue The variable value
  68. */
  69. public static function set($strKey, $varValue)
  70. {
  71. static::$arrCache[$strKey] = $varValue;
  72. }
  73. /**
  74. * Reset the internal cache
  75. */
  76. public static function reset()
  77. {
  78. static::$arrCache = array();
  79. }
  80. /**
  81. * Return the absolute path to the script (e.g. /home/www/html/website/index.php)
  82. *
  83. * @return string The absolute path to the script
  84. */
  85. protected static function scriptFilename()
  86. {
  87. return str_replace('//', '/', strtr((static::$strSapi == 'cgi' || static::$strSapi == 'isapi' || static::$strSapi == 'cgi-fcgi' || static::$strSapi == 'fpm-fcgi') && ($_SERVER['ORIG_PATH_TRANSLATED'] ?? $_SERVER['PATH_TRANSLATED'] ?? null) ? ($_SERVER['ORIG_PATH_TRANSLATED'] ?? $_SERVER['PATH_TRANSLATED']) : ($_SERVER['ORIG_SCRIPT_FILENAME'] ?? $_SERVER['SCRIPT_FILENAME'] ?? ''), '\\', '/'));
  88. }
  89. /**
  90. * Return the relative path to the script (e.g. /website/index.php)
  91. *
  92. * @return string The relative path to the script
  93. */
  94. protected static function scriptName()
  95. {
  96. $request = System::getContainer()->get('request_stack')->getCurrentRequest();
  97. if ($request === null)
  98. {
  99. return $_SERVER['ORIG_SCRIPT_NAME'] ?? $_SERVER['SCRIPT_NAME'];
  100. }
  101. return $request->getScriptName();
  102. }
  103. /**
  104. * Alias for scriptName()
  105. *
  106. * @return string The script name
  107. */
  108. protected static function phpSelf()
  109. {
  110. return static::scriptName();
  111. }
  112. /**
  113. * Return the document root (e.g. /home/www/user/)
  114. *
  115. * Calculated as SCRIPT_FILENAME minus SCRIPT_NAME as some CGI versions
  116. * and mod-rewrite rules might return an incorrect DOCUMENT_ROOT.
  117. *
  118. * @return string The document root
  119. */
  120. protected static function documentRoot()
  121. {
  122. $strDocumentRoot = '';
  123. $arrUriSegments = array();
  124. $scriptName = static::get('scriptName');
  125. $scriptFilename = static::get('scriptFilename');
  126. // Fallback to DOCUMENT_ROOT if SCRIPT_FILENAME and SCRIPT_NAME point to different files
  127. if (basename($scriptName) != basename($scriptFilename))
  128. {
  129. return str_replace('//', '/', strtr(realpath($_SERVER['DOCUMENT_ROOT']), '\\', '/'));
  130. }
  131. if (0 === strncmp($scriptFilename, '/', 1))
  132. {
  133. $strDocumentRoot = '/';
  134. }
  135. $arrSnSegments = explode('/', strrev($scriptName));
  136. $arrSfnSegments = explode('/', strrev($scriptFilename));
  137. foreach ($arrSfnSegments as $k=>$v)
  138. {
  139. if (@$arrSnSegments[$k] != $v)
  140. {
  141. $arrUriSegments[] = $v;
  142. }
  143. }
  144. $strDocumentRoot .= strrev(implode('/', $arrUriSegments));
  145. if (\strlen($strDocumentRoot) < 2)
  146. {
  147. $strDocumentRoot = substr($scriptFilename, 0, -(\strlen($strDocumentRoot) + 1));
  148. }
  149. return str_replace('//', '/', strtr(realpath($strDocumentRoot), '\\', '/'));
  150. }
  151. /**
  152. * Return the query string (e.g. id=2)
  153. *
  154. * @return string The query string
  155. */
  156. protected static function queryString()
  157. {
  158. if (!isset($_SERVER['QUERY_STRING']))
  159. {
  160. return '';
  161. }
  162. return static::encodeRequestString($_SERVER['QUERY_STRING']);
  163. }
  164. /**
  165. * Return the request URI [path]?[query] (e.g. /contao/index.php?id=2)
  166. *
  167. * @return string The request URI
  168. */
  169. protected static function requestUri()
  170. {
  171. if (!empty($_SERVER['REQUEST_URI']))
  172. {
  173. $arrComponents = parse_url($_SERVER['REQUEST_URI']);
  174. if ($arrComponents === false)
  175. {
  176. $strRequest = $_SERVER['REQUEST_URI'];
  177. }
  178. else
  179. {
  180. $strRequest = ($arrComponents['path'] ?? '') . (isset($arrComponents['query']) ? '?' . $arrComponents['query'] : '');
  181. }
  182. }
  183. else
  184. {
  185. $strRequest = '/' . preg_replace('/^\//', '', static::get('scriptName')) . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '');
  186. }
  187. return static::encodeRequestString($strRequest);
  188. }
  189. /**
  190. * Return the first eight accepted languages as array
  191. *
  192. * @return array The languages array
  193. */
  194. protected static function httpAcceptLanguage()
  195. {
  196. $arrAccepted = array();
  197. $arrLanguages = array();
  198. // The implementation differs from the original implementation and also works with .jp browsers
  199. preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '', $arrAccepted);
  200. // Remove all invalid locales
  201. foreach ($arrAccepted[1] as $v)
  202. {
  203. $chunks = explode('-', $v);
  204. // Language plus dialect, e.g. en-US or fr-FR
  205. if (isset($chunks[1]))
  206. {
  207. $locale = $chunks[0] . '-' . strtoupper($chunks[1]);
  208. if (preg_match('/^[a-z]{2}(-[A-Z]{2})?$/', $locale))
  209. {
  210. $arrLanguages[] = $locale;
  211. }
  212. }
  213. $locale = $chunks[0];
  214. // Language only, e.g. en or fr (see #29)
  215. if (preg_match('/^[a-z]{2}$/', $locale))
  216. {
  217. $arrLanguages[] = $locale;
  218. }
  219. }
  220. return \array_slice(array_unique($arrLanguages), 0, 8);
  221. }
  222. /**
  223. * Return accepted encoding types as array
  224. *
  225. * @return array The encoding types array
  226. */
  227. protected static function httpAcceptEncoding()
  228. {
  229. return array_values(array_unique(explode(',', strtolower($_SERVER['HTTP_ACCEPT_ENCODING']))));
  230. }
  231. /**
  232. * Return the user agent as string
  233. *
  234. * @return string The user agent string
  235. */
  236. protected static function httpUserAgent()
  237. {
  238. if (!isset($_SERVER['HTTP_USER_AGENT']))
  239. {
  240. return '';
  241. }
  242. $ua = strip_tags($_SERVER['HTTP_USER_AGENT']);
  243. $ua = preg_replace('/javascript|vbscri?pt|script|applet|alert|document|write|cookie/i', '', $ua);
  244. return substr($ua, 0, 255);
  245. }
  246. /**
  247. * Return the HTTP Host
  248. *
  249. * @return string The host name
  250. */
  251. protected static function httpHost()
  252. {
  253. if (!empty($_SERVER['HTTP_HOST']))
  254. {
  255. $host = $_SERVER['HTTP_HOST'];
  256. }
  257. else
  258. {
  259. $host = $_SERVER['SERVER_NAME'] ?? null;
  260. if (($_SERVER['SERVER_PORT'] ?? 80) != 80)
  261. {
  262. $host .= ':' . $_SERVER['SERVER_PORT'];
  263. }
  264. }
  265. return preg_replace('/[^A-Za-z0-9[\].:_-]/', '', $host);
  266. }
  267. /**
  268. * Return the HTTP X-Forwarded-Host
  269. *
  270. * @return string The name of the X-Forwarded-Host
  271. */
  272. protected static function httpXForwardedHost()
  273. {
  274. if (!isset($_SERVER['HTTP_X_FORWARDED_HOST']))
  275. {
  276. return '';
  277. }
  278. return preg_replace('/[^A-Za-z0-9[\].:-]/', '', $_SERVER['HTTP_X_FORWARDED_HOST']);
  279. }
  280. /**
  281. * Return true if the current page was requested via an SSL connection
  282. *
  283. * @return boolean True if SSL is enabled
  284. */
  285. protected static function ssl()
  286. {
  287. $request = System::getContainer()->get('request_stack')->getCurrentRequest();
  288. if ($request === null)
  289. {
  290. return false;
  291. }
  292. return $request->isSecure();
  293. }
  294. /**
  295. * Return the current URL without path or query string
  296. *
  297. * @return string The URL
  298. */
  299. protected static function url()
  300. {
  301. return (static::get('ssl') ? 'https://' : 'http://') . static::get('httpHost');
  302. }
  303. /**
  304. * Return the current URL with path or query string
  305. *
  306. * @return string The URL
  307. */
  308. protected static function uri()
  309. {
  310. return static::get('url') . static::get('requestUri');
  311. }
  312. /**
  313. * Return the real REMOTE_ADDR even if a proxy server is used
  314. *
  315. * @return string The IP address of the client
  316. */
  317. protected static function ip()
  318. {
  319. $request = System::getContainer()->get('request_stack')->getCurrentRequest();
  320. if ($request === null)
  321. {
  322. return '';
  323. }
  324. return $request->getClientIp();
  325. }
  326. /**
  327. * Return the SERVER_ADDR
  328. *
  329. * @return string The IP address of the server
  330. */
  331. protected static function server()
  332. {
  333. $strServer = !empty($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : $_SERVER['LOCAL_ADDR'];
  334. // Special workaround for Strato users
  335. if (empty($strServer))
  336. {
  337. $strServer = @gethostbyname($_SERVER['SERVER_NAME']);
  338. }
  339. return $strServer;
  340. }
  341. /**
  342. * Return the relative path to the base directory (e.g. /path)
  343. *
  344. * @return string The relative path to the installation
  345. */
  346. protected static function path()
  347. {
  348. $request = System::getContainer()->get('request_stack')->getCurrentRequest();
  349. if ($request === null)
  350. {
  351. return '';
  352. }
  353. return $request->getBasePath();
  354. }
  355. /**
  356. * Return the relative path to the script (e.g. index.php)
  357. *
  358. * @return string The relative path to the script
  359. */
  360. protected static function script()
  361. {
  362. return preg_replace('/^' . preg_quote(static::get('path'), '/') . '\/?/', '', static::get('scriptName'));
  363. }
  364. /**
  365. * Return the relative path to the script and include the request (e.g. index.php?id=2)
  366. *
  367. * @return string The relative path to the script including the request string
  368. */
  369. protected static function request()
  370. {
  371. return preg_replace('/^' . preg_quote(static::get('path'), '/') . '\/?/', '', static::get('requestUri'));
  372. }
  373. /**
  374. * Return the request string without the script name (e.g. en/news.html)
  375. *
  376. * @return string The base URL
  377. */
  378. protected static function relativeRequest()
  379. {
  380. return preg_replace('/^' . preg_quote(static::get('script'), '/') . '\/?/', '', static::get('request'));
  381. }
  382. /**
  383. * Return the request string without the index.php fragment
  384. *
  385. * @return string The request string without the index.php fragment
  386. */
  387. protected static function indexFreeRequest()
  388. {
  389. $strRequest = static::get('request');
  390. if ($strRequest == static::get('script'))
  391. {
  392. return '';
  393. }
  394. return $strRequest;
  395. }
  396. /**
  397. * Return the URL and path that can be used in a <base> tag
  398. *
  399. * @return string The base URL
  400. */
  401. protected static function base()
  402. {
  403. return static::get('url') . static::get('path') . '/';
  404. }
  405. /**
  406. * Return the host name
  407. *
  408. * @return string The host name
  409. */
  410. protected static function host()
  411. {
  412. return preg_replace('/:\d+$/', '', static::get('httpHost'));
  413. }
  414. /**
  415. * Return true on Ajax requests
  416. *
  417. * @return boolean True if it is an Ajax request
  418. */
  419. protected static function isAjaxRequest()
  420. {
  421. if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']))
  422. {
  423. return false;
  424. }
  425. return $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
  426. }
  427. /**
  428. * Return the operating system and the browser name and version
  429. *
  430. * @return object The agent information
  431. *
  432. * @deprecated Deprecated since Contao 4.13, to be removed in Contao 5.0.
  433. */
  434. protected static function agent()
  435. {
  436. trigger_deprecation('contao/core-bundle', '4.13', 'Using "%s::get(\'agent\')" has been deprecated and will no longer work in Contao 5.0.', __CLASS__);
  437. $ua = static::get('httpUserAgent');
  438. $return = new \stdClass();
  439. $return->string = $ua;
  440. $os = 'unknown';
  441. $mobile = false;
  442. $browser = 'other';
  443. $shorty = '';
  444. $version = '';
  445. $engine = '';
  446. // Operating system
  447. foreach (Config::get('os') as $k=>$v)
  448. {
  449. if (stripos($ua, $k) !== false)
  450. {
  451. $os = $v['os'];
  452. $mobile = $v['mobile'];
  453. break;
  454. }
  455. }
  456. $return->os = $os;
  457. // Browser and version
  458. foreach (Config::get('browser') as $k=>$v)
  459. {
  460. if (stripos($ua, $k) !== false)
  461. {
  462. $browser = $v['browser'];
  463. $shorty = $v['shorty'];
  464. $version = preg_replace($v['version'], '$1', $ua);
  465. $engine = $v['engine'];
  466. break;
  467. }
  468. }
  469. $versions = explode('.', $version);
  470. $version = $versions[0];
  471. $return->class = $os . ' ' . $browser . ' ' . $engine;
  472. // Add the version number if available
  473. if ($version)
  474. {
  475. $return->class .= ' ' . $shorty . $version;
  476. }
  477. // Android tablets are not mobile (see #4150 and #5869)
  478. if ($os == 'android' && $engine != 'presto' && stripos($ua, 'mobile') === false)
  479. {
  480. $mobile = false;
  481. }
  482. // Mark mobile devices
  483. if ($mobile)
  484. {
  485. $return->class .= ' mobile';
  486. }
  487. $return->browser = $browser;
  488. $return->shorty = $shorty;
  489. $return->version = $version;
  490. $return->engine = $engine;
  491. $return->versions = $versions;
  492. $return->mobile = $mobile;
  493. return $return;
  494. }
  495. /**
  496. * Encode a request string preserving certain reserved characters
  497. *
  498. * @param string $strRequest The request string
  499. *
  500. * @return string The encoded request string
  501. */
  502. protected static function encodeRequestString($strRequest)
  503. {
  504. return preg_replace_callback('/[^A-Za-z0-9\-_.~&=+,\/?%\[\]]+/', static function ($matches) { return rawurlencode($matches[0]); }, $strRequest);
  505. }
  506. /**
  507. * Prevent direct instantiation (Singleton)
  508. *
  509. * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  510. * The Environment class is now static.
  511. */
  512. protected function __construct()
  513. {
  514. }
  515. /**
  516. * Prevent cloning of the object (Singleton)
  517. *
  518. * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  519. * The Environment class is now static.
  520. */
  521. final public function __clone()
  522. {
  523. }
  524. /**
  525. * Return an environment variable
  526. *
  527. * @param string $strKey The variable name
  528. *
  529. * @return string The variable value
  530. *
  531. * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  532. * Use Environment::get() instead.
  533. */
  534. public function __get($strKey)
  535. {
  536. return static::get($strKey);
  537. }
  538. /**
  539. * Set an environment variable
  540. *
  541. * @param string $strKey The variable name
  542. * @param mixed $varValue The variable value
  543. *
  544. * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  545. * Use Environment::set() instead.
  546. */
  547. public function __set($strKey, $varValue)
  548. {
  549. static::set($strKey, $varValue);
  550. }
  551. /**
  552. * Return the object instance (Singleton)
  553. *
  554. * @return Environment The object instance
  555. *
  556. * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  557. * The Environment class is now static.
  558. */
  559. public static function getInstance()
  560. {
  561. trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Environment::getInstance()" has been deprecated and will no longer work in Contao 5.0. The "Contao\Environment" class is now static.');
  562. if (static::$objInstance === null)
  563. {
  564. static::$objInstance = new static();
  565. }
  566. return static::$objInstance;
  567. }
  568. }
  569. class_alias(Environment::class, 'Environment');