addChamiloRequirement( version_compare($phpVersion, self::REQUIRED_PHP_VERSION, '>='), sprintf('PHP version must be at least %s (%s installed)', self::REQUIRED_PHP_VERSION, $phpVersion), sprintf( 'You are running PHP version "%s", but Chamilo needs at least PHP "%s" to run.' . 'Before using Chamilo, upgrade your PHP installation, preferably to the latest version.', $phpVersion, self::REQUIRED_PHP_VERSION ), sprintf('Install PHP %s or newer (installed version is %s)', self::REQUIRED_PHP_VERSION, $phpVersion) ); $this->addChamiloRequirement( null !== $gdVersion && version_compare($gdVersion, self::REQUIRED_GD_VERSION, '>='), 'GD extension must be at least ' . self::REQUIRED_GD_VERSION, 'Install and enable the GD extension at least ' . self::REQUIRED_GD_VERSION . ' version' ); $this->addChamiloRequirement( null !== $curlVersion && version_compare($curlVersion['version'], self::REQUIRED_CURL_VERSION, '>='), 'cURL extension must be at least ' . self::REQUIRED_CURL_VERSION, 'Install and enable the cURL extension at least ' . self::REQUIRED_CURL_VERSION . ' version' ); $this->addChamiloRequirement( function_exists('mb_strlen'), 'mb_strlen() should be available', 'Install and enable the mbstring extension.' ); $this->addChamiloRequirement( function_exists('mcrypt_encrypt'), 'mcrypt_encrypt() should be available', 'Install and enable the Mcrypt extension.' ); $this->addChamiloRequirement( class_exists('Locale'), 'intl extension should be available', 'Install and enable the intl extension.' ); $this->addChamiloRequirement( null !== $icuVersion && version_compare($icuVersion, self::REQUIRED_ICU_VERSION, '>='), 'icu library must be at least ' . self::REQUIRED_ICU_VERSION, 'Install and enable the icu library at least ' . self::REQUIRED_ICU_VERSION . ' version' ); $extensions = $this->getExtensions(); foreach ($extensions as $type) { $isOptional = $type == 'optional' ? true : false; foreach ($type as $extension => $url) { if (extension_loaded($extension)) { $this->addChamiloRequirement( extension_loaded($extension), "$extension extension should be available", "Install and enable the $extension extension.", $isOptional ); } } } $this->addRecommendation( class_exists('SoapClient'), 'SOAP extension should be installed (API calls)', 'Install and enable the SOAP extension.' ); // Windows specific checks if (defined('PHP_WINDOWS_VERSION_BUILD')) { $this->addRecommendation( function_exists('finfo_open'), 'finfo_open() should be available', 'Install and enable the Fileinfo extension.' ); $this->addRecommendation( class_exists('COM'), 'COM extension should be installed', 'Install and enable the COM extension.' ); } // Unix specific checks if (!defined('PHP_WINDOWS_VERSION_BUILD')) { $this->addRequirement( $this->checkFileNameLength(), 'Cache folder should not be inside encrypted directory', 'Move app/cache folder outside encrypted directory.' ); } // Web installer specific checks if ('cli' !== PHP_SAPI) { $output = $this->checkCliRequirements(); $requirement = new CliRequirement( !$output, 'Requirements validation for PHP CLI', 'If you have multiple PHP versions installed, you need to configure CHAMILO_PHP_PATH variable with PHP binary path used by web server' ); $requirement->setOutput($output); $this->add($requirement); } $baseDir = realpath(__DIR__ . '/..'); $mem = $this->getBytes(ini_get('memory_limit')); $this->addPhpIniRequirement( 'memory_limit', function ($cfgValue) use ($mem) { return $mem >= 256 * 1024 * 1024 || -1 == $mem; }, false, 'memory_limit should be at least 256M', 'Set the "memory_limit" setting in php.ini* to at least "256M".' ); $this->addChamiloRequirement( is_writable($baseDir . '/web/uploads'), 'web/uploads/ directory must be writable', 'Change the permissions of the "web/uploads/" directory so that the web server can write into it.' ); $this->addChamiloRequirement( is_writable($baseDir . '/web/assetic'), 'web/assetic/ directory must be writable', 'Change the permissions of the "web/assetic/" directory so that the web server can write into it.' ); $this->addChamiloRequirement( is_writable($baseDir . '/web/bundles'), 'web/bundles/ directory must be writable', 'Change the permissions of the "web/bundles/" directory so that the web server can write into it.' ); /*$this->addChamiloRequirement( is_writable($baseDir . '/web/media'), 'web/media/ directory must be writable', 'Change the permissions of the "web/media/" directory so that the web server can write into it.' ); /*$this->addChamiloRequirement( is_writable($baseDir . '/app/attachment'), 'app/attachment/ directory must be writable', 'Change the permissions of the "app/attachment/" directory so that the web server can write into it.' );*/ if (is_dir($baseDir . '/web/js')) { $this->addChamiloRequirement( is_writable($baseDir . '/web/js'), 'web/js directory must be writable', 'Change the permissions of the "web/js" directory so that the web server can write into it.' ); } if (is_dir($baseDir . '/web/css')) { $this->addChamiloRequirement( is_writable($baseDir . '/web/css'), 'web/css directory must be writable', 'Change the permissions of the "web/css" directory so that the web server can write into it.' ); } if (!is_dir($baseDir . '/web/css') || !is_dir($baseDir . '/web/js')) { $this->addChamiloRequirement( is_writable($baseDir . '/web'), 'web directory must be writable', 'Change the permissions of the "web" directory so that the web server can write into it.' ); } if (is_file($baseDir . '/app/config/parameters.yml')) { $this->addChamiloRequirement( is_writable($baseDir . '/app/config/parameters.yml'), 'app/config/parameters.yml file must be writable', 'Change the permissions of the "app/config/parameters.yml" file so that the web server can write into it.' ); } } /** * @return array */ private function getExtensions() { return array( 'required' => array( 'mysql' => array('url' => 'http://php.net/manual/en/book.mysql.php'), 'curl' => array('url' => 'http://php.net/manual/fr/book.curl.php'), 'zlib' => array('url' => 'http://php.net/manual/en/book.zlib.php'), 'pcre' => array('url' => 'http://php.net/manual/en/book.pcre.php'), 'xml' => array('url' => 'http://php.net/manual/en/book.xml.php'), 'mbstring' => array('url' => 'http://php.net/manual/en/book.mbstring.php'), 'iconv' => array('url' => 'http://php.net/manual/en/book.iconv.php'), 'intl' => array('url' => 'http://php.net/manual/en/book.intl.php'), 'gd' => array('url' => 'http://php.net/manual/en/book.image.php'), 'json' => array('url' => 'http://php.net/manual/en/book.json.php') ), 'optional' => array( 'imagick' => array('url' => 'http://php.net/manual/en/book.imagick.php'), 'ldap' => array('url' => 'http://php.net/manual/en/book.ldap.php'), 'xapian' => array('url' => 'http://php.net/manual/en/book.xapian.php') ) ); } /** * Adds an Chamilo specific requirement. * * @param boolean $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 bool $optional */ public function addChamiloRequirement( $fulfilled, $testMessage, $helpHtml, $helpText = null, $optional = false ) { $this->add( new ChamiloRequirement( $fulfilled, $testMessage, $helpHtml, $helpText, $optional ) ); } /** * Get the list of mandatory requirements (all requirements excluding PhpIniRequirement) * * @return array */ public function getMandatoryRequirements() { return array_filter( $this->getRequirements(), function ($requirement) { return !($requirement instanceof PhpIniRequirement) && !($requirement instanceof ChamiloRequirement) && !($requirement instanceof CliRequirement); } ); } /** * Get the list of PHP ini requirements * * @return array */ public function getPhpIniRequirements() { return array_filter( $this->getRequirements(), function ($requirement) { return $requirement instanceof PhpIniRequirement; } ); } /** * Get the list of Chamilo specific requirements * * @return array */ public function getChamiloRequirements() { return array_filter( $this->getRequirements(), function ($requirement) { return $requirement instanceof ChamiloRequirement; } ); } /** * @return array */ public function getCliRequirements() { return array_filter( $this->getRequirements(), function ($requirement) { return $requirement instanceof CliRequirement; } ); } /** * @param string $val * @return int */ protected function getBytes($val) { if (empty($val)) { return 0; } preg_match('/([\-0-9]+)[\s]*([a-z]*)$/i', trim($val), $matches); if (isset($matches[1])) { $val = (int)$matches[1]; } switch (strtolower($matches[2])) { case 'g': case 'gb': $val *= 1024; // no break case 'm': case 'mb': $val *= 1024; // no break case 'k': case 'kb': $val *= 1024; // no break } return (float)$val; } /** * {@inheritdoc} */ public function getRequirements() { $requirements = parent::getRequirements(); foreach ($requirements as $key => $requirement) { $testMessage = $requirement->getTestMessage(); if (preg_match_all( self::EXCLUDE_REQUIREMENTS_MASK, $testMessage, $matches )) { unset($requirements[$key]); } } return $requirements; } /** * {@inheritdoc} */ public function getRecommendations() { $recommendations = parent::getRecommendations(); foreach ($recommendations as $key => $recommendation) { $testMessage = $recommendation->getTestMessage(); if (preg_match_all(self::EXCLUDE_REQUIREMENTS_MASK, $testMessage, $matches)) { unset($recommendations[$key]); } } return $recommendations; } /** * @return bool */ protected function checkNodeExists() { $nodeExists = new ProcessBuilder(array('node', '--version')); $nodeExists = $nodeExists->getProcess(); if (isset($_SERVER['PATH'])) { $nodeExists->setEnv(array('PATH' => $_SERVER['PATH'])); } $nodeExists->run(); return $nodeExists->getErrorOutput() === null; } /** * @return bool */ protected function checkFileNameLength() { $getConf = new ProcessBuilder(array('getconf', 'NAME_MAX', __DIR__)); $getConf = $getConf->getProcess(); if (isset($_SERVER['PATH'])) { $getConf->setEnv(array('PATH' => $_SERVER['PATH'])); } $getConf->run(); if ($getConf->getErrorOutput()) { // getconf not installed return true; } $fileLength = trim($getConf->getOutput()); return $fileLength == 255; } /** * @return null|string */ protected function checkCliRequirements() { $finder = new PhpExecutableFinder(); $command = sprintf( '%s %schamilo-check.php', $finder->find(), __DIR__ . DIRECTORY_SEPARATOR ); return shell_exec($command); } } class ChamiloRequirement extends Requirement { } class CliRequirement extends Requirement { /** * @var string */ protected $output; /** * @return string */ public function getOutput() { return $this->output; } /** * @param string $output */ public function setOutput($output) { $this->output = $output; } }