3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
17 class CRM_Utils_Check_Component_Env
extends CRM_Utils_Check_Component
{
22 public function checkPhpVersion() {
24 $phpVersion = phpversion();
26 if (version_compare($phpVersion, CRM_Upgrade_Incremental_General
::RECOMMENDED_PHP_VER
) >= 0) {
27 $messages[] = new CRM_Utils_Check_Message(
29 ts('This system uses PHP version %1 which meets or exceeds the recommendation of %2.',
32 2 => preg_replace(';^(\d+\.\d+(?:\.[1-9]\d*)?).*$;', '\1', CRM_Upgrade_Incremental_General
::RECOMMENDED_PHP_VER
),
35 \Psr\Log\LogLevel
::INFO
,
39 elseif (version_compare($phpVersion, CRM_Upgrade_Incremental_General
::MIN_RECOMMENDED_PHP_VER
) >= 0) {
40 $messages[] = new CRM_Utils_Check_Message(
42 ts('This system uses PHP version %1. This meets the minimum recommendations and you do not need to upgrade immediately, but the preferred version is %2.',
45 2 => CRM_Upgrade_Incremental_General
::RECOMMENDED_PHP_VER
,
47 ts('PHP Out-of-Date'),
48 \Psr\Log\LogLevel
::NOTICE
,
52 elseif (version_compare($phpVersion, CRM_Upgrade_Incremental_General
::MIN_INSTALL_PHP_VER
) >= 0) {
53 $messages[] = new CRM_Utils_Check_Message(
55 ts('This system uses PHP version %1. This meets the minimum requirements for CiviCRM to function but is not recommended. At least PHP version %2 is recommended; the preferred version is %3.',
58 2 => CRM_Upgrade_Incremental_General
::MIN_RECOMMENDED_PHP_VER
,
59 3 => preg_replace(';^(\d+\.\d+(?:\.[1-9]\d*)?).*$;', '\1', CRM_Upgrade_Incremental_General
::RECOMMENDED_PHP_VER
),
61 ts('PHP Out-of-Date'),
62 \Psr\Log\LogLevel
::WARNING
,
67 $messages[] = new CRM_Utils_Check_Message(
69 ts('This system uses PHP version %1. To ensure the continued operation of CiviCRM, upgrade your server now. At least PHP version %2 is recommended; the preferred version is %3.',
72 2 => CRM_Upgrade_Incremental_General
::MIN_RECOMMENDED_PHP_VER
,
73 3 => preg_replace(';^(\d+\.\d+(?:\.[1-9]\d*)?).*$;', '\1', CRM_Upgrade_Incremental_General
::RECOMMENDED_PHP_VER
),
75 ts('PHP Out-of-Date'),
76 \Psr\Log\LogLevel
::ERROR
,
87 public function checkPhpMysqli() {
90 if (!extension_loaded('mysqli')) {
91 $messages[] = new CRM_Utils_Check_Message(
93 ts('Future versions of CiviCRM may require the PHP extension "%2". To ensure that your system will be compatible, please install it in advance. For more explanation, see <a href="%1">the announcement</a>.',
95 1 => 'https://civicrm.org/blog/totten/psa-please-verify-php-extension-mysqli',
98 ts('Forward Compatibility: Enable "mysqli"'),
99 \Psr\Log\LogLevel
::WARNING
,
108 * Check that the MySQL time settings match the PHP time settings.
110 * @return array<CRM_Utils_Check_Message> an empty array, or a list of warnings
112 public function checkMysqlTime() {
115 $phpNow = date('Y-m-d H:i');
116 $sqlNow = CRM_Core_DAO
::singleValueQuery("SELECT date_format(now(), '%Y-%m-%d %H:%i')");
117 if (!CRM_Utils_Time
::isEqual($phpNow, $sqlNow, 2.5 * 60)) {
118 $messages[] = new CRM_Utils_Check_Message(
120 ts('Timestamps reported by MySQL (eg "%2") and PHP (eg "%3" ) are mismatched.<br /><a href="%1">Read more about this warning</a>', [
121 1 => CRM_Utils_System
::docURL2('sysadmin/requirements/#mysql-time', TRUE),
125 ts('Timestamp Mismatch'),
126 \Psr\Log\LogLevel
::ERROR
,
137 public function checkDebug() {
138 $config = CRM_Core_Config
::singleton();
139 if ($config->debug
) {
140 $message = new CRM_Utils_Check_Message(
142 ts('Warning: Debug is enabled in <a href="%1">system settings</a>. This should not be enabled on production servers.',
143 [1 => CRM_Utils_System
::url('civicrm/admin/setting/debug', 'reset=1')]),
144 ts('Debug Mode Enabled'),
145 \Psr\Log\LogLevel
::WARNING
,
149 ts('Disable Debug Mode'),
150 ts('Disable debug mode now?'),
152 ['Setting', 'create', ['debug_enabled' => 0]]
163 public function checkOutboundMail() {
166 $mailingInfo = Civi
::settings()->get('mailing_backend');
167 if (($mailingInfo['outBound_option'] == CRM_Mailing_Config
::OUTBOUND_OPTION_REDIRECT_TO_DB
168 ||
(defined('CIVICRM_MAIL_LOG') && CIVICRM_MAIL_LOG
)
169 ||
$mailingInfo['outBound_option'] == CRM_Mailing_Config
::OUTBOUND_OPTION_DISABLED
170 ||
$mailingInfo['outBound_option'] == CRM_Mailing_Config
::OUTBOUND_OPTION_MOCK
)
172 $messages[] = new CRM_Utils_Check_Message(
174 ts('Warning: Outbound email is disabled in <a href="%1">system settings</a>. Proper settings should be enabled on production servers.',
175 [1 => CRM_Utils_System
::url('civicrm/admin/setting/smtp', 'reset=1')]),
176 ts('Outbound Email Disabled'),
177 \Psr\Log\LogLevel
::WARNING
,
186 * Check that domain email and org name are set
189 public function checkDomainNameEmail() {
192 list($domainEmailName, $domainEmailAddress) = CRM_Core_BAO_Domain
::getNameAndEmail(TRUE);
193 $domain = CRM_Core_BAO_Domain
::getDomain();
194 $domainName = $domain->name
;
195 $fixEmailUrl = CRM_Utils_System
::url("civicrm/admin/options/from_email_address", "&reset=1");
196 $fixDomainName = CRM_Utils_System
::url("civicrm/admin/domain", "action=update&reset=1");
198 if (!$domainEmailAddress ||
$domainEmailAddress == 'info@EXAMPLE.ORG') {
199 if (!$domainName ||
$domainName == 'Default Domain Name') {
200 $msg = ts("Please enter your organization's <a href=\"%1\">name, primary address </a> and <a href=\"%2\">default FROM Email Address </a> (for system-generated emails).",
208 $msg = ts('Please enter a <a href="%1">default FROM Email Address</a> (for system-generated emails).',
209 [1 => $fixEmailUrl]);
212 elseif (!$domainName ||
$domainName == 'Default Domain Name') {
213 $msg = ts("Please enter your organization's <a href=\"%1\">name and primary address</a>.",
214 [1 => $fixDomainName]);
218 $messages[] = new CRM_Utils_Check_Message(
221 ts('Complete Setup'),
222 \Psr\Log\LogLevel
::WARNING
,
231 * Checks if a default bounce handling mailbox is set up
234 public function checkDefaultMailbox() {
236 $config = CRM_Core_Config
::singleton();
238 if (in_array('CiviMail', $config->enableComponents
) &&
239 CRM_Core_BAO_MailSettings
::defaultDomain() == "EXAMPLE.ORG"
241 $message = new CRM_Utils_Check_Message(
243 ts('Please configure a <a href="%1">default mailbox</a> for CiviMail.',
244 [1 => CRM_Utils_System
::url('civicrm/admin/mailSettings', "reset=1")]),
245 ts('Configure Default Mailbox'),
246 \Psr\Log\LogLevel
::WARNING
,
249 $docUrl = 'target="_blank" href="' . CRM_Utils_System
::docURL(['page' => 'user/advanced-configuration/email-system-configuration/', 'URLonly' => TRUE]) . '""';
251 ts('A default mailbox must be configured for email bounce processing.') . '<br />' .
252 ts("Learn more in the <a %1>online documentation</a>.", [1 => $docUrl])
254 $messages[] = $message;
261 * Checks if cron has run in a reasonable amount of time
264 public function checkLastCron() {
267 $statusPreference = new CRM_Core_DAO_StatusPreference();
268 $statusPreference->domain_id
= CRM_Core_Config
::domainID();
269 $statusPreference->name
= 'checkLastCron';
271 if ($statusPreference->find(TRUE) && !empty($statusPreference->check_info
)) {
272 $lastCron = $statusPreference->check_info
;
273 $msg = ts('Last cron run at %1.', [1 => CRM_Utils_Date
::customFormat(date('c', $lastCron))]);
277 $msg = ts('No cron runs have been recorded.');
280 if ($lastCron > gmdate('U') - 3600) {
281 $messages[] = new CRM_Utils_Check_Message(
284 ts('Cron Running OK'),
285 \Psr\Log\LogLevel
::INFO
,
290 $cronLink = 'target="_blank" href="' . htmlentities(CRM_Utils_System
::docURL2('sysadmin/setup/jobs/', TRUE)) . '""';
291 $msg .= '<p>' . ts('To enable scheduling support, please <a %1>set up the cron job</a>.', [
294 $message = new CRM_Utils_Check_Message(
297 ts('Cron Not Running'),
298 ($lastCron > gmdate('U') - 86400) ? \Psr\Log\LogLevel
::WARNING
: \Psr\Log\LogLevel
::ERROR
,
301 $messages[] = $message;
308 * Recommend that sites use path-variables for their directories and URLs.
311 public function checkUrlVariables() {
313 $hasOldStyle = FALSE;
315 'userFrameworkResourceURL',
321 foreach ($settingNames as $settingName) {
322 $settingValue = Civi
::settings()->get($settingName);
323 if (!empty($settingValue) && $settingValue{0} != '[') {
330 $message = new CRM_Utils_Check_Message(
332 ts('<a href="%1">Resource URLs</a> may use absolute paths, relative paths, or variables. Absolute paths are more difficult to maintain. To maximize portability, consider using a variable in each URL (eg "<tt>[cms.root]</tt>" or "<tt>[civicrm.files]</tt>").',
333 [1 => CRM_Utils_System
::url('civicrm/admin/setting/url', "reset=1")]),
334 ts('Resource URLs: Make them portable'),
335 \Psr\Log\LogLevel
::NOTICE
,
338 $messages[] = $message;
345 * Recommend that sites use path-variables for their directories and URLs.
348 public function checkDirVariables() {
350 $hasOldStyle = FALSE;
354 'customFileUploadDir',
360 foreach ($settingNames as $settingName) {
361 $settingValue = Civi
::settings()->get($settingName);
362 if (!empty($settingValue) && $settingValue{0} != '[') {
369 $message = new CRM_Utils_Check_Message(
371 ts('<a href="%1">Directories</a> may use absolute paths, relative paths, or variables. Absolute paths are more difficult to maintain. To maximize portability, consider using a variable in each directory (eg "<tt>[cms.root]</tt>" or "<tt>[civicrm.files]</tt>").',
372 [1 => CRM_Utils_System
::url('civicrm/admin/setting/path', "reset=1")]),
373 ts('Directory Paths: Make them portable'),
374 \Psr\Log\LogLevel
::NOTICE
,
377 $messages[] = $message;
384 * Check that important directories are writable.
387 * Any CRM_Utils_Check_Message instances that need to be generated.
389 public function checkDirsWritable() {
392 $config = CRM_Core_Config
::singleton();
394 'uploadDir' => ts('Temporary Files Directory'),
395 'imageUploadDir' => ts('Images Directory'),
396 'customFileUploadDir' => ts('Custom Files Directory'),
399 foreach ($directories as $directory => $label) {
400 $file = CRM_Utils_File
::createFakeFile($config->$directory);
402 if ($file === FALSE) {
403 $notWritable[] = "$label ({$config->$directory})";
406 $dirWithSlash = CRM_Utils_File
::addTrailingSlash($config->$directory);
407 unlink($dirWithSlash . $file);
413 if (!empty($notWritable)) {
414 $messages[] = new CRM_Utils_Check_Message(
416 ts('The %1 is not writable. Please check your file permissions.', [
417 1 => implode(', ', $notWritable),
418 'count' => count($notWritable),
419 'plural' => 'The following directories are not writable: %1. Please check your file permissions.',
421 ts('Directory not writable', [
422 'count' => count($notWritable),
423 'plural' => 'Directories not writable',
425 \Psr\Log\LogLevel
::ERROR
,
434 * Checks if new versions are available
437 public function checkVersion() {
440 $vc = new CRM_Utils_VersionCheck();
443 catch (Exception
$e) {
444 $messages[] = new CRM_Utils_Check_Message(
446 ts('Directory %1 is not writable. Please change your file permissions.',
447 [1 => dirname($vc->cacheFile
)]),
448 ts('Directory not writable'),
449 \Psr\Log\LogLevel
::ERROR
,
455 // Show a notice if the version_check job is disabled
456 if (empty($vc->cronJob
['is_active'])) {
457 $args = empty($vc->cronJob
['id']) ?
['reset' => 1] : ['reset' => 1, 'action' => 'update', 'id' => $vc->cronJob
['id']];
458 $messages[] = new CRM_Utils_Check_Message(
459 'checkVersionDisabled',
460 ts('The check for new versions of CiviCRM has been disabled. <a %1>Re-enable the scheduled job</a> to receive important security update notifications.', [1 => 'href="' . CRM_Utils_System
::url('civicrm/admin/job', $args) . '"']),
461 ts('Update Check Disabled'),
462 \Psr\Log\LogLevel
::NOTICE
,
467 if ($vc->isInfoAvailable
) {
469 'info' => CRM_Utils_Check
::severityMap(\Psr\Log\LogLevel
::INFO
),
470 'notice' => CRM_Utils_Check
::severityMap(\Psr\Log\LogLevel
::NOTICE
) ,
471 'warning' => CRM_Utils_Check
::severityMap(\Psr\Log\LogLevel
::WARNING
) ,
472 'critical' => CRM_Utils_Check
::severityMap(\Psr\Log\LogLevel
::CRITICAL
),
474 foreach ($vc->getVersionMessages() ??
[] as $msg) {
475 $messages[] = new CRM_Utils_Check_Message(__FUNCTION__
. '_' . $msg['name'],
476 $msg['message'], $msg['title'], $severities[$msg['severity']], 'fa-cloud-upload');
484 * Checks if extensions are set up properly
487 public function checkExtensions() {
489 $extensionSystem = CRM_Extension_System
::singleton();
490 $mapper = $extensionSystem->getMapper();
491 $manager = $extensionSystem->getManager();
493 if ($extensionSystem->getDefaultContainer()) {
494 $basedir = $extensionSystem->getDefaultContainer()->baseDir
;
497 if (empty($basedir)) {
498 // no extension directory
499 $messages[] = new CRM_Utils_Check_Message(
501 ts('Your extensions directory is not set. Click <a href="%1">here</a> to set the extensions directory.',
502 [1 => CRM_Utils_System
::url('civicrm/admin/setting/path', 'reset=1')]),
503 ts('Directory not writable'),
504 \Psr\Log\LogLevel
::NOTICE
,
510 if (!is_dir($basedir)) {
511 $messages[] = new CRM_Utils_Check_Message(
513 ts('Your extensions directory path points to %1, which is not a directory. Please check your file system.',
515 ts('Extensions directory incorrect'),
516 \Psr\Log\LogLevel
::ERROR
,
521 elseif (!is_writable($basedir)) {
522 $messages[] = new CRM_Utils_Check_Message(
523 __FUNCTION__
. 'Writable',
524 ts('Your extensions directory (%1) is read-only. If you would like to perform downloads or upgrades, then change the file permissions.',
526 ts('Read-Only Extensions'),
527 \Psr\Log\LogLevel
::NOTICE
,
532 if (empty($extensionSystem->getDefaultContainer()->baseUrl
)) {
533 $messages[] = new CRM_Utils_Check_Message(
534 __FUNCTION__
. 'URL',
535 ts('The extensions URL is not properly set. Please go to the <a href="%1">URL setting page</a> and correct it.',
536 [1 => CRM_Utils_System
::url('civicrm/admin/setting/url', 'reset=1')]),
537 ts('Extensions url missing'),
538 \Psr\Log\LogLevel
::ERROR
,
543 if (!$extensionSystem->getBrowser()->isEnabled()) {
544 $messages[] = new CRM_Utils_Check_Message(
546 ts('Not checking remote URL for extensions since ext_repo_url is set to false.'),
547 ts('Extensions check disabled'),
548 \Psr\Log\LogLevel
::NOTICE
,
555 $remotes = $extensionSystem->getBrowser()->getExtensions();
557 catch (CRM_Extension_Exception
$e) {
558 $messages[] = new CRM_Utils_Check_Message(
561 ts('Extension download error'),
562 \Psr\Log\LogLevel
::ERROR
,
569 // CRM-13141 There may not be any compatible extensions available for the requested CiviCRM version + CMS. If so, $extdir is empty so just return a notice.
570 $messages[] = new CRM_Utils_Check_Message(
572 ts('There are currently no extensions on the CiviCRM public extension directory which are compatible with version %1. If you want to install an extension which is not marked as compatible, you may be able to <a %2>download and install extensions manually</a> (depending on access to your web server).', [
573 1 => CRM_Utils_System
::majorVersion(),
574 2 => 'href="http://wiki.civicrm.org/confluence/display/CRMDOC/Extensions"',
576 ts('No Extensions Available for this Version'),
577 \Psr\Log\LogLevel
::NOTICE
,
583 $keys = array_keys($manager->getStatuses());
585 $updates = $errors = $okextensions = [];
587 foreach ($keys as $key) {
589 $obj = $mapper->keyToInfo($key);
591 catch (CRM_Extension_Exception
$ex) {
592 $errors[] = ts('Failed to read extension (%1). Please refresh the extension list.', [1 => $key]);
595 $row = CRM_Admin_Page_Extensions
::createExtendedInfo($obj);
596 switch ($row['status']) {
597 case CRM_Extension_Manager
::STATUS_INSTALLED_MISSING
:
598 $errors[] = ts('%1 extension (%2) is installed but missing files.', [1 => $row['label'] ??
NULL, 2 => $key]);
601 case CRM_Extension_Manager
::STATUS_INSTALLED
:
602 if (!empty($remotes[$key]) && version_compare($row['version'], $remotes[$key]->version
, '<')) {
603 $updates[] = ts('%1 (%2) version %3 is installed. <a %4>Upgrade to version %5</a>.', [
604 1 => $row['label'] ??
NULL,
606 3 => $row['version'],
607 4 => 'href="' . CRM_Utils_System
::url('civicrm/admin/extensions', "action=update&id=$key&key=$key") . '"',
608 5 => $remotes[$key]->version
,
612 if (empty($row['label'])) {
613 $okextensions[] = $key;
616 $okextensions[] = ts('%1 (%2) version %3', [
619 3 => $row['version'],
627 if (!$okextensions && !$updates && !$errors) {
628 $messages[] = new CRM_Utils_Check_Message(
630 ts('No extensions installed. <a %1>Browse available extensions</a>.', [
631 1 => 'href="' . CRM_Utils_System
::url('civicrm/admin/extensions', 'reset=1') . '"',
634 \Psr\Log\LogLevel
::INFO
,
640 $messages[] = new CRM_Utils_Check_Message(
641 __FUNCTION__
. 'Error',
642 '<ul><li>' . implode('</li><li>', $errors) . '</li></ul>',
643 ts('Extension Error'),
644 \Psr\Log\LogLevel
::ERROR
,
650 $messages[] = new CRM_Utils_Check_Message(
652 '<ul><li>' . implode('</li><li>', $updates) . '</li></ul>',
653 ts('Extension Update Available', ['plural' => '%count Extension Updates Available', 'count' => count($updates)]),
654 \Psr\Log\LogLevel
::WARNING
,
660 if ($updates ||
$errors) {
661 $message = ts('1 extension is up-to-date:', ['plural' => '%count extensions are up-to-date:', 'count' => count($okextensions)]);
664 $message = ts('All extensions are up-to-date:');
666 $messages[] = new CRM_Utils_Check_Message(
668 $message . '<ul><li>' . implode('</li><li>', $okextensions) . '</li></ul>',
670 \Psr\Log\LogLevel
::INFO
,
679 * Checks if there are pending extension upgrades.
683 public function checkExtensionUpgrades() {
684 if (CRM_Extension_Upgrades
::hasPending()) {
685 $message = new CRM_Utils_Check_Message(
687 ts('Extension upgrades should be run as soon as possible.'),
688 ts('Extension Upgrades Pending'),
689 \Psr\Log\LogLevel
::ERROR
,
694 ts('Run extension upgrades now?'),
696 ['path' => 'civicrm/admin/extensions/upgrade', 'query' => ['reset' => 1, 'destination' => CRM_Utils_System
::url('civicrm/a/#/status')]]
704 * Checks if CiviCRM database version is up-to-date
707 public function checkDbVersion() {
709 $dbVersion = CRM_Core_BAO_Domain
::version();
710 $upgradeUrl = CRM_Utils_System
::url("civicrm/upgrade", "reset=1");
714 $messages[] = new CRM_Utils_Check_Message(
716 ts('Version information found to be missing in database. You will need to determine the correct version corresponding to your current database state.'),
717 ts('Database Version Missing'),
718 \Psr\Log\LogLevel
::ERROR
,
722 elseif (!CRM_Utils_System
::isVersionFormatValid($dbVersion)) {
723 $messages[] = new CRM_Utils_Check_Message(
725 ts('Database is marked with invalid version format. You may want to investigate this before you proceed further.'),
726 ts('Database Version Invalid'),
727 \Psr\Log\LogLevel
::ERROR
,
731 elseif (stripos($dbVersion, 'upgrade')) {
732 // if db.ver indicates a partially upgraded db
733 $messages[] = new CRM_Utils_Check_Message(
735 ts('Database check failed - the database looks to have been partially upgraded. You must reload the database with the backup and try the <a href=\'%1\'>upgrade process</a> again.', [1 => $upgradeUrl]),
736 ts('Database Partially Upgraded'),
737 \Psr\Log\LogLevel
::ALERT
,
742 $codeVersion = CRM_Utils_System
::version();
744 // if db.ver < code.ver, time to upgrade
745 if (version_compare($dbVersion, $codeVersion) < 0) {
746 $messages[] = new CRM_Utils_Check_Message(
748 ts('New codebase version detected. You must visit <a href=\'%1\'>upgrade screen</a> to upgrade the database.', [1 => $upgradeUrl]),
749 ts('Database Upgrade Required'),
750 \Psr\Log\LogLevel
::ALERT
,
755 // if db.ver > code.ver, sth really wrong
756 if (version_compare($dbVersion, $codeVersion) > 0) {
757 $messages[] = new CRM_Utils_Check_Message(
759 ts('Your database is marked with an unexpected version number: %1. The v%2 codebase may not be compatible with your database state.
760 You will need to determine the correct version corresponding to your current database state. You may want to revert to the codebase
761 you were using until you resolve this problem.<br/>OR if this is a manual install from git, you might want to fix civicrm-version.php file.',
762 [1 => $dbVersion, 2 => $codeVersion]
764 ts('Database In Unexpected Version'),
765 \Psr\Log\LogLevel
::ERROR
,
775 * ensure that all CiviCRM tables are InnoDB
778 public function checkDbEngine() {
781 if (CRM_Core_DAO
::isDBMyISAM(150)) {
782 $messages[] = new CRM_Utils_Check_Message(
784 ts('Your database is configured to use the MyISAM database engine. CiviCRM requires InnoDB. You will need to convert any MyISAM tables in your database to InnoDB. Using MyISAM tables will result in data integrity issues.'),
785 ts('MyISAM Database Engine'),
786 \Psr\Log\LogLevel
::ERROR
,
794 * ensure reply id is set to any default value
797 public function checkReplyIdForMailing() {
800 if (!CRM_Mailing_PseudoConstant
::defaultComponent('Reply', '')) {
801 $messages[] = new CRM_Utils_Check_Message(
803 ts('Reply Auto Responder is not set to any default value in <a %1>Headers, Footers, and Automated Messages</a>. This will disable the submit operation on any mailing created from CiviMail.', [1 => 'href="' . CRM_Utils_System
::url('civicrm/admin/component', 'reset=1') . '"']),
804 ts('No Default value for Auto Responder.'),
805 \Psr\Log\LogLevel
::WARNING
,
813 * Check for required mbstring extension
816 public function checkMbstring() {
819 if (!function_exists('mb_substr')) {
820 $messages[] = new CRM_Utils_Check_Message(
822 ts('The PHP Multibyte String extension is needed for CiviCRM to correctly handle user input among other functionality. Ask your system administrator to install it.'),
823 ts('Missing mbstring Extension'),
824 \Psr\Log\LogLevel
::WARNING
,
832 * Check if environment is Production.
835 public function checkEnvironment() {
838 $environment = CRM_Core_Config
::environment();
839 if ($environment != 'Production') {
840 $messages[] = new CRM_Utils_Check_Message(
842 ts('The environment of this CiviCRM instance is set to \'%1\'. Certain functionality like scheduled jobs has been disabled.', [1 => $environment]),
843 ts('Non-Production Environment'),
844 \Psr\Log\LogLevel
::ALERT
,
852 * Check that the resource URL points to the correct location.
855 public function checkResourceUrl() {
857 // Skip when run during unit tests, you can't check without a CMS.
858 if (CRM_Core_Config
::singleton()->userFramework
== 'UnitTests') {
861 // CRM-21629 Set User Agent to avoid being blocked by filters
862 stream_context_set_default([
863 'http' => ['user_agent' => 'CiviCRM'],
866 // Does arrow.png exist where we expect it?
867 $arrowUrl = CRM_Core_Config
::singleton()->userFrameworkResourceURL
. 'packages/jquery/css/images/arrow.png';
868 if ($this->fileExists($arrowUrl) === FALSE) {
869 $messages[] = new CRM_Utils_Check_Message(
871 ts('The Resource URL is not set correctly. Please set the <a href="%1">CiviCRM Resource URL</a>.',
872 [1 => CRM_Utils_System
::url('civicrm/admin/setting/url', 'reset=1')]),
873 ts('Incorrect Resource URL'),
874 \Psr\Log\LogLevel
::ERROR
,
882 * Check for utf8mb4 support by MySQL.
884 * @return array<CRM_Utils_Check_Message> an empty array, or a list of warnings
886 public function checkMysqlUtf8mb4() {
889 if (CRM_Core_DAO
::getConnection()->phptype
!= 'mysqli') {
893 // Use mysqli_query() to avoid logging an error message.
894 $mb4testTableName = CRM_Utils_SQL_TempTable
::build()->setCategory('utf8mb4test')->getName();
895 if (mysqli_query(CRM_Core_DAO
::getConnection()->connection
, 'CREATE TEMPORARY TABLE ' . $mb4testTableName . ' (id VARCHAR(255), PRIMARY KEY(id(255))) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC ENGINE=INNODB')) {
896 CRM_Core_DAO
::executeQuery('DROP TEMPORARY TABLE ' . $mb4testTableName);
899 $messages[] = new CRM_Utils_Check_Message(
901 ts("Future versions of CiviCRM may require MySQL to support utf8mb4 encoding. It is recommended, though not yet required. Please discuss with your server administrator about configuring your MySQL server for utf8mb4. CiviCRM's recommended configurations are in the <a href='%1' title='System Administrator Guide'>System Administrator Guide</a>", [1 => CRM_Utils_System
::docURL2("sysadmin/requirements/#mysql-configuration", TRUE)]),
902 ts('MySQL Emoji Support (utf8mb4)'),
903 \Psr\Log\LogLevel
::WARNING
,
907 // Ensure that the MySQL driver supports utf8mb4 encoding.
908 $version = mysqli_get_client_info();
909 if (strpos($version, 'mysqlnd') !== FALSE) {
910 // The mysqlnd driver supports utf8mb4 starting at version 5.0.9.
911 $version = preg_replace('/^\D+([\d.]+).*/', '$1', $version);
912 if (version_compare($version, '5.0.9', '<')) {
913 $messages[] = new CRM_Utils_Check_Message(
914 __FUNCTION__
. 'mysqlnd',
915 ts('It is recommended, though not yet required, to upgrade your PHP MySQL driver (mysqlnd) to >= 5.0.9 for utf8mb4 support.'),
916 ts('PHP MySQL Driver (mysqlnd)'),
917 \Psr\Log\LogLevel
::WARNING
,
923 // The libmysqlclient driver supports utf8mb4 starting at version 5.5.3.
924 if (version_compare($version, '5.5.3', '<')) {
925 $messages[] = new CRM_Utils_Check_Message(
926 __FUNCTION__
. 'libmysqlclient',
927 ts('It is recommended, though not yet required, to upgrade your PHP MySQL driver (libmysqlclient) to >= 5.5.3 for utf8mb4 support.'),
928 ts('PHP MySQL Driver (libmysqlclient)'),
929 \Psr\Log\LogLevel
::WARNING
,
938 public function checkMysqlVersion() {
940 $version = CRM_Utils_SQL
::getDatabaseVersion();
941 $minRecommendedVersion = CRM_Upgrade_Incremental_General
::MIN_RECOMMENDED_MYSQL_VER
;
942 $mariaDbRecommendedVersion = '10.1';
943 $upcomingCiviChangeVersion = '5.34';
944 if (version_compare(CRM_Utils_SQL
::getDatabaseVersion(), $minRecommendedVersion, '<')) {
945 $messages[] = new CRM_Utils_Check_Message(
947 ts('To prepare for CiviCRM v%4, please upgrade MySQL. The recommended version will be MySQL v%2 or MariaDB v%3.', [
949 2 => $minRecommendedVersion . '+',
950 3 => $mariaDbRecommendedVersion . '+',
951 4 => $upcomingCiviChangeVersion . '+',
953 ts('MySQL Out-of-Date'),
954 \Psr\Log\LogLevel
::NOTICE
,
961 public function checkPHPIntlExists() {
963 if (!extension_loaded('intl')) {
964 $messages[] = new CRM_Utils_Check_Message(
966 ts('This system currently does not have the PHP-INTL extension enabled please contact your system administrator about getting the extension enabled'),
967 ts('Missing PHP Extension: INTL'),
968 \Psr\Log\LogLevel
::WARNING
,