3 namespace Civi\Install
;
7 * @package Civi\Install
12 * Requirement severity -- Requirement successfully met.
14 const REQUIREMENT_OK
= 0;
17 * Requirement severity -- Warning condition; proceed but flag warning.
19 const REQUIREMENT_WARNING
= 1;
22 * Requirement severity -- Error condition; abort installation.
24 const REQUIREMENT_ERROR
= 2;
26 protected $system_checks = array(
28 'checkServerVariables',
29 'checkMysqlConnectExists',
30 'checkJsonEncodeExists',
33 protected $database_checks = array(
34 'checkMysqlConnection',
37 'checkMysqlTempTables',
38 'checkMySQLAutoIncrementIncrementOne',
40 'checkMysqlThreadStack',
41 'checkMysqlLockTables',
45 * Run all requirements tests.
47 * @param array $config
48 * An array with two keys:
53 * An array of check summaries. Each array contains the keys 'title', 'severity', and 'details'.
55 public function checkAll(array $config) {
56 return array_merge($this->checkSystem($config['file_paths']), $this->checkDatabase($config['db_config']));
60 * Check system requirements are met, such as sufficient memory,
61 * necessary file paths are writable and required php extensions
64 * @param array $file_paths
65 * An array of file paths that will be checked to confirm they
70 public function checkSystem(array $file_paths) {
73 $errors[] = $this->checkFilepathIsWritable($file_paths);
74 foreach ($this->system_checks
as $check) {
75 $errors[] = $this->$check();
82 * Check database connection, database version and other
83 * database requirements are met.
85 * @param array $db_config
87 * - host (with optional port specified eg. localhost:12345)
88 * - database (name of database to select)
94 public function checkDatabase(array $db_config) {
97 foreach ($this->database_checks
as $check) {
98 $errors[] = $this->$check($db_config);
105 * Generates a mysql connection
107 * @param $db_confic array
108 * @return object mysqli connection
110 protected function connect($db_config) {
112 if (!empty($db_config['host'])) {
113 $host = $db_config['host'];
115 elseif (!empty($db_config['server'])) {
116 $host = $db_config['server'];
118 $conn = @mysqli_connect
($host, $db_config['username'], $db_config['password'], $db_config['database'], !empty($db_config['port']) ?
$db_config['port'] : NULL);
123 * Check configured php Memory.
126 public function checkMemory() {
127 $min = 1024 * 1024 * 32;
128 $recommended = 1024 * 1024 * 64;
130 $mem = $this->getPHPMemory();
131 $mem_string = ini_get('memory_limit');
134 'title' => 'CiviCRM memory check',
135 'severity' => $this::REQUIREMENT_OK
,
136 'details' => "You have $mem_string allocated (minimum 32Mb, recommended 64Mb)",
139 if ($mem < $min && $mem > 0) {
140 $results['severity'] = $this::REQUIREMENT_ERROR
;
142 elseif ($mem < $recommended && $mem != 0) {
143 $results['severity'] = $this::REQUIREMENT_WARNING
;
146 $results['details'] = "Cannot determine PHP memory allocation. Install only if you're sure you've allocated at least 32 MB.";
147 $results['severity'] = $this::REQUIREMENT_WARNING
;
154 * Get Configured PHP memory.
157 protected function getPHPMemory() {
158 $memString = ini_get("memory_limit");
160 switch (strtolower(substr($memString, -1))) {
162 return round(substr($memString, 0, -1) * 1024);
165 return round(substr($memString, 0, -1) * 1024 * 1024);
168 return round(substr($memString, 0, -1) * 1024 * 1024 * 1024);
171 return round($memString);
178 public function checkServerVariables() {
180 'title' => 'CiviCRM PHP server variables',
181 'severity' => $this::REQUIREMENT_OK
,
182 'details' => 'The required $_SERVER variables are set',
185 $required_variables = array('SCRIPT_NAME', 'HTTP_HOST', 'SCRIPT_FILENAME');
188 foreach ($required_variables as $required_variable) {
189 if (empty($_SERVER[$required_variable])) {
190 $missing[] = '$_SERVER[' . $required_variable . ']';
195 $results['severity'] = $this::REQUIREMENT_ERROR
;
196 $results['details'] = 'The following PHP variables are not set: ' . implode(', ', $missing);
205 public function checkJsonEncodeExists() {
207 'title' => 'CiviCRM JSON encoding support',
208 'severity' => $this::REQUIREMENT_OK
,
209 'details' => 'Function json_encode() found',
211 if (!function_exists('json_encode')) {
212 $results['severity'] = $this::REQUIREMENT_ERROR
;
213 $results['details'] = 'Function json_encode() does not exist';
222 public function checkMysqlConnectExists() {
224 'title' => 'CiviCRM MySQL check',
225 'severity' => $this::REQUIREMENT_OK
,
226 'details' => 'Function mysqli_connect() found',
228 if (!function_exists('mysqli_connect')) {
229 $results['severity'] = $this::REQUIREMENT_ERROR
;
230 $results['details'] = 'Function mysqli_connect() does not exist';
237 * @param array $db_config
241 public function checkMysqlConnection(array $db_config) {
243 'title' => 'CiviCRM MySQL connection',
244 'severity' => $this::REQUIREMENT_OK
,
245 'details' => "Connected",
248 $conn = $this->connect($db_config);
251 $results['details'] = mysqli_connect_error();
252 $results['severity'] = $this::REQUIREMENT_ERROR
;
256 if (!@mysqli_select_db
($conn, $db_config['database'])) {
257 $results['details'] = mysqli_error($conn);
258 $results['severity'] = $this::REQUIREMENT_ERROR
;
266 * @param array $db_config
270 public function checkMysqlVersion(array $db_config) {
273 'title' => 'CiviCRM MySQL Version',
274 'severity' => $this::REQUIREMENT_OK
,
277 $conn = $this->connect($db_config);
278 if (!$conn ||
!($info = mysqli_get_server_info($conn))) {
279 $results['severity'] = $this::REQUIREMENT_WARNING
;
280 $results['details'] = "Cannot determine the version of MySQL installed. Please ensure at least version {$min} is installed.";
284 if (version_compare($info, $min) == -1) {
285 $results['severity'] = $this::REQUIREMENT_ERROR
;
286 $results['details'] = "MySQL version is {$info}; minimum required is {$min}";
290 $results['details'] = "MySQL version is {$info}";
295 * @param array $db_config
299 public function checkMysqlInnodb(array $db_config) {
301 'title' => 'CiviCRM InnoDB support',
302 'severity' => $this::REQUIREMENT_ERROR
,
303 'details' => 'Could not determine if MySQL has InnoDB support. Assuming none.',
306 $conn = $this->connect($db_config);
311 $innodb_support = FALSE;
312 $result = mysqli_query($conn, "SHOW ENGINES");
313 while ($values = mysqli_fetch_array($result)) {
314 if ($values['Engine'] == 'InnoDB') {
315 if (strtolower($values['Support']) == 'yes' ||
strtolower($values['Support']) == 'default') {
316 $innodb_support = TRUE;
322 if ($innodb_support) {
323 $results['severity'] = $this::REQUIREMENT_OK
;
324 $results['details'] = 'MySQL supports InnoDB';
330 * @param array $db_config
334 public function checkMysqlTempTables(array $db_config) {
336 'title' => 'CiviCRM MySQL Temp Tables',
337 'severity' => $this::REQUIREMENT_OK
,
338 'details' => 'MySQL server supports temporary tables',
341 $conn = $this->connect($db_config);
343 $results['severity'] = $this::REQUIREMENT_ERROR
;
344 $results['details'] = "Could not connect to database";
348 if (!@mysqli_select_db
($conn, $db_config['database'])) {
349 $results['severity'] = $this::REQUIREMENT_ERROR
;
350 $results['details'] = "Could not select the database";
354 $r = mysqli_query($conn, 'CREATE TEMPORARY TABLE civicrm_install_temp_table_test (test text)');
356 $results['severity'] = $this::REQUIREMENT_ERROR
;
357 $results['details'] = "Database does not support creation of temporary tables";
361 mysqli_query($conn, 'DROP TEMPORARY TABLE civicrm_install_temp_table_test');
370 public function checkMysqlTrigger($db_config) {
372 'title' => 'CiviCRM MySQL Trigger',
373 'severity' => $this::REQUIREMENT_OK
,
374 'details' => 'Database supports MySQL triggers',
377 $conn = $this->connect($db_config);
379 $results['severity'] = $this::REQUIREMENT_ERROR
;
380 $results['details'] = 'Could not connect to database';
384 if (!@mysqli_select_db
($conn, $db_config['database'])) {
385 $results['severity'] = $this::REQUIREMENT_ERROR
;
386 $results['details'] = "Could not select the database";
390 $r = mysqli_query($conn, 'CREATE TABLE civicrm_install_temp_table_test (test text)');
392 $results['severity'] = $this::REQUIREMENT_ERROR
;
393 $results['details'] = 'Could not create a table to run test';
397 $r = mysqli_query($conn, 'CREATE TRIGGER civicrm_install_temp_table_test_trigger BEFORE INSERT ON civicrm_install_temp_table_test FOR EACH ROW BEGIN END');
399 $results['severity'] = $this::REQUIREMENT_ERROR
;
400 $results['details'] = 'Database does not support creation of triggers';
403 mysqli_query($conn, 'DROP TRIGGER civicrm_install_temp_table_test_trigger');
406 mysqli_query($conn, 'DROP TABLE civicrm_install_temp_table_test');
411 * @param array $db_config
415 public function checkMySQLAutoIncrementIncrementOne(array $db_config) {
417 'title' => 'CiviCRM MySQL AutoIncrementIncrement',
418 'severity' => $this::REQUIREMENT_OK
,
419 'details' => 'MySQL server auto_increment_increment is 1',
422 $conn = $this->connect($db_config);
424 $results['severity'] = $this::REQUIREMENT_ERROR
;
425 $results['details'] = 'Could not connect to database';
429 $r = mysqli_query($conn, "SHOW variables like 'auto_increment_increment'");
431 $results['severity'] = $this::REQUIREMENT_ERROR
;
432 $results['details'] = 'Could not query database server variables';
436 $values = mysqli_fetch_row($r);
437 if ($values[1] != 1) {
438 $results['severity'] = $this::REQUIREMENT_ERROR
;
439 $results['details'] = 'MySQL server auto_increment_increment is not 1';
449 public function checkMysqlThreadStack($db_config) {
450 $min_thread_stack = 192;
453 'title' => 'CiviCRM Mysql thread stack',
454 'severity' => $this::REQUIREMENT_OK
,
455 'details' => 'MySQL thread_stack is OK',
458 $conn = $this->connect($db_config);
460 $results['severity'] = $this::REQUIREMENT_ERROR
;
461 $results['details'] = 'Could not connect to database';
465 if (!@mysqli_select_db
($conn, $db_config['database'])) {
466 $results['severity'] = $this::REQUIREMENT_ERROR
;
467 $results['details'] = 'Could not select the database';
471 $r = mysqli_query($conn, "SHOW VARIABLES LIKE 'thread_stack'"); // bytes => kb
473 $results['severity'] = $this::REQUIREMENT_ERROR
;
474 $results['details'] = 'Could not query thread_stack value';
477 $values = mysqli_fetch_row($r);
478 if ($values[1] < (1024 * $min_thread_stack)) {
479 $results['severity'] = $this::REQUIREMENT_ERROR
;
480 $results['details'] = 'MySQL thread_stack is ' . ($values[1] / 1024) . "kb (minimum required is {$min_thread_stack} kb";
492 public function checkMysqlLockTables($db_config) {
494 'title' => 'CiviCRM MySQL Lock Tables',
495 'severity' => $this::REQUIREMENT_OK
,
496 'details' => 'Can successfully lock and unlock tables',
499 $conn = $this->connect($db_config);
501 $results['severity'] = $this::REQUIREMENT_ERROR
;
502 $results['details'] = 'Could not connect to database';
506 if (!@mysqli_select_db
($conn, $db_config['database'])) {
507 $results['severity'] = $this::REQUIREMENT_ERROR
;
508 $results['details'] = 'Could not select the database';
513 $r = mysqli_query($conn, 'CREATE TEMPORARY TABLE civicrm_install_temp_table_test (test text)');
515 $results['severity'] = $this::REQUIREMENT_ERROR
;
516 $results['details'] = 'Could not create a table';
521 $r = mysqli_query($conn, 'LOCK TABLES civicrm_install_temp_table_test WRITE');
523 $results['severity'] = $this::REQUIREMENT_ERROR
;
524 $results['details'] = 'Could not obtain a write lock';
529 $r = mysqli_query($conn, 'UNLOCK TABLES');
531 $results['severity'] = $this::REQUIREMENT_ERROR
;
532 $results['details'] = 'Could not release table lock';
544 public function checkFilepathIsWritable($file_paths) {
546 'title' => 'CiviCRM directories are writable',
547 'severity' => $this::REQUIREMENT_OK
,
548 'details' => 'All required directories are writable: ' . implode(', ', $file_paths),
551 $unwritable_dirs = array();
552 foreach ($file_paths as $path) {
553 if (!is_writable($path)) {
554 $unwritable_dirs[] = $path;
558 if ($unwritable_dirs) {
559 $results['severity'] = $this::REQUIREMENT_ERROR
;
560 $results['details'] = "The following directories need to be made writable by the webserver: " . implode(', ', $unwritable_dirs);