vendor/pimcore/pimcore/lib/Tool/Console.php line 128

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\Tool;
  15. use Pimcore\Config;
  16. use Pimcore\Logger;
  17. use Symfony\Component\Process\ExecutableFinder;
  18. use Symfony\Component\Process\PhpExecutableFinder;
  19. use Symfony\Component\Process\Process;
  20. final class Console
  21. {
  22.     /**
  23.      * @var string system environment
  24.      */
  25.     private static $systemEnvironment;
  26.     /**
  27.      * @var array
  28.      */
  29.     protected static $executableCache = [];
  30.     /**
  31.      * @deprecated since v.6.9.
  32.      *
  33.      * @static
  34.      *
  35.      * @return string "windows" or "unix"
  36.      */
  37.     public static function getSystemEnvironment()
  38.     {
  39.         if (self::$systemEnvironment == null) {
  40.             if (stripos(php_uname('s'), 'windows') !== false) {
  41.                 self::$systemEnvironment 'windows';
  42.             } elseif (stripos(php_uname('s'), 'darwin') !== false) {
  43.                 self::$systemEnvironment 'darwin';
  44.             } else {
  45.                 self::$systemEnvironment 'unix';
  46.             }
  47.         }
  48.         return self::$systemEnvironment;
  49.     }
  50.     /**
  51.      * @param string $name
  52.      * @param bool $throwException
  53.      *
  54.      * @return bool|string
  55.      *
  56.      * @throws \Exception
  57.      */
  58.     public static function getExecutable($name$throwException false)
  59.     {
  60.         if (isset(self::$executableCache[$name])) {
  61.             if (!self::$executableCache[$name] && $throwException) {
  62.                 throw new \Exception("No '$name' executable found, please install the application or add it to the PATH (in system settings or to your PATH environment variable");
  63.             }
  64.             return self::$executableCache[$name];
  65.         }
  66.         // allow custom setup routines for certain programs
  67.         $customSetupMethod 'setup' ucfirst($name);
  68.         if (method_exists(__CLASS__$customSetupMethod)) {
  69.             self::$customSetupMethod();
  70.         }
  71.         // use DI to provide the ability to customize / overwrite paths
  72.         if (\Pimcore::hasContainer() && \Pimcore::getContainer()->hasParameter('pimcore_executable_' $name)) {
  73.             $value \Pimcore::getContainer()->getParameter('pimcore_executable_' $name);
  74.             if ($value === false) {
  75.                 if ($throwException) {
  76.                     throw new \Exception("'$name' executable was disabled manually in parameters.yml");
  77.                 }
  78.                 return false;
  79.             }
  80.             if ($value) {
  81.                 return $value;
  82.             }
  83.         }
  84.         $paths = [];
  85.         try {
  86.             $systemConfig Config::getSystemConfiguration('general');
  87.             if (!empty($systemConfig['path_variable'])) {
  88.                 $paths explode(PATH_SEPARATOR$systemConfig['path_variable']);
  89.             }
  90.         } catch (\Exception $e) {
  91.             Logger::warning((string) $e);
  92.         }
  93.         array_push($paths'');
  94.         // allow custom check routines for certain programs
  95.         $customCheckMethod 'check' ucfirst($name);
  96.         if (!method_exists(__CLASS__$customCheckMethod)) {
  97.             $customCheckMethod null;
  98.         }
  99.         foreach ($paths as $path) {
  100.             try {
  101.                 $path rtrim($path'/\\ ');
  102.                 if ($path) {
  103.                     $executablePath $path DIRECTORY_SEPARATOR $name;
  104.                 } else {
  105.                     $executablePath $name;
  106.                 }
  107.                 $executableFinder = new ExecutableFinder();
  108.                 $fullQualifiedPath $executableFinder->find($executablePath);
  109.                 if ($fullQualifiedPath) {
  110.                     if (!$customCheckMethod || self::$customCheckMethod($executablePath)) {
  111.                         self::$executableCache[$name] = $fullQualifiedPath;
  112.                         return $fullQualifiedPath;
  113.                     }
  114.                 }
  115.             } catch (\Exception $e) {
  116.                 // nothing to do ...
  117.             }
  118.         }
  119.         self::$executableCache[$name] = false;
  120.         if ($throwException) {
  121.             throw new \Exception("No '$name' executable found, please install the application or add it to the PATH (in system settings or to your PATH environment variable");
  122.         }
  123.         return false;
  124.     }
  125.     /**
  126.      * @param string $process
  127.      *
  128.      * @return bool
  129.      */
  130.     protected static function checkComposite($process)
  131.     {
  132.         return self::checkConvert($process);
  133.     }
  134.     /**
  135.      * @param string $executablePath
  136.      *
  137.      * @return bool
  138.      */
  139.     protected static function checkConvert($executablePath)
  140.     {
  141.         try {
  142.             $process = new Process([$executablePath'--help']);
  143.             $process->run();
  144.             if (strpos($process->getOutput() . $process->getErrorOutput(), 'imagemagick.org') !== false) {
  145.                 return true;
  146.             }
  147.         } catch (\Exception $e) {
  148.             // noting to do
  149.         }
  150.         return false;
  151.     }
  152.     /**
  153.      * @return mixed
  154.      *
  155.      * @throws \Exception
  156.      */
  157.     public static function getPhpCli()
  158.     {
  159.         try {
  160.             return self::getExecutable('php'true);
  161.         } catch (\Exception $e) {
  162.             $phpFinder = new PhpExecutableFinder();
  163.             $phpPath $phpFinder->find(true);
  164.             if (!$phpPath) {
  165.                 throw $e;
  166.             }
  167.             return $phpPath;
  168.         }
  169.     }
  170.     /**
  171.      * @return bool|string
  172.      */
  173.     public static function getTimeoutBinary()
  174.     {
  175.         return self::getExecutable('timeout');
  176.     }
  177.     /**
  178.      * @param string $script
  179.      * @param array $arguments
  180.      *
  181.      * @return array
  182.      */
  183.     protected static function buildPhpScriptCmd(string $script, array $arguments = [])
  184.     {
  185.         $phpCli self::getPhpCli();
  186.         $cmd = [$phpCli$script];
  187.         if (Config::getEnvironment()) {
  188.             array_push($cmd'--env=' Config::getEnvironment());
  189.         }
  190.         if (!empty($arguments)) {
  191.             $cmd array_merge($cmd$arguments);
  192.         }
  193.         return $cmd;
  194.     }
  195.     /**
  196.      * @param string $script
  197.      * @param array $arguments
  198.      * @param string|null $outputFile
  199.      * @param float|null $timeout
  200.      *
  201.      * @return string
  202.      */
  203.     public static function runPhpScript($script$arguments = [], $outputFile null$timeout 60)
  204.     {
  205.         $cmd self::buildPhpScriptCmd($script$arguments);
  206.         self::addLowProcessPriority($cmd);
  207.         $process = new Process($cmd);
  208.         $process->setTimeout($timeout);
  209.         $process->start();
  210.         if (!empty($outputFile)) {
  211.             $logHandle fopen($outputFile'a');
  212.             $process->wait(function ($type$buffer) use ($logHandle) {
  213.                 fwrite($logHandle$buffer);
  214.             });
  215.             fclose($logHandle);
  216.         } else {
  217.             $process->wait();
  218.         }
  219.         return $process->getOutput();
  220.     }
  221.     /**
  222.      * @deprecated since v6.9. For long running background tasks switch to a queue implementation.
  223.      *
  224.      * @param string $script
  225.      * @param array $arguments
  226.      * @param string|null $outputFile
  227.      *
  228.      * @return int
  229.      */
  230.     public static function runPhpScriptInBackground($script$arguments = [], $outputFile null)
  231.     {
  232.         $cmd self::buildPhpScriptCmd($script$arguments);
  233.         $process = new Process($cmd);
  234.         $commandLine $process->getCommandLine();
  235.         return self::execInBackground($commandLine$outputFile);
  236.     }
  237.     /**
  238.      * @deprecated since v.6.9. Use Symfony\Component\Process\Process instead. For long running background tasks use queues.
  239.      *
  240.      * @static
  241.      *
  242.      * @param string $cmd
  243.      * @param null|string $outputFile
  244.      *
  245.      * @return int
  246.      */
  247.     public static function execInBackground($cmd$outputFile null)
  248.     {
  249.         // windows systems
  250.         if (self::getSystemEnvironment() == 'windows') {
  251.             return self::execInBackgroundWindows($cmd$outputFile);
  252.         } elseif (self::getSystemEnvironment() == 'darwin') {
  253.             return self::execInBackgroundUnix($cmd$outputFilefalse);
  254.         } else {
  255.             return self::execInBackgroundUnix($cmd$outputFile);
  256.         }
  257.     }
  258.     /**
  259.      * @deprecated since v.6.9. For long running background tasks use queues.
  260.      *
  261.      * @static
  262.      *
  263.      * @param string $cmd
  264.      * @param string $outputFile
  265.      * @param bool $useNohup
  266.      *
  267.      * @return int
  268.      */
  269.     protected static function execInBackgroundUnix($cmd$outputFile$useNohup true)
  270.     {
  271.         if (!$outputFile) {
  272.             $outputFile '/dev/null';
  273.         }
  274.         $nice = (string) self::getExecutable('nice');
  275.         if ($nice) {
  276.             $nice .= ' -n 19 ';
  277.         }
  278.         if ($useNohup) {
  279.             $nohup = (string) self::getExecutable('nohup');
  280.             if ($nohup) {
  281.                 $nohup .= ' ';
  282.             }
  283.         } else {
  284.             $nohup '';
  285.         }
  286.         /**
  287.          * mod_php seems to lose the environment variables if we do not set them manually before the child process is started
  288.          */
  289.         if (strpos(php_sapi_name(), 'apache') !== false) {
  290.             foreach (['APP_ENV'] as $envVarName) {
  291.                 if ($envValue $_SERVER[$envVarName] ?? $_SERVER['REDIRECT_' $envVarName] ?? null) {
  292.                     putenv($envVarName '='.$envValue);
  293.                 }
  294.             }
  295.         }
  296.         $commandWrapped $nohup $nice $cmd ' > '$outputFile .' 2>&1 & echo $!';
  297.         Logger::debug('Executing command `' $commandWrapped '´ on the current shell in background');
  298.         $pid shell_exec($commandWrapped);
  299.         Logger::debug('Process started with PID ' $pid);
  300.         return (int)$pid;
  301.     }
  302.     /**
  303.      * @deprecated since v.6.9. For long running background tasks use queues.
  304.      *
  305.      * @static
  306.      *
  307.      * @param string $cmd
  308.      * @param string $outputFile
  309.      *
  310.      * @return int
  311.      */
  312.     protected static function execInBackgroundWindows($cmd$outputFile)
  313.     {
  314.         if (!$outputFile) {
  315.             $outputFile 'NUL';
  316.         }
  317.         $commandWrapped 'cmd /c ' $cmd ' > '$outputFile ' 2>&1';
  318.         Logger::debug('Executing command `' $commandWrapped '´ on the current shell in background');
  319.         $WshShell = new \COM('WScript.Shell');
  320.         $WshShell->Run($commandWrapped0false);
  321.         Logger::debug('Process started - returning the PID is not supported on Windows Systems');
  322.         return 0;
  323.     }
  324.     /**
  325.      * @internal
  326.      *
  327.      * @param array|string $cmd
  328.      *
  329.      * @return void
  330.      */
  331.     public static function addLowProcessPriority(&$cmd): void
  332.     {
  333.         $nice = (string) self::getExecutable('nice');
  334.         if ($nice) {
  335.             if (is_string($cmd)) {
  336.                 $cmd $nice ' -n 19 ' $cmd;
  337.             } elseif (is_array($cmd)) {
  338.                 array_unshift($cmd$nice'-n''19');
  339.             }
  340.         }
  341.     }
  342. }