PageRenderTime 44ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/app/SymfonyRequirements.php

https://github.com/neoxygen/symfony-standard
PHP | 609 lines | 342 code | 76 blank | 191 comment | 29 complexity | 71eaaf2a45e7ae1b7c8f7ec62c84990a MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. /**
  11. * Represents a single PHP requirement, e.g. an installed extension.
  12. * It can be a mandatory requirement or an optional recommendation.
  13. * There is a special subclass, named PhpIniRequirement, to check a php.ini configuration.
  14. *
  15. * This file must be compatible with PHP 5.2+.
  16. *
  17. * ************** CAUTION **************
  18. *
  19. * DO NOT EDIT THIS FILE AS IT WILL BE OVERRIDEN BY COMPOSER
  20. *
  21. * If you want to change this file, edit the one in the
  22. * SensioDistributionBundle instead.
  23. *
  24. * ************** CAUTION **************
  25. *
  26. * @author Tobias Schultze <http://tobion.de>
  27. */
  28. class Requirement
  29. {
  30. private $fulfilled;
  31. private $testMessage;
  32. private $helpText;
  33. private $helpHtml;
  34. private $optional;
  35. /**
  36. * Constructor that initializes the requirement.
  37. *
  38. * @param Boolean $fulfilled Whether the requirement is fulfilled
  39. * @param string $testMessage The message for testing the requirement
  40. * @param string $helpHtml The help text formatted in HTML for resolving the problem
  41. * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
  42. * @param Boolean $optional Whether this is only an optional recommendation not a mandatory requirement
  43. */
  44. public function __construct($fulfilled, $testMessage, $helpHtml, $helpText = null, $optional = false)
  45. {
  46. $this->fulfilled = (Boolean) $fulfilled;
  47. $this->testMessage = (string) $testMessage;
  48. $this->helpHtml = (string) $helpHtml;
  49. $this->helpText = null === $helpText ? strip_tags($this->helpHtml) : (string) $helpText;
  50. $this->optional = (Boolean) $optional;
  51. }
  52. /**
  53. * Returns whether the requirement is fulfilled.
  54. *
  55. * @return Boolean true if fulfilled, otherwise false
  56. */
  57. public function isFulfilled()
  58. {
  59. return $this->fulfilled;
  60. }
  61. /**
  62. * Returns the message for testing the requirement.
  63. *
  64. * @return string The test message
  65. */
  66. public function getTestMessage()
  67. {
  68. return $this->testMessage;
  69. }
  70. /**
  71. * Returns the help text for resolving the problem
  72. *
  73. * @return string The help text
  74. */
  75. public function getHelpText()
  76. {
  77. return $this->helpText;
  78. }
  79. /**
  80. * Returns the help text formatted in HTML.
  81. *
  82. * @return string The HTML help
  83. */
  84. public function getHelpHtml()
  85. {
  86. return $this->helpHtml;
  87. }
  88. /**
  89. * Returns whether this is only an optional recommendation and not a mandatory requirement.
  90. *
  91. * @return Boolean true if optional, false if mandatory
  92. */
  93. public function isOptional()
  94. {
  95. return $this->optional;
  96. }
  97. }
  98. /**
  99. * Represents a PHP requirement in form of a php.ini configuration.
  100. *
  101. * @author Tobias Schultze <http://tobion.de>
  102. */
  103. class PhpIniRequirement extends Requirement
  104. {
  105. /**
  106. * Constructor that initializes the requirement.
  107. *
  108. * @param string $cfgName The configuration name used for ini_get()
  109. * @param Boolean|callback $evaluation Either a Boolean indicating whether the configuration should evaluate to true or false,
  110. or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
  111. * @param Boolean $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false.
  112. This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
  113. Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
  114. * @param string $testMessage The message for testing the requirement (when null and $evaluation is a Boolean a default message is derived)
  115. * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a Boolean a default help is derived)
  116. * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
  117. * @param Boolean $optional Whether this is only an optional recommendation not a mandatory requirement
  118. */
  119. public function __construct($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null, $optional = false)
  120. {
  121. $cfgValue = ini_get($cfgName);
  122. if (is_callable($evaluation)) {
  123. if (null === $testMessage || null === $helpHtml) {
  124. throw new InvalidArgumentException('You must provide the parameters testMessage and helpHtml for a callback evaluation.');
  125. }
  126. $fulfilled = call_user_func($evaluation, $cfgValue);
  127. } else {
  128. if (null === $testMessage) {
  129. $testMessage = sprintf('%s %s be %s in php.ini',
  130. $cfgName,
  131. $optional ? 'should' : 'must',
  132. $evaluation ? 'enabled' : 'disabled'
  133. );
  134. }
  135. if (null === $helpHtml) {
  136. $helpHtml = sprintf('Set <strong>%s</strong> to <strong>%s</strong> in php.ini<a href="#phpini">*</a>.',
  137. $cfgName,
  138. $evaluation ? 'on' : 'off'
  139. );
  140. }
  141. $fulfilled = $evaluation == $cfgValue;
  142. }
  143. parent::__construct($fulfilled || ($approveCfgAbsence && false === $cfgValue), $testMessage, $helpHtml, $helpText, $optional);
  144. }
  145. }
  146. /**
  147. * A RequirementCollection represents a set of Requirement instances.
  148. *
  149. * Users of PHP 5.2 should be able to run the requirements checks.
  150. * This is why the class must be compatible with PHP 5.2
  151. * (e.g. not using namespaces and closures).
  152. *
  153. * @author Tobias Schultze <http://tobion.de>
  154. */
  155. class RequirementCollection implements IteratorAggregate
  156. {
  157. private $requirements = array();
  158. /**
  159. * Gets the current RequirementCollection as an Iterator.
  160. *
  161. * @return Traversable A Traversable interface
  162. */
  163. public function getIterator()
  164. {
  165. return new ArrayIterator($this->requirements);
  166. }
  167. /**
  168. * Adds a Requirement.
  169. *
  170. * @param Requirement $requirement A Requirement instance
  171. */
  172. public function add(Requirement $requirement)
  173. {
  174. $this->requirements[] = $requirement;
  175. }
  176. /**
  177. * Adds a mandatory requirement.
  178. *
  179. * @param Boolean $fulfilled Whether the requirement is fulfilled
  180. * @param string $testMessage The message for testing the requirement
  181. * @param string $helpHtml The help text formatted in HTML for resolving the problem
  182. * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
  183. */
  184. public function addRequirement($fulfilled, $testMessage, $helpHtml, $helpText = null)
  185. {
  186. $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, false));
  187. }
  188. /**
  189. * Adds an optional recommendation.
  190. *
  191. * @param Boolean $fulfilled Whether the recommendation is fulfilled
  192. * @param string $testMessage The message for testing the recommendation
  193. * @param string $helpHtml The help text formatted in HTML for resolving the problem
  194. * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
  195. */
  196. public function addRecommendation($fulfilled, $testMessage, $helpHtml, $helpText = null)
  197. {
  198. $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, true));
  199. }
  200. /**
  201. * Adds a mandatory requirement in form of a php.ini configuration.
  202. *
  203. * @param string $cfgName The configuration name used for ini_get()
  204. * @param Boolean|callback $evaluation Either a Boolean indicating whether the configuration should evaluate to true or false,
  205. or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
  206. * @param Boolean $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false.
  207. This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
  208. Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
  209. * @param string $testMessage The message for testing the requirement (when null and $evaluation is a Boolean a default message is derived)
  210. * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a Boolean a default help is derived)
  211. * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
  212. */
  213. public function addPhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null)
  214. {
  215. $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, false));
  216. }
  217. /**
  218. * Adds an optional recommendation in form of a php.ini configuration.
  219. *
  220. * @param string $cfgName The configuration name used for ini_get()
  221. * @param Boolean|callback $evaluation Either a Boolean indicating whether the configuration should evaluate to true or false,
  222. or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
  223. * @param Boolean $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false.
  224. This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
  225. Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
  226. * @param string $testMessage The message for testing the requirement (when null and $evaluation is a Boolean a default message is derived)
  227. * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a Boolean a default help is derived)
  228. * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
  229. */
  230. public function addPhpIniRecommendation($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null)
  231. {
  232. $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, true));
  233. }
  234. /**
  235. * Adds a requirement collection to the current set of requirements.
  236. *
  237. * @param RequirementCollection $collection A RequirementCollection instance
  238. */
  239. public function addCollection(RequirementCollection $collection)
  240. {
  241. $this->requirements = array_merge($this->requirements, $collection->all());
  242. }
  243. /**
  244. * Returns both requirements and recommendations.
  245. *
  246. * @return array Array of Requirement instances
  247. */
  248. public function all()
  249. {
  250. return $this->requirements;
  251. }
  252. /**
  253. * Returns all mandatory requirements.
  254. *
  255. * @return array Array of Requirement instances
  256. */
  257. public function getRequirements()
  258. {
  259. $array = array();
  260. foreach ($this->requirements as $req) {
  261. if (!$req->isOptional()) {
  262. $array[] = $req;
  263. }
  264. }
  265. return $array;
  266. }
  267. /**
  268. * Returns the mandatory requirements that were not met.
  269. *
  270. * @return array Array of Requirement instances
  271. */
  272. public function getFailedRequirements()
  273. {
  274. $array = array();
  275. foreach ($this->requirements as $req) {
  276. if (!$req->isFulfilled() && !$req->isOptional()) {
  277. $array[] = $req;
  278. }
  279. }
  280. return $array;
  281. }
  282. /**
  283. * Returns all optional recommmendations.
  284. *
  285. * @return array Array of Requirement instances
  286. */
  287. public function getRecommendations()
  288. {
  289. $array = array();
  290. foreach ($this->requirements as $req) {
  291. if ($req->isOptional()) {
  292. $array[] = $req;
  293. }
  294. }
  295. return $array;
  296. }
  297. /**
  298. * Returns the recommendations that were not met.
  299. *
  300. * @return array Array of Requirement instances
  301. */
  302. public function getFailedRecommendations()
  303. {
  304. $array = array();
  305. foreach ($this->requirements as $req) {
  306. if (!$req->isFulfilled() && $req->isOptional()) {
  307. $array[] = $req;
  308. }
  309. }
  310. return $array;
  311. }
  312. /**
  313. * Returns whether a php.ini configuration is not correct.
  314. *
  315. * @return Boolean php.ini configuration problem?
  316. */
  317. public function hasPhpIniConfigIssue()
  318. {
  319. foreach ($this->requirements as $req) {
  320. if (!$req->isFulfilled() && $req instanceof PhpIniRequirement) {
  321. return true;
  322. }
  323. }
  324. return false;
  325. }
  326. /**
  327. * Returns the PHP configuration file (php.ini) path.
  328. *
  329. * @return string|false php.ini file path
  330. */
  331. public function getPhpIniConfigPath()
  332. {
  333. return get_cfg_var('cfg_file_path');
  334. }
  335. }
  336. /**
  337. * This class specifies all requirements and optional recommendations that
  338. * are necessary to run the Symfony Standard Edition.
  339. *
  340. * @author Tobias Schultze <http://tobion.de>
  341. */
  342. class SymfonyRequirements extends RequirementCollection
  343. {
  344. const REQUIRED_PHP_VERSION = '5.3.3';
  345. /**
  346. * Constructor that initializes the requirements.
  347. */
  348. public function __construct()
  349. {
  350. /* mandatory requirements follow */
  351. $installedPhpVersion = phpversion();
  352. $this->addRequirement(
  353. version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>='),
  354. sprintf('PHP version must be at least %s (%s installed)', self::REQUIRED_PHP_VERSION, $installedPhpVersion),
  355. sprintf('You are running PHP version "<strong>%s</strong>", but Symfony needs at least PHP "<strong>%s</strong>" to run.
  356. Before using Symfony, upgrade your PHP installation, preferably to the latest version.',
  357. $installedPhpVersion, self::REQUIRED_PHP_VERSION),
  358. sprintf('Install PHP %s or newer (installed version is %s)', self::REQUIRED_PHP_VERSION, $installedPhpVersion)
  359. );
  360. $this->addRequirement(
  361. is_dir(__DIR__.'/../vendor/composer'),
  362. 'Vendor libraries must be installed',
  363. 'Vendor libraries are missing. Install composer following instructions from <a href="http://getcomposer.org/">http://getcomposer.org/</a>. ' .
  364. 'Then run "<strong>php composer.phar install</strong>" to install them.'
  365. );
  366. $this->addRequirement(
  367. file_get_contents(__FILE__) == file_get_contents(__DIR__.'/../vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/skeleton/app/SymfonyRequirements.php'),
  368. 'Outdated requirements file',
  369. 'Your requirements file is outdated. Run composer install and re-check your configuration.'
  370. );
  371. $baseDir = basename(__DIR__);
  372. $this->addRequirement(
  373. is_writable(__DIR__.'/cache'),
  374. "$baseDir/cache/ directory must be writable",
  375. "Change the permissions of the \"<strong>$baseDir/cache/</strong>\" directory so that the web server can write into it."
  376. );
  377. $this->addRequirement(
  378. is_writable(__DIR__.'/logs'),
  379. "$baseDir/logs/ directory must be writable",
  380. "Change the permissions of the \"<strong>$baseDir/logs/</strong>\" directory so that the web server can write into it."
  381. );
  382. $this->addPhpIniRequirement(
  383. 'date.timezone', true, false,
  384. 'date.timezone setting must be set',
  385. 'Set the "<strong>date.timezone</strong>" setting in php.ini<a href="#phpini">*</a> (like Europe/Paris).'
  386. );
  387. if (version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>=')) {
  388. $this->addRequirement(
  389. (in_array(date_default_timezone_get(), DateTimeZone::listIdentifiers())),
  390. sprintf('Default timezone "%s" is not supported by your installation of PHP', date_default_timezone_get()),
  391. 'Fix your <strong>php.ini</strong> file (check for typos and have a look at the list of deprecated timezones http://php.net/manual/en/timezones.others.php).'
  392. );
  393. }
  394. $this->addRequirement(
  395. function_exists('json_encode'),
  396. 'json_encode() must be available',
  397. 'Install and enable the <strong>JSON</strong> extension.'
  398. );
  399. $this->addRequirement(
  400. function_exists('session_start'),
  401. 'session_start() must be available',
  402. 'Install and enable the <strong>session</strong> extension.'
  403. );
  404. $this->addRequirement(
  405. function_exists('ctype_alpha'),
  406. 'ctype_alpha() must be available',
  407. 'Install and enable the <strong>ctype</strong> extension.'
  408. );
  409. $this->addRequirement(
  410. function_exists('token_get_all'),
  411. 'token_get_all() must be available',
  412. 'Install and enable the <strong>Tokenizer</strong> extension.'
  413. );
  414. $this->addRequirement(
  415. function_exists('simplexml_import_dom'),
  416. 'simplexml_import_dom() must be available',
  417. 'Install and enable the <strong>SimpleXML</strong> extension.'
  418. );
  419. $this->addRequirement(
  420. !(function_exists('apc_store') && ini_get('apc.enabled')) || version_compare(phpversion('apc'), '3.0.17', '>='),
  421. 'APC version must be at least 3.0.17',
  422. 'Upgrade your <strong>APC</strong> extension (3.0.17+)'
  423. );
  424. $this->addPhpIniRequirement('detect_unicode', false);
  425. $this->addPhpIniRequirement(
  426. 'suhosin.executor.include.whitelist',
  427. create_function('$cfgValue', 'return false !== stripos($cfgValue, "phar");'),
  428. true,
  429. 'suhosin.executor.include.whitelist must be configured correctly in php.ini',
  430. 'Add "<strong>phar</strong>" to <strong>suhosin.executor.include.whitelist</strong> in php.ini<a href="#phpini">*</a>.'
  431. );
  432. $pcreVersion = defined('PCRE_VERSION') ? (float) PCRE_VERSION : null;
  433. $this->addRequirement(
  434. null !== $pcreVersion && $pcreVersion > 8.0,
  435. sprintf('PCRE extension must be available and at least 8.0 (%s installed)', $pcreVersion ? $pcreVersion : 'not'),
  436. 'Upgrade your <strong>PCRE</strong> extension (8.0+)'
  437. );
  438. /* optional recommendations follow */
  439. $this->addRecommendation(
  440. version_compare($installedPhpVersion, '5.3.4', '>='),
  441. sprintf('Your project might not work properly ("Notice: Trying to get property of non-object") due to the PHP bug #52083 before PHP 5.3.4 (%s installed)', $installedPhpVersion),
  442. 'Install PHP 5.3.4 or newer'
  443. );
  444. $this->addRecommendation(
  445. version_compare($installedPhpVersion, '5.4.0', '!='),
  446. 'Your project might not work properly ("Cannot dump definitions which have method calls") due to the PHP bug #61453 in PHP 5.4.0',
  447. 'Install PHP 5.4.1 or newer'
  448. );
  449. $this->addRecommendation(
  450. version_compare($installedPhpVersion, '5.3.8', '>='),
  451. sprintf('Annotations might not work properly due to the PHP bug #55156 before PHP 5.3.8 (%s installed)', $installedPhpVersion),
  452. 'Install PHP 5.3.8 or newer if your project uses annotations'
  453. );
  454. $this->addRecommendation(
  455. class_exists('DomDocument'),
  456. 'PHP-XML module should be installed',
  457. 'Install and enable the <strong>PHP-XML</strong> module.'
  458. );
  459. $this->addRecommendation(
  460. function_exists('mb_strlen'),
  461. 'mb_strlen() should be available',
  462. 'Install and enable the <strong>mbstring</strong> extension.'
  463. );
  464. $this->addRecommendation(
  465. function_exists('iconv'),
  466. 'iconv() should be available',
  467. 'Install and enable the <strong>iconv</strong> extension.'
  468. );
  469. $this->addRecommendation(
  470. function_exists('utf8_decode'),
  471. 'utf8_decode() should be available',
  472. 'Install and enable the <strong>XML</strong> extension.'
  473. );
  474. if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
  475. $this->addRecommendation(
  476. function_exists('posix_isatty'),
  477. 'posix_isatty() should be available',
  478. 'Install and enable the <strong>php_posix</strong> extension (used to colorize the CLI output).'
  479. );
  480. }
  481. $this->addRecommendation(
  482. class_exists('Locale'),
  483. 'intl extension should be available',
  484. 'Install and enable the <strong>intl</strong> extension (used for validators).'
  485. );
  486. if (class_exists('Locale')) {
  487. if (defined('INTL_ICU_VERSION')) {
  488. $version = INTL_ICU_VERSION;
  489. } else {
  490. $reflector = new ReflectionExtension('intl');
  491. ob_start();
  492. $reflector->info();
  493. $output = strip_tags(ob_get_clean());
  494. preg_match('/^ICU version +(?:=> )?(.*)$/m', $output, $matches);
  495. $version = $matches[1];
  496. }
  497. $this->addRecommendation(
  498. version_compare($version, '4.0', '>='),
  499. 'intl ICU version should be at least 4+',
  500. 'Upgrade your <strong>intl</strong> extension with a newer ICU version (4+).'
  501. );
  502. }
  503. $accelerator =
  504. (function_exists('apc_store') && ini_get('apc.enabled'))
  505. ||
  506. function_exists('eaccelerator_put') && ini_get('eaccelerator.enable')
  507. ||
  508. function_exists('xcache_set')
  509. ;
  510. $this->addRecommendation(
  511. $accelerator,
  512. 'a PHP accelerator should be installed',
  513. 'Install and enable a <strong>PHP accelerator</strong> like APC (highly recommended).'
  514. );
  515. $this->addPhpIniRecommendation('short_open_tag', false);
  516. $this->addPhpIniRecommendation('magic_quotes_gpc', false, true);
  517. $this->addPhpIniRecommendation('register_globals', false, true);
  518. $this->addPhpIniRecommendation('session.auto_start', false);
  519. $this->addRecommendation(
  520. class_exists('PDO'),
  521. 'PDO should be installed',
  522. 'Install <strong>PDO</strong> (mandatory for Doctrine).'
  523. );
  524. if (class_exists('PDO')) {
  525. $drivers = PDO::getAvailableDrivers();
  526. $this->addRecommendation(
  527. count($drivers),
  528. sprintf('PDO should have some drivers installed (currently available: %s)', count($drivers) ? implode(', ', $drivers) : 'none'),
  529. 'Install <strong>PDO drivers</strong> (mandatory for Doctrine).'
  530. );
  531. }
  532. }
  533. }