vendor/pimcore/pimcore/lib/Tool.php line 179

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore;
  15. use GuzzleHttp\RequestOptions;
  16. use Pimcore\Http\RequestHelper;
  17. use Pimcore\Localization\LocaleServiceInterface;
  18. use Pimcore\Model\Element;
  19. use Symfony\Component\HttpFoundation\Request;
  20. final class Tool
  21. {
  22.     /**
  23.      * Sets the current request to use when resolving request at early
  24.      * stages (before container is loaded)
  25.      *
  26.      * @var Request
  27.      */
  28.     private static $currentRequest;
  29.     /**
  30.      * @var array
  31.      */
  32.     protected static $notFoundClassNames = [];
  33.     /**
  34.      * @var array
  35.      */
  36.     protected static $validLanguages = [];
  37.     /**
  38.      * @var null
  39.      */
  40.     protected static $isFrontend null;
  41.     /**
  42.      * Sets the current request to operate on
  43.      *
  44.      * @param Request|null $request
  45.      *
  46.      * @internal
  47.      */
  48.     public static function setCurrentRequest(Request $request null)
  49.     {
  50.         self::$currentRequest $request;
  51.     }
  52.     /**
  53.      * Checks, if the given language is configured in pimcore's system
  54.      * settings at "Localization & Internationalization (i18n/l10n)".
  55.      * Returns true, if the language is valid or no language is
  56.      * configured at all, false otherwise.
  57.      *
  58.      * @static
  59.      *
  60.      * @param  string $language
  61.      *
  62.      * @return bool
  63.      */
  64.     public static function isValidLanguage($language)
  65.     {
  66.         $language = (string) $language// cast to string
  67.         $languages self::getValidLanguages();
  68.         // if not configured, every language is valid
  69.         if (!$languages) {
  70.             return true;
  71.         }
  72.         if (in_array($language$languages)) {
  73.             return true;
  74.         }
  75.         return false;
  76.     }
  77.     /**
  78.      * Returns an array of language codes that configured for this system
  79.      * in pimcore's system settings at "Localization & Internationalization (i18n/l10n)".
  80.      * An empty array is returned if no languages are configured.
  81.      *
  82.      * @static
  83.      *
  84.      * @return string[]
  85.      */
  86.     public static function getValidLanguages()
  87.     {
  88.         if (empty(self::$validLanguages)) {
  89.             $config Config::getSystemConfiguration('general');
  90.             if (empty($config['valid_languages'])) {
  91.                 return [];
  92.             }
  93.             $validLanguages str_replace(' ''', (string)$config['valid_languages']);
  94.             $languages explode(','$validLanguages);
  95.             if (!is_array($languages)) {
  96.                 $languages = [];
  97.             }
  98.             self::$validLanguages $languages;
  99.         }
  100.         return self::$validLanguages;
  101.     }
  102.     /**
  103.      * @internal
  104.      *
  105.      * @param string $language
  106.      *
  107.      * @return array
  108.      */
  109.     public static function getFallbackLanguagesFor($language)
  110.     {
  111.         $languages = [];
  112.         $config Config::getSystemConfiguration('general');
  113.         if (!empty($config['fallback_languages'][$language])) {
  114.             $fallbackLanguages explode(','$config['fallback_languages'][$language]);
  115.             foreach ($fallbackLanguages as $l) {
  116.                 if (self::isValidLanguage($l)) {
  117.                     $languages[] = trim($l);
  118.                 }
  119.             }
  120.         }
  121.         return $languages;
  122.     }
  123.     /**
  124.      * Returns the default language for this system. If no default is set,
  125.      * returns the first language, or null, if no languages are configured
  126.      * at all.
  127.      *
  128.      * @return null|string
  129.      */
  130.     public static function getDefaultLanguage()
  131.     {
  132.         $config Config::getSystemConfiguration('general');
  133.         $defaultLanguage $config['default_language'] ?? null;
  134.         $languages self::getValidLanguages();
  135.         if (!empty($languages) && in_array($defaultLanguage$languages)) {
  136.             return $defaultLanguage;
  137.         } elseif (!empty($languages)) {
  138.             return $languages[0];
  139.         }
  140.         return null;
  141.     }
  142.     /**
  143.      * @return array|mixed
  144.      *
  145.      * @throws \Exception
  146.      */
  147.     public static function getSupportedLocales()
  148.     {
  149.         $localeService \Pimcore::getContainer()->get(LocaleServiceInterface::class);
  150.         $locale $localeService->findLocale();
  151.         $cacheKey 'system_supported_locales_' strtolower((string) $locale);
  152.         if (!$languageOptions Cache::load($cacheKey)) {
  153.             $languages $localeService->getLocaleList();
  154.             $languageOptions = [];
  155.             foreach ($languages as $code) {
  156.                 $translation \Locale::getDisplayLanguage($code$locale);
  157.                 $displayRegion \Locale::getDisplayRegion($code$locale);
  158.                 if ($displayRegion) {
  159.                     $translation .= ' (' $displayRegion ')';
  160.                 }
  161.                 if (!$translation) {
  162.                     $translation $code;
  163.                 }
  164.                 $languageOptions[$code] = $translation;
  165.             }
  166.             asort($languageOptions);
  167.             Cache::save($languageOptions$cacheKey, ['system']);
  168.         }
  169.         return $languageOptions;
  170.     }
  171.     /**
  172.      * @internal
  173.      *
  174.      * @param string $language
  175.      * @param bool $absolutePath
  176.      *
  177.      * @return string
  178.      */
  179.     public static function getLanguageFlagFile($language$absolutePath true)
  180.     {
  181.         $basePath '/bundles/pimcoreadmin/img/flags';
  182.         $iconFsBasePath PIMCORE_WEB_ROOT $basePath;
  183.         if ($absolutePath === true) {
  184.             $basePath PIMCORE_WEB_ROOT $basePath;
  185.         }
  186.         $code strtolower($language);
  187.         $code str_replace('_''-'$code);
  188.         $countryCode null;
  189.         $fallbackLanguageCode null;
  190.         $parts explode('-'$code);
  191.         if (count($parts) > 1) {
  192.             $countryCode array_pop($parts);
  193.             $fallbackLanguageCode $parts[0];
  194.         }
  195.         $languageFsPath $iconFsBasePath '/languages/' $code '.svg';
  196.         $countryFsPath $iconFsBasePath '/countries/' $countryCode '.svg';
  197.         $fallbackFsLanguagePath $iconFsBasePath '/languages/' $fallbackLanguageCode '.svg';
  198.         $iconPath = ($absolutePath === true $iconFsBasePath $basePath) . '/countries/_unknown.svg';
  199.         $languageCountryMapping = [
  200.             'aa' => 'er''af' => 'za''am' => 'et''as' => 'in''ast' => 'es''asa' => 'tz',
  201.             'az' => 'az''bas' => 'cm''eu' => 'es''be' => 'by''bem' => 'zm''bez' => 'tz''bg' => 'bg',
  202.             'bm' => 'ml''bn' => 'bd''br' => 'fr''brx' => 'in''bs' => 'ba''cs' => 'cz''da' => 'dk',
  203.             'de' => 'de''dz' => 'bt''el' => 'gr''en' => 'gb''es' => 'es''et' => 'ee''fi' => 'fi',
  204.             'fo' => 'fo''fr' => 'fr''ga' => 'ie''gv' => 'im''he' => 'il''hi' => 'in''hr' => 'hr',
  205.             'hu' => 'hu''hy' => 'am''id' => 'id''ig' => 'ng''is' => 'is''it' => 'it''ja' => 'jp',
  206.             'ka' => 'ge''os' => 'ge''kea' => 'cv''kk' => 'kz''kl' => 'gl''km' => 'kh''ko' => 'kr',
  207.             'lg' => 'ug''lo' => 'la''lt' => 'lt''mg' => 'mg''mk' => 'mk''mn' => 'mn''ms' => 'my',
  208.             'mt' => 'mt''my' => 'mm''nb' => 'no''ne' => 'np''nl' => 'nl''nn' => 'no''pl' => 'pl',
  209.             'pt' => 'pt''ro' => 'ro''ru' => 'ru''sg' => 'cf''sk' => 'sk''sl' => 'si''sq' => 'al',
  210.             'sr' => 'rs''sv' => 'se''swc' => 'cd''th' => 'th''to' => 'to''tr' => 'tr''tzm' => 'ma',
  211.             'uk' => 'ua''uz' => 'uz''vi' => 'vn''zh' => 'cn''gd' => 'gb-sct''gd-gb' => 'gb-sct',
  212.             'cy' => 'gb-wls''cy-gb' => 'gb-wls''fy' => 'nl''xh' => 'za''yo' => 'bj''zu' => 'za',
  213.             'ta' => 'lk''te' => 'in''ss' => 'za''sw' => 'ke''so' => 'so''si' => 'lk''ii' => 'cn',
  214.             'zh-hans' => 'cn',  'zh-hant' => 'cn''sn' => 'zw''rm' => 'ch''pa' => 'in''fa' => 'ir''lv' => 'lv''gl' => 'es',
  215.             'fil' => 'ph',
  216.         ];
  217.         if (array_key_exists($code$languageCountryMapping)) {
  218.             $iconPath $basePath '/countries/' $languageCountryMapping[$code] . '.svg';
  219.         } elseif (file_exists($languageFsPath)) {
  220.             $iconPath $basePath '/languages/' $code '.svg';
  221.         } elseif ($countryCode && file_exists($countryFsPath)) {
  222.             $iconPath $basePath '/countries/' $countryCode '.svg';
  223.         } elseif ($fallbackLanguageCode && file_exists($fallbackFsLanguagePath)) {
  224.             $iconPath $basePath '/languages/' $fallbackLanguageCode '.svg';
  225.         }
  226.         return $iconPath;
  227.     }
  228.     /**
  229.      * @param Request|null $request
  230.      *
  231.      * @return null|Request
  232.      */
  233.     private static function resolveRequest(Request $request null)
  234.     {
  235.         if (null === $request) {
  236.             // do an extra check for the container as we might be in a state where no container is set yet
  237.             if (\Pimcore::hasContainer()) {
  238.                 $request \Pimcore::getContainer()->get('request_stack')->getMainRequest();
  239.             } else {
  240.                 if (null !== self::$currentRequest) {
  241.                     return self::$currentRequest;
  242.                 }
  243.             }
  244.         }
  245.         return $request;
  246.     }
  247.     /**
  248.      * @param Request|null $request
  249.      *
  250.      * @return bool
  251.      */
  252.     public static function isFrontend(Request $request null): bool
  253.     {
  254.         if (null === $request) {
  255.             $request \Pimcore::getContainer()->get('request_stack')->getMainRequest();
  256.         }
  257.         if (null === $request) {
  258.             return false;
  259.         }
  260.         return \Pimcore::getContainer()
  261.             ->get(RequestHelper::class)
  262.             ->isFrontendRequest($request);
  263.     }
  264.     /**
  265.      * eg. editmode, preview, version preview, always when it is a "frontend-request", but called out of the admin
  266.      *
  267.      * @param Request|null $request
  268.      *
  269.      * @return bool
  270.      */
  271.     public static function isFrontendRequestByAdmin(Request $request null)
  272.     {
  273.         $request self::resolveRequest($request);
  274.         if (null === $request) {
  275.             return false;
  276.         }
  277.         return \Pimcore::getContainer()
  278.             ->get(RequestHelper::class)
  279.             ->isFrontendRequestByAdmin($request);
  280.     }
  281.     /**
  282.      * Verify element request (eg. editmode, preview, version preview) called within admin, with permissions.
  283.      *
  284.      * @param Request $request
  285.      * @param Element\ElementInterface $element
  286.      *
  287.      * @return bool
  288.      */
  289.     public static function isElementRequestByAdmin(Request $requestElement\ElementInterface $element)
  290.     {
  291.         if (!self::isFrontendRequestByAdmin($request)) {
  292.             return false;
  293.         }
  294.         $user Tool\Authentication::authenticateSession($request);
  295.         return $user && $element->isAllowed('view'$user);
  296.     }
  297.     /**
  298.      * @internal
  299.      *
  300.      * @param Request|null $request
  301.      *
  302.      * @return bool
  303.      */
  304.     public static function useFrontendOutputFilters(Request $request null)
  305.     {
  306.         $request self::resolveRequest($request);
  307.         if (null === $request) {
  308.             return false;
  309.         }
  310.         if (!self::isFrontend($request)) {
  311.             return false;
  312.         }
  313.         if (self::isFrontendRequestByAdmin($request)) {
  314.             return false;
  315.         }
  316.         $requestKeys array_merge(
  317.             array_keys($request->query->all()),
  318.             array_keys($request->request->all())
  319.         );
  320.         // check for manually disabled ?pimcore_outputfilters_disabled=true
  321.         if (in_array('pimcore_outputfilters_disabled'$requestKeys) && \Pimcore::inDebugMode()) {
  322.             return false;
  323.         }
  324.         return true;
  325.     }
  326.     /**
  327.      * @internal
  328.      *
  329.      * @param Request|null $request
  330.      *
  331.      * @return null|string
  332.      */
  333.     public static function getHostname(Request $request null)
  334.     {
  335.         $request self::resolveRequest($request);
  336.         if (null === $request) {
  337.             return null;
  338.         }
  339.         return $request->getHost();
  340.     }
  341.     /**
  342.      * @internal
  343.      *
  344.      * @return string
  345.      */
  346.     public static function getRequestScheme(Request $request null)
  347.     {
  348.         $request self::resolveRequest($request);
  349.         if (null === $request) {
  350.             return 'http';
  351.         }
  352.         return $request->getScheme();
  353.     }
  354.     /**
  355.      * Returns the host URL
  356.      *
  357.      * @param string|null $useProtocol use a specific protocol
  358.      * @param Request|null $request
  359.      *
  360.      * @return string
  361.      */
  362.     public static function getHostUrl($useProtocol nullRequest $request null)
  363.     {
  364.         $request self::resolveRequest($request);
  365.         $protocol 'http';
  366.         $hostname '';
  367.         $port '';
  368.         if (null !== $request) {
  369.             $protocol $request->getScheme();
  370.             $hostname $request->getHost();
  371.             if (!in_array($request->getPort(), [44380])) {
  372.                 $port ':' $request->getPort();
  373.             }
  374.         }
  375.         // get it from System settings
  376.         if (!$hostname || $hostname == 'localhost') {
  377.             $systemConfig Config::getSystemConfiguration('general');
  378.             $hostname $systemConfig['domain'] ?? null;
  379.             if (!$hostname) {
  380.                 Logger::warn('Couldn\'t determine HTTP Host. No Domain set in "Settings" -> "System" -> "Website" -> "Domain"');
  381.                 return '';
  382.             }
  383.         }
  384.         if ($useProtocol) {
  385.             $protocol $useProtocol;
  386.         }
  387.         return $protocol '://' $hostname $port;
  388.     }
  389.     /**
  390.      * @internal
  391.      *
  392.      * @param Request|null $request
  393.      *
  394.      * @return string|null
  395.      */
  396.     public static function getClientIp(Request $request null)
  397.     {
  398.         $request self::resolveRequest($request);
  399.         if ($request) {
  400.             return $request->getClientIp();
  401.         }
  402.         // fallback to $_SERVER variables
  403.         if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
  404.             $ip $_SERVER['HTTP_CLIENT_IP'];
  405.         } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
  406.             $ip $_SERVER['HTTP_X_FORWARDED_FOR'];
  407.         } elseif (!empty($_SERVER['REMOTE_ADDR'])) {
  408.             $ip $_SERVER['REMOTE_ADDR'];
  409.         } else {
  410.             return null;
  411.         }
  412.         $ips explode(','$ip);
  413.         $ip trim(array_shift($ips));
  414.         return $ip;
  415.     }
  416.     /**
  417.      * @internal
  418.      *
  419.      * @param Request|null $request
  420.      *
  421.      * @return null|string
  422.      */
  423.     public static function getAnonymizedClientIp(Request $request null)
  424.     {
  425.         $request self::resolveRequest($request);
  426.         if (null === $request) {
  427.             return null;
  428.         }
  429.         return \Pimcore::getContainer()
  430.             ->get(RequestHelper::class)
  431.             ->getAnonymizedClientIp($request);
  432.     }
  433.     /**
  434.      * @param array|string|null $recipients
  435.      * @param string|null $subject
  436.      *
  437.      * @return Mail
  438.      *
  439.      * @throws \Exception
  440.      */
  441.     public static function getMail($recipients null$subject null)
  442.     {
  443.         $mail = new Mail();
  444.         if ($recipients) {
  445.             if (is_string($recipients)) {
  446.                 $mail->addTo($recipients);
  447.             } elseif (is_array($recipients)) {
  448.                 foreach ($recipients as $recipient) {
  449.                     $mail->addTo($recipient);
  450.                 }
  451.             }
  452.         }
  453.         if ($subject) {
  454.             $mail->setSubject($subject);
  455.         }
  456.         return $mail;
  457.     }
  458.     /**
  459.      * @param string $url
  460.      * @param array $paramsGet
  461.      * @param array $paramsPost
  462.      * @param array $options
  463.      *
  464.      * @return bool|string
  465.      */
  466.     public static function getHttpData($url$paramsGet = [], $paramsPost = [], $options = [])
  467.     {
  468.         $client \Pimcore::getContainer()->get('pimcore.http_client');
  469.         $requestType 'GET';
  470.         if (!isset($options['timeout'])) {
  471.             $options['timeout'] = 5;
  472.         }
  473.         if (is_array($paramsGet) && count($paramsGet) > 0) {
  474.             //need to insert get params from url to $paramsGet because otherwise the would be ignored
  475.             $urlParts parse_url($url);
  476.             $urlParams = [];
  477.             parse_str($urlParts['query'], $urlParams);
  478.             if ($urlParams) {
  479.                 $paramsGet array_merge($urlParams$paramsGet);
  480.             }
  481.             $options[RequestOptions::QUERY] = $paramsGet;
  482.         }
  483.         if (is_array($paramsPost) && count($paramsPost) > 0) {
  484.             $options[RequestOptions::FORM_PARAMS] = $paramsPost;
  485.             $requestType 'POST';
  486.         }
  487.         try {
  488.             $response $client->request($requestType$url$options);
  489.             if ($response->getStatusCode() < 300) {
  490.                 return (string)$response->getBody();
  491.             }
  492.         } catch (\Exception $e) {
  493.         }
  494.         return false;
  495.     }
  496.     /**
  497.      * @internal
  498.      *
  499.      * @param string $class
  500.      *
  501.      * @return bool
  502.      */
  503.     public static function classExists($class)
  504.     {
  505.         return self::classInterfaceExists($class'class');
  506.     }
  507.     /**
  508.      * @internal
  509.      *
  510.      * @param string $class
  511.      *
  512.      * @return bool
  513.      */
  514.     public static function interfaceExists($class)
  515.     {
  516.         return self::classInterfaceExists($class'interface');
  517.     }
  518.     /**
  519.      * @internal
  520.      *
  521.      * @param string $class
  522.      *
  523.      * @return bool
  524.      */
  525.     public static function traitExists($class)
  526.     {
  527.         return self::classInterfaceExists($class'trait');
  528.     }
  529.     /**
  530.      * @param string $class
  531.      * @param string $type (e.g. 'class', 'interface', 'trait')
  532.      *
  533.      * @return bool
  534.      */
  535.     private static function classInterfaceExists($class$type)
  536.     {
  537.         $functionName $type '_exists';
  538.         // if the class is already loaded we can skip right here
  539.         if ($functionName($classfalse)) {
  540.             return true;
  541.         }
  542.         $class '\\' ltrim($class'\\');
  543.         // let's test if we have seens this class already before
  544.         if (isset(self::$notFoundClassNames[$class])) {
  545.             return false;
  546.         }
  547.         // we need to set a custom error handler here for the time being
  548.         // unfortunately suppressNotFoundWarnings() doesn't work all the time, it has something to do with the calls in
  549.         // Pimcore\Tool::ClassMapAutoloader(), but don't know what actual conditions causes this problem.
  550.         // but to be save we log the errors into the debug.log, so if anything else happens we can see it there
  551.         // the normal warning is e.g. Warning: include_once(Path/To/Class.php): failed to open stream: No such file or directory in ...
  552.         set_error_handler(function ($errno$errstr$errfile$errline) {
  553.             //Logger::debug(implode(" ", [$errno, $errstr, $errfile, $errline]));
  554.         });
  555.         $exists $functionName($class);
  556.         restore_error_handler();
  557.         if (!$exists) {
  558.             self::$notFoundClassNames[$class] = true// value doesn't matter, key lookups are faster ;-)
  559.         }
  560.         return $exists;
  561.     }
  562.     /**
  563.      * @internal
  564.      *
  565.      * @return array
  566.      */
  567.     public static function getCachedSymfonyEnvironments(): array
  568.     {
  569.         $dirs glob(PIMCORE_SYMFONY_CACHE_DIRECTORY '/*'GLOB_ONLYDIR);
  570.         if (($key array_search(PIMCORE_CACHE_DIRECTORY$dirs)) !== false) {
  571.             unset($dirs[$key]);
  572.         }
  573.         $dirs array_map('basename'$dirs);
  574.         $dirs array_filter($dirs, function ($value) {
  575.             // this filters out "old" build directories, which end with a ~
  576.             return !(bool) \preg_match('/~$/'$value);
  577.         });
  578.         return array_values($dirs);
  579.     }
  580.     /**
  581.      * @internal
  582.      *
  583.      * @param string $message
  584.      */
  585.     public static function exitWithError($message)
  586.     {
  587.         while (@ob_end_flush());
  588.         if (php_sapi_name() != 'cli') {
  589.             header('HTTP/1.1 503 Service Temporarily Unavailable');
  590.         }
  591.         die($message);
  592.     }
  593. }