PageRenderTime 42ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/app/OroRequirements.php

https://gitlab.com/x33n/platform-application
PHP | 402 lines | 282 code | 59 blank | 61 comment | 23 complexity | b9dbec130821e0a8665beab645efea2d MD5 | raw file
  1. <?php
  2. require_once __DIR__ . '/SymfonyRequirements.php';
  3. use Symfony\Component\Process\ProcessBuilder;
  4. use Symfony\Component\Intl\Intl;
  5. use Oro\Bundle\InstallerBundle\Process\PhpExecutableFinder;
  6. use Oro\Bundle\RequireJSBundle\DependencyInjection\Configuration as RequireJSConfiguration;
  7. /**
  8. * This class specifies all requirements and optional recommendations that are necessary to run the Oro Application.
  9. */
  10. class OroRequirements extends SymfonyRequirements
  11. {
  12. const REQUIRED_PHP_VERSION = '5.4.9';
  13. const REQUIRED_GD_VERSION = '2.0';
  14. const REQUIRED_CURL_VERSION = '7.0';
  15. const REQUIRED_ICU_VERSION = '3.8';
  16. const EXCLUDE_REQUIREMENTS_MASK = '/5\.3\.(3|4|8|16)|5\.4\.(0|8)/';
  17. public function __construct()
  18. {
  19. parent::__construct();
  20. $phpVersion = phpversion();
  21. $gdVersion = defined('GD_VERSION') ? GD_VERSION : null;
  22. $curlVersion = function_exists('curl_version') ? curl_version() : null;
  23. $icuVersion = Intl::getIcuVersion();
  24. $this->addOroRequirement(
  25. version_compare($phpVersion, self::REQUIRED_PHP_VERSION, '>='),
  26. sprintf('PHP version must be at least %s (%s installed)', self::REQUIRED_PHP_VERSION, $phpVersion),
  27. sprintf(
  28. 'You are running PHP version "<strong>%s</strong>", but Oro needs at least PHP "<strong>%s</strong>" to run.' .
  29. 'Before using Oro, upgrade your PHP installation, preferably to the latest version.',
  30. $phpVersion,
  31. self::REQUIRED_PHP_VERSION
  32. ),
  33. sprintf('Install PHP %s or newer (installed version is %s)', self::REQUIRED_PHP_VERSION, $phpVersion)
  34. );
  35. $this->addOroRequirement(
  36. null !== $gdVersion && version_compare($gdVersion, self::REQUIRED_GD_VERSION, '>='),
  37. 'GD extension must be at least ' . self::REQUIRED_GD_VERSION,
  38. 'Install and enable the <strong>GD</strong> extension at least ' . self::REQUIRED_GD_VERSION . ' version'
  39. );
  40. $this->addOroRequirement(
  41. null !== $curlVersion && version_compare($curlVersion['version'], self::REQUIRED_CURL_VERSION, '>='),
  42. 'cURL extension must be at least ' . self::REQUIRED_CURL_VERSION,
  43. 'Install and enable the <strong>cURL</strong> extension at least ' . self::REQUIRED_CURL_VERSION . ' version'
  44. );
  45. $this->addOroRequirement(
  46. function_exists('mcrypt_encrypt'),
  47. 'mcrypt_encrypt() should be available',
  48. 'Install and enable the <strong>Mcrypt</strong> extension.'
  49. );
  50. $this->addOroRequirement(
  51. class_exists('Locale'),
  52. 'intl extension should be available',
  53. 'Install and enable the <strong>intl</strong> extension.'
  54. );
  55. $this->addOroRequirement(
  56. null !== $icuVersion && version_compare($icuVersion, self::REQUIRED_ICU_VERSION, '>='),
  57. 'icu library must be at least ' . self::REQUIRED_ICU_VERSION,
  58. 'Install and enable the <strong>icu</strong> library at least ' . self::REQUIRED_ICU_VERSION . ' version'
  59. );
  60. $this->addRecommendation(
  61. class_exists('SoapClient'),
  62. 'SOAP extension should be installed (API calls)',
  63. 'Install and enable the <strong>SOAP</strong> extension.'
  64. );
  65. // Windows specific checks
  66. if (defined('PHP_WINDOWS_VERSION_BUILD')) {
  67. $this->addRecommendation(
  68. function_exists('finfo_open'),
  69. 'finfo_open() should be available',
  70. 'Install and enable the <strong>Fileinfo</strong> extension.'
  71. );
  72. $this->addRecommendation(
  73. class_exists('COM'),
  74. 'COM extension should be installed',
  75. 'Install and enable the <strong>COM</strong> extension.'
  76. );
  77. }
  78. // Unix specific checks
  79. if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
  80. $this->addRequirement(
  81. $this->checkFileNameLength(),
  82. 'Cache folder should not be inside encrypted directory',
  83. 'Move <strong>app/cache</strong> folder outside encrypted directory.'
  84. );
  85. }
  86. // Web installer specific checks
  87. if ('cli' !== PHP_SAPI) {
  88. $output = $this->checkCliRequirements();
  89. $requirement = new CliRequirement(
  90. !$output,
  91. 'Requirements validation for PHP CLI',
  92. 'If you have multiple PHP versions installed, you need to configure ORO_PHP_PATH variable with PHP binary path used by web server'
  93. );
  94. $requirement->setOutput($output);
  95. $this->add($requirement);
  96. }
  97. $baseDir = realpath(__DIR__ . '/..');
  98. $mem = $this->getBytes(ini_get('memory_limit'));
  99. $this->addPhpIniRequirement(
  100. 'memory_limit',
  101. function ($cfgValue) use ($mem) {
  102. return $mem >= 512 * 1024 * 1024 || -1 == $mem;
  103. },
  104. false,
  105. 'memory_limit should be at least 512M',
  106. 'Set the "<strong>memory_limit</strong>" setting in php.ini<a href="#phpini">*</a> to at least "512M".'
  107. );
  108. $jsEngine = RequireJSConfiguration::getDefaultJsEngine();
  109. $this->addRecommendation(
  110. $jsEngine ? true : false,
  111. $jsEngine ? "A JS Engine ($jsEngine) is installed" : 'JSEngine such as NodeJS should be installed',
  112. 'Install <strong>JSEngine</strong>.'
  113. );
  114. $this->addOroRequirement(
  115. is_writable($baseDir . '/web/uploads'),
  116. 'web/uploads/ directory must be writable',
  117. 'Change the permissions of the "<strong>web/uploads/</strong>" directory so that the web server can write into it.'
  118. );
  119. $this->addOroRequirement(
  120. is_writable($baseDir . '/web/media'),
  121. 'web/media/ directory must be writable',
  122. 'Change the permissions of the "<strong>web/media/</strong>" directory so that the web server can write into it.'
  123. );
  124. $this->addOroRequirement(
  125. is_writable($baseDir . '/web/bundles'),
  126. 'web/bundles/ directory must be writable',
  127. 'Change the permissions of the "<strong>web/bundles/</strong>" directory so that the web server can write into it.'
  128. );
  129. $this->addOroRequirement(
  130. is_writable($baseDir . '/app/attachment'),
  131. 'app/attachment/ directory must be writable',
  132. 'Change the permissions of the "<strong>app/attachment/</strong>" directory so that the web server can write into it.'
  133. );
  134. if (is_dir($baseDir . '/web/js')) {
  135. $this->addOroRequirement(
  136. is_writable($baseDir . '/web/js'),
  137. 'web/js directory must be writable',
  138. 'Change the permissions of the "<strong>web/js</strong>" directory so that the web server can write into it.'
  139. );
  140. }
  141. if (is_dir($baseDir . '/web/css')) {
  142. $this->addOroRequirement(
  143. is_writable($baseDir . '/web/css'),
  144. 'web/css directory must be writable',
  145. 'Change the permissions of the "<strong>web/css</strong>" directory so that the web server can write into it.'
  146. );
  147. }
  148. if (!is_dir($baseDir . '/web/css') || !is_dir($baseDir . '/web/js')) {
  149. $this->addOroRequirement(
  150. is_writable($baseDir . '/web'),
  151. 'web directory must be writable',
  152. 'Change the permissions of the "<strong>web</strong>" directory so that the web server can write into it.'
  153. );
  154. }
  155. if (is_file($baseDir . '/app/config/parameters.yml')) {
  156. $this->addOroRequirement(
  157. is_writable($baseDir . '/app/config/parameters.yml'),
  158. 'app/config/parameters.yml file must be writable',
  159. 'Change the permissions of the "<strong>app/config/parameters.yml</strong>" file so that the web server can write into it.'
  160. );
  161. }
  162. }
  163. /**
  164. * Adds an Oro specific requirement.
  165. *
  166. * @param Boolean $fulfilled Whether the requirement is fulfilled
  167. * @param string $testMessage The message for testing the requirement
  168. * @param string $helpHtml The help text formatted in HTML for resolving the problem
  169. * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
  170. */
  171. public function addOroRequirement($fulfilled, $testMessage, $helpHtml, $helpText = null)
  172. {
  173. $this->add(new OroRequirement($fulfilled, $testMessage, $helpHtml, $helpText, false));
  174. }
  175. /**
  176. * Get the list of mandatory requirements (all requirements excluding PhpIniRequirement)
  177. *
  178. * @return array
  179. */
  180. public function getMandatoryRequirements()
  181. {
  182. return array_filter(
  183. $this->getRequirements(),
  184. function ($requirement) {
  185. return !($requirement instanceof PhpIniRequirement)
  186. && !($requirement instanceof OroRequirement)
  187. && !($requirement instanceof CliRequirement);
  188. }
  189. );
  190. }
  191. /**
  192. * Get the list of PHP ini requirements
  193. *
  194. * @return array
  195. */
  196. public function getPhpIniRequirements()
  197. {
  198. return array_filter(
  199. $this->getRequirements(),
  200. function ($requirement) {
  201. return $requirement instanceof PhpIniRequirement;
  202. }
  203. );
  204. }
  205. /**
  206. * Get the list of Oro specific requirements
  207. *
  208. * @return array
  209. */
  210. public function getOroRequirements()
  211. {
  212. return array_filter(
  213. $this->getRequirements(),
  214. function ($requirement) {
  215. return $requirement instanceof OroRequirement;
  216. }
  217. );
  218. }
  219. /**
  220. * @return array
  221. */
  222. public function getCliRequirements()
  223. {
  224. return array_filter(
  225. $this->getRequirements(),
  226. function ($requirement) {
  227. return $requirement instanceof CliRequirement;
  228. }
  229. );
  230. }
  231. /**
  232. * @param string $val
  233. * @return int
  234. */
  235. protected function getBytes($val)
  236. {
  237. if (empty($val)) {
  238. return 0;
  239. }
  240. preg_match('/([\-0-9]+)[\s]*([a-z]*)$/i', trim($val), $matches);
  241. if (isset($matches[1])) {
  242. $val = (int)$matches[1];
  243. }
  244. switch (strtolower($matches[2])) {
  245. case 'g':
  246. case 'gb':
  247. $val *= 1024;
  248. // no break
  249. case 'm':
  250. case 'mb':
  251. $val *= 1024;
  252. // no break
  253. case 'k':
  254. case 'kb':
  255. $val *= 1024;
  256. // no break
  257. }
  258. return (float)$val;
  259. }
  260. /**
  261. * {@inheritdoc}
  262. */
  263. public function getRequirements()
  264. {
  265. $requirements = parent::getRequirements();
  266. foreach ($requirements as $key => $requirement) {
  267. $testMessage = $requirement->getTestMessage();
  268. if (preg_match_all(self::EXCLUDE_REQUIREMENTS_MASK, $testMessage, $matches)) {
  269. unset($requirements[$key]);
  270. }
  271. }
  272. return $requirements;
  273. }
  274. /**
  275. * {@inheritdoc}
  276. */
  277. public function getRecommendations()
  278. {
  279. $recommendations = parent::getRecommendations();
  280. foreach ($recommendations as $key => $recommendation) {
  281. $testMessage = $recommendation->getTestMessage();
  282. if (preg_match_all(self::EXCLUDE_REQUIREMENTS_MASK, $testMessage, $matches)) {
  283. unset($recommendations[$key]);
  284. }
  285. }
  286. return $recommendations;
  287. }
  288. /**
  289. * @return bool
  290. */
  291. protected function checkFileNameLength()
  292. {
  293. $getConf = new ProcessBuilder(array('getconf', 'NAME_MAX', __DIR__));
  294. $getConf = $getConf->getProcess();
  295. if (isset($_SERVER['PATH'])) {
  296. $getConf->setEnv(array('PATH' => $_SERVER['PATH']));
  297. }
  298. $getConf->run();
  299. if ($getConf->getErrorOutput()) {
  300. // getconf not installed
  301. return true;
  302. }
  303. $fileLength = trim($getConf->getOutput());
  304. return $fileLength == 255;
  305. }
  306. /**
  307. * @return null|string
  308. */
  309. protected function checkCliRequirements()
  310. {
  311. $finder = new PhpExecutableFinder();
  312. $command = sprintf(
  313. '%s %soro-check.php',
  314. $finder->find(),
  315. __DIR__ . DIRECTORY_SEPARATOR
  316. );
  317. return shell_exec($command);
  318. }
  319. }
  320. class OroRequirement extends Requirement
  321. {
  322. }
  323. class CliRequirement extends Requirement
  324. {
  325. /**
  326. * @var string
  327. */
  328. protected $output;
  329. /**
  330. * @return string
  331. */
  332. public function getOutput()
  333. {
  334. return $this->output;
  335. }
  336. /**
  337. * @param string $output
  338. */
  339. public function setOutput($output)
  340. {
  341. $this->output = $output;
  342. }
  343. }