diff --git a/app/SymfonyRequirements.php b/app/SymfonyRequirements.php index fb70f803fd..c4894c057d 100644 --- a/app/SymfonyRequirements.php +++ b/app/SymfonyRequirements.php @@ -41,25 +41,25 @@ class Requirement /** * Constructor that initializes the requirement. * - * @param Boolean $fulfilled Whether the requirement is fulfilled + * @param bool $fulfilled Whether the requirement is fulfilled * @param string $testMessage The message for testing the requirement * @param string $helpHtml The help text formatted in HTML for resolving the problem * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) - * @param Boolean $optional Whether this is only an optional recommendation not a mandatory requirement + * @param bool $optional Whether this is only an optional recommendation not a mandatory requirement */ public function __construct($fulfilled, $testMessage, $helpHtml, $helpText = null, $optional = false) { - $this->fulfilled = (Boolean) $fulfilled; + $this->fulfilled = (bool) $fulfilled; $this->testMessage = (string) $testMessage; $this->helpHtml = (string) $helpHtml; $this->helpText = null === $helpText ? strip_tags($this->helpHtml) : (string) $helpText; - $this->optional = (Boolean) $optional; + $this->optional = (bool) $optional; } /** * Returns whether the requirement is fulfilled. * - * @return Boolean true if fulfilled, otherwise false + * @return bool true if fulfilled, otherwise false */ public function isFulfilled() { @@ -99,7 +99,7 @@ class Requirement /** * Returns whether this is only an optional recommendation and not a mandatory requirement. * - * @return Boolean true if optional, false if mandatory + * @return bool true if optional, false if mandatory */ public function isOptional() { @@ -117,16 +117,16 @@ class PhpIniRequirement extends Requirement /** * Constructor that initializes the requirement. * - * @param string $cfgName The configuration name used for ini_get() - * @param Boolean|callback $evaluation Either a Boolean indicating whether the configuration should evaluate to true or false, + * @param string $cfgName The configuration name used for ini_get() + * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement - * @param Boolean $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. + * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. Example: You require a config to be true but PHP later removes this config and defaults it to true internally. - * @param string|null $testMessage The message for testing the requirement (when null and $evaluation is a Boolean a default message is derived) - * @param string|null $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a Boolean a default help is derived) - * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) - * @param Boolean $optional Whether this is only an optional recommendation not a mandatory requirement + * @param string|null $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) + * @param string|null $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param bool $optional Whether this is only an optional recommendation not a mandatory requirement */ public function __construct($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null, $optional = false) { @@ -193,7 +193,7 @@ class RequirementCollection implements IteratorAggregate /** * Adds a mandatory requirement. * - * @param Boolean $fulfilled Whether the requirement is fulfilled + * @param bool $fulfilled Whether the requirement is fulfilled * @param string $testMessage The message for testing the requirement * @param string $helpHtml The help text formatted in HTML for resolving the problem * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) @@ -206,7 +206,7 @@ class RequirementCollection implements IteratorAggregate /** * Adds an optional recommendation. * - * @param Boolean $fulfilled Whether the recommendation is fulfilled + * @param bool $fulfilled Whether the recommendation is fulfilled * @param string $testMessage The message for testing the recommendation * @param string $helpHtml The help text formatted in HTML for resolving the problem * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) @@ -219,15 +219,15 @@ class RequirementCollection implements IteratorAggregate /** * Adds a mandatory requirement in form of a php.ini configuration. * - * @param string $cfgName The configuration name used for ini_get() - * @param Boolean|callback $evaluation Either a Boolean indicating whether the configuration should evaluate to true or false, + * @param string $cfgName The configuration name used for ini_get() + * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement - * @param Boolean $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. + * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. Example: You require a config to be true but PHP later removes this config and defaults it to true internally. - * @param string $testMessage The message for testing the requirement (when null and $evaluation is a Boolean a default message is derived) - * @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) - * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) + * @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) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) */ public function addPhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null) { @@ -237,15 +237,15 @@ class RequirementCollection implements IteratorAggregate /** * Adds an optional recommendation in form of a php.ini configuration. * - * @param string $cfgName The configuration name used for ini_get() - * @param Boolean|callback $evaluation Either a Boolean indicating whether the configuration should evaluate to true or false, + * @param string $cfgName The configuration name used for ini_get() + * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement - * @param Boolean $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. + * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. Example: You require a config to be true but PHP later removes this config and defaults it to true internally. - * @param string $testMessage The message for testing the requirement (when null and $evaluation is a Boolean a default message is derived) - * @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) - * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) + * @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) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) */ public function addPhpIniRecommendation($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null) { @@ -343,7 +343,7 @@ class RequirementCollection implements IteratorAggregate /** * Returns whether a php.ini configuration is not correct. * - * @return Boolean php.ini configuration problem? + * @return bool php.ini configuration problem? */ public function hasPhpIniConfigIssue() { @@ -405,22 +405,24 @@ class SymfonyRequirements extends RequirementCollection $this->addRequirement( is_dir(__DIR__.'/../vendor/composer'), 'Vendor libraries must be installed', - 'Vendor libraries are missing. Install composer following instructions from http://getcomposer.org/. ' . + 'Vendor libraries are missing. Install composer following instructions from http://getcomposer.org/. '. 'Then run "php composer.phar install" to install them.' ); - $baseDir = basename(__DIR__); + $cacheDir = is_dir(__DIR__.'/../var/cache') ? __DIR__.'/../var/cache' : __DIR__.'/cache'; $this->addRequirement( - is_writable(__DIR__.'/cache'), - "$baseDir/cache/ directory must be writable", - "Change the permissions of the \"$baseDir/cache/\" directory so that the web server can write into it." + is_writable($cacheDir), + 'app/cache/ or var/cache/ directory must be writable', + 'Change the permissions of either "app/cache/" or "var/cache/" directory so that the web server can write into it.' ); + $logsDir = is_dir(__DIR__.'/../var/logs') ? __DIR__.'/../var/logs' : __DIR__.'/logs'; + $this->addRequirement( - is_writable(__DIR__.'/logs'), - "$baseDir/logs/ directory must be writable", - "Change the permissions of the \"$baseDir/logs/\" directory so that the web server can write into it." + is_writable($logsDir), + 'app/logs/ or var/logs/ directory must be writable', + 'Change the permissions of either "app/logs/" or "var/logs/" directory so that the web server can write into it.' ); $this->addPhpIniRequirement( @@ -438,8 +440,8 @@ class SymfonyRequirements extends RequirementCollection } $this->addRequirement( - isset($timezones[date_default_timezone_get()]), - sprintf('Configured default timezone "%s" must be supported by your installation of PHP', date_default_timezone_get()), + isset($timezones[@date_default_timezone_get()]), + sprintf('Configured default timezone "%s" must be supported by your installation of PHP', @date_default_timezone_get()), 'Your default timezone is not supported by PHP. Check for typos in your php.ini file and have a look at the list of deprecated timezones at http://php.net/manual/en/timezones.others.php.' ); } @@ -528,6 +530,16 @@ class SymfonyRequirements extends RequirementCollection 'Install the PCRE extension (version 8.0+).' ); + if (extension_loaded('mbstring')) { + $this->addPhpIniRequirement( + 'mbstring.func_overload', + create_function('$cfgValue', 'return (int) $cfgValue === 0;'), + true, + 'string functions should not be overloaded', + 'Set "mbstring.func_overload" to 0 in php.ini* to disable function overloading by the mbstring extension.' + ); + } + /* optional recommendations follow */ $this->addRecommendation( @@ -600,6 +612,12 @@ class SymfonyRequirements extends RequirementCollection 'Install and enable the XML extension.' ); + $this->addRecommendation( + function_exists('filter_var'), + 'filter_var() should be available', + 'Install and enable the filter extension.' + ); + if (!defined('PHP_WINDOWS_VERSION_BUILD')) { $this->addRecommendation( function_exists('posix_isatty'), @@ -648,6 +666,8 @@ class SymfonyRequirements extends RequirementCollection || (extension_loaded('apc') && ini_get('apc.enabled')) || + (extension_loaded('Zend Optimizer+') && ini_get('zend_optimizerplus.enable')) + || (extension_loaded('Zend OPcache') && ini_get('opcache.enable')) || (extension_loaded('xcache') && ini_get('xcache.cacher')) @@ -658,9 +678,19 @@ class SymfonyRequirements extends RequirementCollection $this->addRecommendation( $accelerator, 'a PHP accelerator should be installed', - 'Install and enable a PHP accelerator like APC (highly recommended).' + 'Install and/or enable a PHP accelerator (highly recommended).' ); + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + $this->addPhpIniRecommendation( + 'realpath_cache_size', + create_function('$cfgValue', 'return (int) $cfgValue > 1000;'), + false, + 'realpath_cache_size should be above 1024 in php.ini', + 'Set "realpath_cache_size" to e.g. "1024" in php.ini* to improve performance on windows.' + ); + } + $this->addPhpIniRecommendation('short_open_tag', false); $this->addPhpIniRecommendation('magic_quotes_gpc', false, true); @@ -678,7 +708,7 @@ class SymfonyRequirements extends RequirementCollection if (class_exists('PDO')) { $drivers = PDO::getAvailableDrivers(); $this->addRecommendation( - count($drivers), + count($drivers) > 0, sprintf('PDO should have some drivers installed (currently available: %s)', count($drivers) ? implode(', ', $drivers) : 'none'), 'Install PDO drivers (mandatory for Doctrine).' ); diff --git a/app/bootstrap.php.cache b/app/bootstrap.php.cache index 56e6ada340..31e6ec5f23 100644 --- a/app/bootstrap.php.cache +++ b/app/bootstrap.php.cache @@ -1,6 +1,6 @@ get($key, $default, $deep); } +public function getBoolean($key, $default = false, $deep = false) +{ +return $this->filter($key, $default, $deep, FILTER_VALIDATE_BOOLEAN); +} public function filter($key, $default = null, $deep = false, $filter = FILTER_DEFAULT, $options = array()) { $value = $this->get($key, $default, $deep); @@ -396,6 +400,16 @@ const HEADER_CLIENT_IP ='client_ip'; const HEADER_CLIENT_HOST ='client_host'; const HEADER_CLIENT_PROTO ='client_proto'; const HEADER_CLIENT_PORT ='client_port'; +const METHOD_HEAD ='HEAD'; +const METHOD_GET ='GET'; +const METHOD_POST ='POST'; +const METHOD_PUT ='PUT'; +const METHOD_PATCH ='PATCH'; +const METHOD_DELETE ='DELETE'; +const METHOD_PURGE ='PURGE'; +const METHOD_OPTIONS ='OPTIONS'; +const METHOD_TRACE ='TRACE'; +const METHOD_CONNECT ='CONNECT'; protected static $trustedProxies = array(); protected static $trustedHostPatterns = array(); protected static $trustedHosts = array(); @@ -678,7 +692,16 @@ return self::$httpMethodParameterOverride; } public function get($key, $default = null, $deep = false) { -return $this->query->get($key, $this->attributes->get($key, $this->request->get($key, $default, $deep), $deep), $deep); +if ($this !== $result = $this->query->get($key, $this, $deep)) { +return $result; +} +if ($this !== $result = $this->attributes->get($key, $this, $deep)) { +return $result; +} +if ($this !== $result = $this->request->get($key, $this, $deep)) { +return $result; +} +return $default; } public function getSession() { @@ -1161,7 +1184,7 @@ return (string) $pathInfo; } protected static function initializeFormats() { -static::$formats = array('html'=> array('text/html','application/xhtml+xml'),'txt'=> array('text/plain'),'js'=> array('application/javascript','application/x-javascript','text/javascript'),'css'=> array('text/css'),'json'=> array('application/json','application/x-json'),'xml'=> array('text/xml','application/xml','application/x-xml'),'rdf'=> array('application/rdf+xml'),'atom'=> array('application/atom+xml'),'rss'=> array('application/rss+xml'), +static::$formats = array('html'=> array('text/html','application/xhtml+xml'),'txt'=> array('text/plain'),'js'=> array('application/javascript','application/x-javascript','text/javascript'),'css'=> array('text/css'),'json'=> array('application/json','application/x-json'),'xml'=> array('text/xml','application/xml','application/x-xml'),'rdf'=> array('application/rdf+xml'),'atom'=> array('application/atom+xml'),'rss'=> array('application/rss+xml'),'form'=> array('application/x-www-form-urlencoded'), ); } private function setPhpDefaultLocale($locale) @@ -1845,9 +1868,9 @@ $flattenedCookies[] = $cookie; } return $flattenedCookies; } -public function clearCookie($name, $path ='/', $domain = null) +public function clearCookie($name, $path ='/', $domain = null, $secure = false, $httpOnly = true) { -$this->setCookie(new Cookie($name, null, 1, $path, $domain)); +$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly)); } public function makeDisposition($disposition, $filename, $filenameFallback ='') { @@ -2000,10 +2023,10 @@ $this->services[$id] = $service; if (method_exists($this, $method ='synchronize'.strtr($id, array('_'=>'','.'=>'_','\\'=>'_')).'Service')) { $this->$method(); } -if (self::SCOPE_CONTAINER !== $scope && null === $service) { +if (null === $service) { +if (self::SCOPE_CONTAINER !== $scope) { unset($this->scopedServices[$scope][$id]); } -if (null === $service) { unset($this->services[$id]); } } @@ -2079,6 +2102,9 @@ $id = strtolower($id); if ('service_container'=== $id) { return true; } +if (isset($this->aliases[$id])) { +$id = $this->aliases[$id]; +} return isset($this->services[$id]) || array_key_exists($id, $this->services); } public function getServiceIds() @@ -2127,16 +2153,18 @@ throw new InvalidArgumentException(sprintf('The scope "%s" is not active.', $nam $services = array($this->services, $this->scopedServices[$name]); unset($this->scopedServices[$name]); foreach ($this->scopeChildren[$name] as $child) { -if (!isset($this->scopedServices[$child])) { -continue; -} +if (isset($this->scopedServices[$child])) { $services[] = $this->scopedServices[$child]; unset($this->scopedServices[$child]); } +} $this->services = call_user_func_array('array_diff_key', $services); if (isset($this->scopeStacks[$name]) && count($this->scopeStacks[$name]) > 0) { $services = $this->scopeStacks[$name]->pop(); $this->scopedServices += $services; +if ($this->scopeStacks[$name]->isEmpty()) { +unset($this->scopeStacks[$name]); +} foreach ($services as $array) { foreach ($array as $id => $service) { $this->set($id, $service, $name); @@ -2263,11 +2291,11 @@ protected $booted = false; protected $name; protected $startTime; protected $loadClassCache; -const VERSION ='2.5.7'; -const VERSION_ID ='20507'; +const VERSION ='2.6.1'; +const VERSION_ID ='20601'; const MAJOR_VERSION ='2'; -const MINOR_VERSION ='5'; -const RELEASE_VERSION ='7'; +const MINOR_VERSION ='6'; +const RELEASE_VERSION ='1'; const EXTRA_VERSION =''; public function __construct($environment, $debug) { @@ -2605,7 +2633,7 @@ $dumper = new PhpDumper($container); if (class_exists('ProxyManager\Configuration')) { $dumper->setProxyDumper(new ProxyDumper()); } -$content = $dumper->dump(array('class'=> $class,'base_class'=> $baseClass)); +$content = $dumper->dump(array('class'=> $class,'base_class'=> $baseClass,'file'=> (string) $cache)); if (!$this->debug) { $content = static::stripComments($content); } @@ -2769,13 +2797,13 @@ public function build(ContainerBuilder $container) public function getContainerExtension() { if (null === $this->extension) { -$basename = preg_replace('/Bundle$/','', $this->getName()); -$class = $this->getNamespace().'\\DependencyInjection\\'.$basename.'Extension'; +$class = $this->getContainerExtensionClass(); if (class_exists($class)) { $extension = new $class(); +$basename = preg_replace('/Bundle$/','', $this->getName()); $expectedAlias = Container::underscore($basename); if ($expectedAlias != $extension->getAlias()) { -throw new \LogicException(sprintf('The extension alias for the default extension of a '.'bundle must be the underscored version of the '.'bundle name ("%s" instead of "%s")', +throw new \LogicException(sprintf('Users will expect the alias of the default extension of a bundle to be the underscored version of the bundle name ("%s"). You can override "Bundle::getContainerExtension()" if you want to use "%s" or another alias.', $expectedAlias, $extension->getAlias() )); } @@ -2839,6 +2867,11 @@ $application->add($r->newInstance()); } } } +protected function getContainerExtensionClass() +{ +$basename = preg_replace('/Bundle$/','', $this->getName()); +return $this->getNamespace().'\\DependencyInjection\\'.$basename.'Extension'; +} } } namespace Symfony\Component\Config @@ -2966,7 +2999,7 @@ if ($event->hasResponse()) { return $this->filterResponse($event->getResponse(), $request, $type); } if (false === $controller = $this->resolver->getController($request)) { -throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s". Maybe you forgot to add the matching route in your routing configuration?', $request->getPathInfo())); +throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s". The route is wrongly configured.', $request->getPathInfo())); } $event = new FilterControllerEvent($this, $controller, $request, $type); $this->dispatcher->dispatch(KernelEvents::CONTROLLER, $event); diff --git a/app/check.php b/app/check.php index 91b826befe..90bad4a718 100644 --- a/app/check.php +++ b/app/check.php @@ -2,61 +2,141 @@ require_once dirname(__FILE__).'/SymfonyRequirements.php'; +$lineSize = 70; $symfonyRequirements = new SymfonyRequirements(); - $iniPath = $symfonyRequirements->getPhpIniConfigPath(); -echo "********************************\n"; -echo "* *\n"; -echo "* Symfony requirements check *\n"; -echo "* *\n"; -echo "********************************\n\n"; - -echo $iniPath ? sprintf("* Configuration file used by PHP: %s\n\n", $iniPath) : "* WARNING: No configuration file (php.ini) used by PHP!\n\n"; +echo_title('Symfony2 Requirements Checker'); -echo "** ATTENTION **\n"; -echo "* The PHP CLI can use a different php.ini file\n"; -echo "* than the one used with your web server.\n"; -if ('\\' == DIRECTORY_SEPARATOR) { - echo "* (especially on the Windows platform)\n"; +echo '> PHP is using the following php.ini file:'.PHP_EOL; +if ($iniPath) { + echo_style('green', ' '.$iniPath); +} else { + echo_style('warning', ' WARNING: No configuration file (php.ini) used by PHP!'); } -echo "* To be on the safe side, please also launch the requirements check\n"; -echo "* from your web server using the web/config.php script.\n"; -echo_title('Mandatory requirements'); +echo PHP_EOL.PHP_EOL; + +echo '> Checking Symfony requirements:'.PHP_EOL.' '; -$checkPassed = true; +$messages = array(); foreach ($symfonyRequirements->getRequirements() as $req) { /** @var $req Requirement */ - echo_requirement($req); - if (!$req->isFulfilled()) { - $checkPassed = false; + if ($helpText = get_error_message($req, $lineSize)) { + echo_style('red', 'E'); + $messages['error'][] = $helpText; + } else { + echo_style('green', '.'); } } -echo_title('Optional recommendations'); +$checkPassed = empty($messages['error']); foreach ($symfonyRequirements->getRecommendations() as $req) { - echo_requirement($req); + if ($helpText = get_error_message($req, $lineSize)) { + echo_style('yellow', 'W'); + $messages['warning'][] = $helpText; + } else { + echo_style('green', '.'); + } +} + +if ($checkPassed) { + echo_block('success', 'OK', 'Your system is ready to run Symfony2 projects', true); +} else { + echo_block('error', 'ERROR', 'Your system is not ready to run Symfony2 projects', true); + + echo_title('Fix the following mandatory requirements', 'red'); + + foreach ($messages['error'] as $helpText) { + echo ' * '.$helpText.PHP_EOL; + } } +if (!empty($messages['warning'])) { + echo_title('Optional recommendations to improve your setup', 'yellow'); + + foreach ($messages['warning'] as $helpText) { + echo ' * '.$helpText.PHP_EOL; + } +} + +echo PHP_EOL; +echo_style('title', 'Note'); +echo ' The command console could use a different php.ini file'.PHP_EOL; +echo_style('title', '~~~~'); +echo ' than the one used with your web server. To be on the'.PHP_EOL; +echo ' safe side, please check the requirements from your web'.PHP_EOL; +echo ' server using the '; +echo_style('yellow', 'web/config.php'); +echo ' script.'.PHP_EOL; +echo PHP_EOL; + exit($checkPassed ? 0 : 1); -/** - * Prints a Requirement instance - */ -function echo_requirement(Requirement $requirement) +function get_error_message(Requirement $requirement, $lineSize) { - $result = $requirement->isFulfilled() ? 'OK' : ($requirement->isOptional() ? 'WARNING' : 'ERROR'); - echo ' ' . str_pad($result, 9); - echo $requirement->getTestMessage() . "\n"; - - if (!$requirement->isFulfilled()) { - echo sprintf(" %s\n\n", $requirement->getHelpText()); + if ($requirement->isFulfilled()) { + return; } + + $errorMessage = wordwrap($requirement->getTestMessage(), $lineSize - 3, PHP_EOL.' ').PHP_EOL; + $errorMessage .= ' > '.wordwrap($requirement->getHelpText(), $lineSize - 5, PHP_EOL.' > ').PHP_EOL; + + return $errorMessage; } -function echo_title($title) +function echo_title($title, $style = null) { - echo "\n** $title **\n\n"; + $style = $style ?: 'title'; + + echo PHP_EOL; + echo_style($style, $title.PHP_EOL); + echo_style($style, str_repeat('~', strlen($title)).PHP_EOL); + echo PHP_EOL; +} + +function echo_style($style, $message) +{ + // ANSI color codes + $styles = array( + 'reset' => "\033[0m", + 'red' => "\033[31m", + 'green' => "\033[32m", + 'yellow' => "\033[33m", + 'error' => "\033[37;41m", + 'success' => "\033[37;42m", + 'title' => "\033[34m", + ); + $supports = has_color_support(); + + echo($supports ? $styles[$style] : '').$message.($supports ? $styles['reset'] : ''); +} + +function echo_block($style, $title, $message) +{ + $message = ' '.trim($message).' '; + $width = strlen($message); + + echo PHP_EOL.PHP_EOL; + + echo_style($style, str_repeat(' ', $width).PHP_EOL); + echo_style($style, str_pad(' ['.$title.']', $width, ' ', STR_PAD_RIGHT).PHP_EOL); + echo_style($style, str_pad($message, $width, ' ', STR_PAD_RIGHT).PHP_EOL); + echo_style($style, str_repeat(' ', $width).PHP_EOL); +} + +function has_color_support() +{ + static $support; + + if (null === $support) { + if (DIRECTORY_SEPARATOR == '\\') { + $support = false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI'); + } else { + $support = function_exists('posix_isatty') && @posix_isatty(STDOUT); + } + } + + return $support; }