├── .gitignore
├── .scrutinizer.yml
├── LICENSE
├── README.md
├── admin
├── arrilot_systemcheck_check.php
├── arrilot_systemcheck_monitoring.php
└── menu.php
├── composer.json
├── include.php
├── install
├── admin
│ ├── arrilot_systemcheck_check.php
│ └── arrilot_systemcheck_monitoring.php
├── index.php
└── version.php
├── options.php
├── phpunit.xml
└── src
├── Checks
├── Bitrix
│ ├── AgentsUsingCronCheck.php
│ ├── ApacheModules.php
│ ├── CacheDirPermissionsCheck.php
│ ├── ConfigFilesExcessOutputCheck.php
│ ├── DataBaseCheck.php
│ ├── DataBaseConfigsMatchForBothCores.php
│ ├── EmailsCanBeSend.php
│ ├── EncodingSettingsAreCorrect.php
│ ├── Mail.php
│ ├── PhpSettings.php
│ ├── RequiredPhpModules.php
│ ├── ServiceScriptsAreRemoved.php
│ └── UpdateServerCheck.php
├── Check.php
├── Custom
│ ├── BasicAuthIsTurnedOn.php
│ ├── BitrixDebugIsTurnedOff.php
│ ├── BitrixDebugIsTurnedOn.php
│ ├── ComposerSecurityCheck.php
│ ├── DiskSpaceCheck.php
│ ├── ElasticSearchConnection.php
│ ├── HttpsRedirect.php
│ ├── NewRelicIsLoaded.php
│ ├── RamCheck.php
│ ├── RobotsTxt.php
│ ├── SSLCertificateIsValid.php
│ ├── WwwRedirect.php
│ └── XDebugIsNotLoaded.php
└── Greensight
│ └── FrontendBuildIsProduction.php
├── Console
└── SystemCheckCommand.php
├── EventHandlers.php
├── Exceptions
├── FailCheckException.php
└── SkipCheckException.php
└── Monitorings
├── DataStorage.php
└── Monitoring.php
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.phar
3 | composer.lock
4 | .DS_Store
5 | /.idea
6 |
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | filter:
2 | paths:
3 | - 'src/*'
4 | excluded_paths:
5 | - 'vendor/*'
6 | - 'tests/*'
7 | tools:
8 | php_cs_fixer:
9 | config: { level: psr2 }
10 | checks:
11 | php:
12 | code_rating: true
13 | duplication: true
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Nekrasov Ilya
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://packagist.org/packages/arrilot/bitrix-systemcheck/)
2 |
3 | # Данный пакет больше активно не поддерживается
4 |
5 | Причина - мы больше не используем Битрикс в своих проектах.
6 | Если вам интересен этот проект и вы хотите заняться его поддержкой - форкните его и создайте Issue в данном репозитории чтобы мы поместили здесь ссылку на форк.
7 |
8 | # Bitrix System Checks
9 |
10 | *Пакет представляет из себя модуль для Битрикса, который является более гибкой и функциональной альтернативой штатной битриксовой функциональности "Проверка системы"*
11 |
12 | ### Установка
13 |
14 | 1. Добавляем в `composer.json` следующие строчки если их там еще нет
15 |
16 | ```
17 | "extra": {
18 | "installer-paths": {
19 | "app/local/modules/{$name}/": [
20 | "type:bitrix-d7-module",
21 | "type:bitrix-module"
22 | ]
23 | }
24 | },
25 | ```
26 |
27 | 2.
28 |
29 | `composer require arrilot/bitrix-systemcheck`
30 |
31 | 3. Устанавливаем модуль на странице /bitrix/admin/partner_modules.php?lang=ru
32 |
33 | 4. Добавляем local/modules/arrilot.systemcheck в .gitignore
34 |
35 | ### Теория
36 |
37 | #### Возможности bitrix-systemcheck
38 |
39 | - Предоставляет обширную коллекцию готовых проверок, которые включают в себя как как и проверки из встроенного в Битрикс решения, так и дополнительные
40 | - Позволяет создавать новые, специфичные для приложения проверки
41 | - Позволяет создавать неограниченное количество мониторингов с любым набором проверок в каждом
42 | - Позволяет запускать эти мониторинги как через админку (web thread), так и через консоль (cli)
43 | - Умеет логировать результат через PSR-логгер и помогает автоматизировать таким образом контроль за приложением.
44 | - При необходимости дружит c basic auth в отличие от встроенной проверки
45 |
46 | #### Основные сущности
47 |
48 | Основные сушности пакета - проверка и мониторинг.
49 |
50 | Проверка - класс, наследующий `Arrilot\BitrixSystemCheck\Checks\Check`. Цель проверки - вернуть `true/false` в методе `run()`.
51 | Если проверка возвращает false, то где-то перед `return false;` следует вызвать метод `$this->logError("текст ошибки тут");` один или несколько раз чтобы было понятна причина провала проверки.
52 | Пример:
53 |
54 | ```php
55 | class BitrixDebugIsTurnedOn extends Check
56 | {
57 | /**
58 | * @return string
59 | */
60 | public function name()
61 | {
62 | return "Проверка на exception_handling.debug = true ...";
63 | }
64 |
65 | /**
66 | * @return boolean
67 | */
68 | public function run()
69 | {
70 | $config = Configuration::getInstance()->get('exception_handling');
71 |
72 | if (empty($config['debug'])) {
73 | $this->logError('Значение конфигурации exception_handling.debug должно быть true в данном окружении');
74 | return false;
75 | }
76 |
77 | return true;
78 | }
79 | }
80 | ```
81 |
82 | Мониторинг - класс, наследующий `Arrilot\BitrixSystemCheck\Checks\Monitoring`
83 | Логически он представляет из себя именованный набор проверок, причем этот набор может зависить от окружения и других параметров.
84 |
85 | Пример:
86 |
87 | ```php
88 | class FullMonitoring extends Monitoring
89 | {
90 | /**
91 | * Russian monitoring name
92 | *
93 | * @return string
94 | */
95 | public function name()
96 | {
97 | return 'Полный мониторинг';
98 | }
99 |
100 | /**
101 | * Monitoring code (id)
102 | *
103 | * @return string
104 | */
105 | public function code()
106 | {
107 | return 'full';
108 | }
109 |
110 | /**
111 | * @return array
112 | */
113 | public function checks()
114 | {
115 | if (in_production()) {
116 | return [
117 | new BitrixChecks\RequiredPhpModules(),
118 | new BitrixChecks\PhpSettings(),
119 | ...
120 | }
121 |
122 | return [
123 | new BitrixChecks\RequiredPhpModules(),
124 | new BitrixChecks\PhpSettings(),
125 | ...
126 | ];
127 | }
128 |
129 | /**
130 | * @return Logger|null|\Psr\Log\LoggerInterface
131 | */
132 | public function logger()
133 | {
134 | $logger = new Logger('monitoring-full');
135 | $logger->pushHandler(new StreamHandler(logs_path('systemcheck/monitoring-full.log')));
136 |
137 | return $logger;
138 | }
139 | }
140 | ```
141 |
142 | ### Использование
143 |
144 | 1. Реализовуем мониторинг/мониторинги (пример выше)
145 | 2. Добавляем их в ``.setting_extra.php`
146 |
147 | ```php
148 | 'bitrix-systemcheck' => [
149 | 'value' => [
150 | 'monitorings' => [
151 | \System\SystemCheck\Monitorings\FullMonitoring::class,
152 | \System\SystemCheck\Monitorings\BriefMonitoring::class,
153 | ]
154 | ],
155 | 'readonly' => true,
156 | ],
157 | ```
158 |
159 | 3. Теперь можно запустить выбранный мониторинг в админке `/bitrix/admin/arrilot_systemcheck_monitoring.php?code=full`
160 | 4. Регистрируем команду `Arrilot\BitrixSystemCheck\Console\SystemCheckCommand` в консольном приложении на базе symfony/console если у нас есть такое
161 | 5. После этого мы сможем запускать мониторинги через консольное приложение следующим образом :
162 | `php our_console_app.php system:check full`, где `full` - код мониторинга, возвращаемый методом `Monitoring::code()`.
163 | Запускать лучше от пользователя из-под которого у вас работают кроны и веб-сервер, например вот так ` sudo -H -u www-data bash -c 'php our_console_app.php system:check full'`
164 | 6. При желании ставим консольную команду запускающую мониторинг в крон и получаем логи/алерты согласно нашему логгеру из `Monitoring::logger()`
165 |
166 | Имеющиеся в пакете проверки можно посмотреть в `src/Checks`
167 |
168 | ### Добавление проверок в пакет aka Contributing
169 |
170 | Любой желающий может через Pull Request (PR) предложить свою проверку для рассмотрения на добавление в ядро модуля.
171 | При этом необходимо следовать следующим несложным правилам.
172 |
173 | 1. Перед началом работы на проверкой нужно удостовериться что такой проверки еще нет в пакете и в открытых PR
174 | 2. Один PR = одна проверка
175 | 3. Проверка должна быть достаточно общей и подходить таким образом большому числу приложений. Если ваша проверка завязана на ваше приложение, то лучше ей в нём и оставаться
176 | 4. Нужно предусмотреть чтобы проверка не закончилась Fatal Exception/Error при запуске на каком-то другом приложении. Например если в ней используется какой-то дополнительный модуль php, то нужно сделать проверку `extension_loaded()` и `$this->skip()` или `return false` в случае false в `extension_loaded()`
177 | 5. Проверки должны работать на `"php": ">=5.6.0"`
178 | 6. Из предыдущего пункта следует, что проверка должна по минимуму использовать внешние зависимости (модули php, композер-пакеты и т д)
179 | 7. Если проверка является еще почему-то нереализованным аналогом проверки из битриксовой "Проверки системы", то она должна идти в неймспейс `Arrilot\BitrixSystemCheck\Checks\Bitrix`, в противном случае - в `Arrilot\BitrixSystemCheck\Checks\Custom`
180 | 8. Код должен следовать PSR-2
181 |
--------------------------------------------------------------------------------
/admin/arrilot_systemcheck_check.php:
--------------------------------------------------------------------------------
1 | GetGroupRight('arrilot.systemcheck') < 'W') {
13 | $APPLICATION->AuthForm('Доступ запрещён');
14 | }
15 |
16 | if (!check_bitrix_sessid()) {
17 | echo json_encode([
18 | 'result' => false,
19 | 'message' => 'Ошибка в check_bitrix_sessid()',
20 | ]);
21 | die();
22 | }
23 |
24 | $config = Configuration::getInstance()->get('bitrix-systemcheck');
25 | $monitorings = !empty($config['monitorings']) ? $config['monitorings'] : [];
26 | $monitoringIsFound = false;
27 | foreach ($monitorings as $monitoringClass) {
28 | /** @var \Arrilot\BitrixSystemCheck\Monitorings\Monitoring $monitoring */
29 | $monitoring = new $monitoringClass;
30 | if ($monitoring->code() == $_POST['monitoring']) {
31 | $monitoringIsFound = true;
32 | break;
33 | }
34 | }
35 |
36 | if (!$monitoringIsFound) {
37 | echo json_encode([
38 | 'result' => false,
39 | 'message' => 'Не найден мониторинг',
40 | ]);
41 | die();
42 | }
43 |
44 | $index = (int) $_POST['index'];
45 |
46 | if ($index === 0) {
47 | $monitoring->getDataStorage()->cleanOutdatedData($monitoring->dataTtlDays);
48 | }
49 |
50 | foreach ($monitoring->checks() as $i => $check) {
51 | if ($i === $index) {
52 | $check->setDataStorage($monitoring->getDataStorage());
53 |
54 | $messages = [];
55 | $result = false;
56 | try {
57 | if ($check->run()) {
58 | $result = true;
59 | } else {
60 | $result = false;
61 | foreach ($check->getMessages() as $errorMessage) {
62 | $messages[] = $errorMessage;
63 | }
64 | }
65 | } catch (SkipCheckException $e) {
66 | $result = null;
67 | $messages[] = $e->getMessage();
68 | } catch (FailCheckException $e) {
69 | $result = false;
70 | $messages[] = $e->getMessage();
71 | } catch (Exception $e) {
72 | $result = false;
73 | $messages[] = $e->getMessage();
74 | }
75 |
76 | echo json_encode([
77 | 'result' => $result,
78 | 'message' => implode('
', $messages),
79 | ]);
80 | die();
81 | }
82 | }
83 |
84 | echo json_encode([
85 | 'result' => false,
86 | 'message' => 'Проверка не найдена',
87 | ]);
88 |
89 |
--------------------------------------------------------------------------------
/admin/arrilot_systemcheck_monitoring.php:
--------------------------------------------------------------------------------
1 | GetGroupRight('arrilot.systemcheck') < 'W') {
14 | $APPLICATION->AuthForm('Доступ запрещён');
15 | }
16 |
17 | require_once $_SERVER['DOCUMENT_ROOT'].'/bitrix/modules/main/include/prolog_admin_after.php';
18 |
19 | $APPLICATION->SetTitle('Мониторинг');
20 |
21 | if (empty($_GET['code'])) {
22 | CAdminMessage::ShowMessage(array('MESSAGE' => 'Не указан код мониторинга', 'TYPE' => 'ERROR'));
23 | return;
24 | }
25 |
26 | $config = Configuration::getInstance()->get('bitrix-systemcheck');
27 | $monitorings = !empty($config['monitorings']) ? $config['monitorings'] : [];
28 | $monitoringIsFound = false;
29 | foreach ($monitorings as $monitoringClass) {
30 | /** @var \Arrilot\BitrixSystemCheck\Monitorings\Monitoring $monitoring */
31 | $monitoring = new $monitoringClass;
32 | if ($monitoring->code() == $_GET['code']) {
33 | $monitoringIsFound = true;
34 | break;
35 | }
36 | }
37 |
38 |
39 | if (!$monitoringIsFound) {
40 | CAdminMessage::ShowMessage(array('MESSAGE' => 'Мониторинг с указанным кодом не найден', 'TYPE' => 'ERROR'));
41 | return;
42 | }
43 |
44 | $checks = $monitoring->checks();
45 |
46 | $APPLICATION->SetTitle('Мониторинг "' . $monitoring->name() . '"');
47 |
48 | ?>
49 |
85 |
86 |
87 |
88 |
89 |
90 |
98 |
99 |
100 | foreach ($checks as $i => $check): ?>
101 |
102 | = htmlspecialcharsbx($check->name()) ?> |
103 | |
104 | endforeach ?>
105 |
106 |
107 |
159 |
160 | require_once $_SERVER['DOCUMENT_ROOT'].'/bitrix/modules/main/include/epilog_admin.php';
161 |
--------------------------------------------------------------------------------
/admin/menu.php:
--------------------------------------------------------------------------------
1 | get('bitrix-systemcheck');
7 | $monitorings = !empty($config['monitorings']) ? $config['monitorings'] : [];
8 | foreach ($monitorings as $monitoringClass) {
9 | /** @var \Arrilot\BitrixSystemCheck\Monitorings\Monitoring $monitoring */
10 | $monitoring = new $monitoringClass;
11 | $items[] = [
12 | 'text' => $monitoring->name(),
13 | 'title' => $monitoring->name(),
14 | 'url' => 'arrilot_systemcheck_monitoring.php?code=' . $monitoring->code(),
15 | ];
16 | }
17 |
18 | return [
19 | 'parent_menu' => 'global_menu_settings',
20 | 'sort' => 2800,
21 | 'text' => 'Мониторинги',
22 | 'title' => 'Мониторинги',
23 | 'icon' => 'util_menu_icon',
24 | 'page_icon' => 'util_page_icon',
25 | 'items_id' => 'menu_monitorings',
26 | 'items' => $items,
27 | ];
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "arrilot/bitrix-systemcheck",
3 | "license": "MIT",
4 | "keywords": [
5 | "bitrix"
6 | ],
7 | "authors": [
8 | {
9 | "name": "Nekrasov Ilya",
10 | "email": "nekrasov.ilya90@gmail.com"
11 | }
12 | ],
13 | "homepage": "https://github.com/arrilot/bitrix-systemcheck",
14 | "type": "bitrix-d7-module",
15 | "extra": {
16 | "installer-name": "arrilot.systemcheck"
17 | },
18 | "require": {
19 | "php": ">=5.6.0",
20 | "composer/installers": "~1",
21 | "symfony/console": "~3 || ~4",
22 | "psr/log": "^1.1"
23 | },
24 | "suggest": {
25 | "sensiolabs/security-checker": "Required to use the composer class (>=5.0)."
26 | },
27 | "autoload": {
28 | "psr-4": {
29 | "Arrilot\\BitrixSystemCheck\\": "src/"
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/include.php:
--------------------------------------------------------------------------------
1 | MODULE_VERSION = '0.1.1';
16 | $this->MODULE_VERSION_DATE = '2019-01-01';
17 |
18 | $this->MODULE_NAME = 'Bitrix System Checks';
19 | $this->MODULE_DESCRIPTION = 'Производит мониторинг приложения';
20 | $this->MODULE_GROUP_RIGHTS = 'Y';
21 | }
22 |
23 | public function DoInstall()
24 | {
25 | ModuleManager::registerModule($this->MODULE_ID);
26 | $this->InstallFiles();
27 | $this->InstallDB();
28 | }
29 |
30 | public function DoUninstall()
31 | {
32 | $this->UnInstallFiles();
33 | $this->UnInstallDB();
34 | ModuleManager::unRegisterModule($this->MODULE_ID);
35 | }
36 |
37 | public function InstallFiles()
38 | {
39 | CopyDirFiles(__DIR__ ."/admin", $_SERVER["DOCUMENT_ROOT"]."/bitrix/admin", true, true);
40 | return true;
41 | }
42 |
43 | public function UnInstallFiles()
44 | {
45 | DeleteDirFiles(__DIR__ . "/admin", $_SERVER["DOCUMENT_ROOT"]."/bitrix/admin");
46 | return true;
47 | }
48 |
49 | public function InstallDB()
50 | {
51 | $connection = $connection = Application::getConnection();
52 |
53 | $connection->query('DROP TABLE IF EXISTS arrilot_systemcheck_checks_data');
54 |
55 | $sql = "
56 | CREATE TABLE `arrilot_systemcheck_checks_data` (
57 | `ID` INT NOT NULL AUTO_INCREMENT,
58 | `MONITORING` VARCHAR(256) NOT NULL,
59 | `CHECK` VARCHAR(256) NOT NULL,
60 | `DATA` TEXT NOT NULL,
61 | `CREATED_AT` DATETIME NOT NULL,
62 | PRIMARY KEY (id),
63 | INDEX IX_CREATED_AT (`CREATED_AT`),
64 | INDEX IX_MONITORING (`MONITORING`),
65 | INDEX IX_CHECK (`CHECK`)
66 | );";
67 | $connection->query($sql);
68 |
69 | $eventManager = \Bitrix\Main\EventManager::getInstance();
70 | $eventManager->registerEventHandler(
71 | 'main',
72 | 'OnBuildGlobalMenu',
73 | $this->MODULE_ID,
74 | '\Arrilot\BitrixSystemCheck\EventHandlers',
75 | 'addMonitoringPageToAdminMenu'
76 | );
77 |
78 | return true;
79 | }
80 |
81 | public function UnInstallDB()
82 | {
83 | $connection = $connection = Application::getConnection();
84 |
85 | $connection->query('DROP TABLE IF EXISTS arrilot_systemcheck_checks_data');
86 |
87 | $eventManager = \Bitrix\Main\EventManager::getInstance();
88 | $eventManager->unRegisterEventHandler(
89 | 'main',
90 | 'OnBuildGlobalMenu',
91 | $this->MODULE_ID,
92 | '\Arrilot\BitrixSystemCheck\EventHandlers',
93 | 'addMonitoringPageToAdminMenu'
94 | );
95 |
96 | return true;
97 | }
98 | }
--------------------------------------------------------------------------------
/install/version.php:
--------------------------------------------------------------------------------
1 | '0.1.1',
5 | 'VERSION_DATE' => '2019-01-01'
6 | ];
--------------------------------------------------------------------------------
/options.php:
--------------------------------------------------------------------------------
1 |
2 | $module_id = "arrilot.systemcheck";
3 | $RIGHT = $APPLICATION->GetGroupRight($module_id);
4 | if ($RIGHT >= "R") :
5 |
6 | IncludeModuleLangFile($_SERVER["DOCUMENT_ROOT"].BX_ROOT."/modules/main/options.php");
7 | IncludeModuleLangFile(__FILE__);
8 |
9 | $aTabs = array(
10 | array("DIV" => "edit1", "TAB" => GetMessage("MAIN_TAB_RIGHTS"), "ICON" => "perfmon_settings", "TITLE" => GetMessage("MAIN_TAB_TITLE_RIGHTS")),
11 | );
12 | $tabControl = new CAdminTabControl("tabControl", $aTabs);
13 |
14 | CModule::IncludeModule($module_id);
15 |
16 | if ($REQUEST_METHOD == "POST" && strlen($Update.$Apply.$RestoreDefaults) > 0 && $RIGHT == "W" && check_bitrix_sessid())
17 | {
18 | require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/perfmon/prolog.php");
19 |
20 | if ($_REQUEST["clear_data"] === "y")
21 | {
22 | CPerfomanceComponent::Clear();
23 | CPerfomanceSQL::Clear();
24 | CPerfomanceHit::Clear();
25 | CPerfomanceError::Clear();
26 | CPerfomanceCache::Clear();
27 | }
28 |
29 | if (array_key_exists("ACTIVE", $_REQUEST))
30 | {
31 | $ACTIVE = intval($_REQUEST["ACTIVE"]);
32 | CPerfomanceKeeper::SetActive($ACTIVE > 0, time() + $ACTIVE);
33 | }
34 |
35 | if (strlen($RestoreDefaults) > 0)
36 | {
37 | COption::RemoveOption("perfmon");
38 | }
39 | else
40 | {
41 | foreach ($arAllOptions as $arOption)
42 | {
43 | $name = $arOption[0];
44 | $val = $_REQUEST[$name];
45 | if ($arOption[2][0] == "checkbox" && $val != "Y")
46 | $val = "N";
47 | COption::SetOptionString("perfmon", $name, $val, $arOption[1]);
48 | }
49 | }
50 |
51 | ob_start();
52 | $Update = $Update.$Apply;
53 | require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/admin/group_rights.php");
54 | ob_end_clean();
55 |
56 | if (strlen($_REQUEST["back_url_settings"]) > 0)
57 | {
58 | if ((strlen($Apply) > 0) || (strlen($RestoreDefaults) > 0))
59 | LocalRedirect($APPLICATION->GetCurPage()."?mid=".urlencode($module_id)."&lang=".urlencode(LANGUAGE_ID)."&back_url_settings=".urlencode($_REQUEST["back_url_settings"])."&".$tabControl->ActiveTabParam());
60 | else
61 | LocalRedirect($_REQUEST["back_url_settings"]);
62 | }
63 | else
64 | {
65 | LocalRedirect($APPLICATION->GetCurPage()."?mid=".urlencode($module_id)."&lang=".urlencode(LANGUAGE_ID)."&".$tabControl->ActiveTabParam());
66 | }
67 | }
68 |
69 | ?>
70 |
107 | endif; ?>
108 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | tests
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Checks/Bitrix/AgentsUsingCronCheck.php:
--------------------------------------------------------------------------------
1 | logError('Агенты не выполняются на cron');
24 | $result = false;
25 | }
26 |
27 | return $result;
28 | }
29 |
30 | /**
31 | * Получаем название проверки
32 | *
33 | * @return string
34 | */
35 | public function name()
36 | {
37 | return 'Проверка выполнения агентов на cron...';
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Checks/Bitrix/ApacheModules.php:
--------------------------------------------------------------------------------
1 | blacklist as $module) {
27 | if (in_array($module, $loadedModules)) {
28 | $this->logError("Необходимо выключить модуль $module");
29 | $result = false;
30 | }
31 | }
32 |
33 | return $result;
34 | }
35 |
36 | /**
37 | * @return string
38 | */
39 | public function name()
40 | {
41 | return "Проверка модулей apache...";
42 | }
43 | }
--------------------------------------------------------------------------------
/src/Checks/Bitrix/CacheDirPermissionsCheck.php:
--------------------------------------------------------------------------------
1 | logError("Не удалось создать файл $file1 в директории $dir");
33 | fclose($f);
34 | return false;
35 | }
36 |
37 | fclose($f);
38 |
39 | if (!rename($file1, $file2)) {
40 | $this->logError("Не удалось создать переименовать $file1 в $file2 в директории $dir");
41 | return false;
42 | }
43 |
44 | if (!unlink($file2)) {
45 | $this->logError("Не удалось удалить $file2 в директории $dir");
46 | return false;
47 | }
48 |
49 | return true;
50 | }
51 |
52 | /**
53 | * Получаем название проверки
54 | *
55 | * @return string
56 | */
57 | public function name()
58 | {
59 | return 'Проверка работы с файлами кеша...';
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Checks/Bitrix/ConfigFilesExcessOutputCheck.php:
--------------------------------------------------------------------------------
1 | extraFiles = [];
26 | }
27 |
28 | /**
29 | * Проверяем указанный файл
30 | *
31 | * @param string $filePath - Путь до файла, который необходимо проверить
32 | * @return void
33 | */
34 | private function checkFile($filePath)
35 | {
36 | if (!file_exists($filePath)) {
37 | return;
38 | }
39 |
40 | $file = file_get_contents($filePath);
41 | if (preg_match('/<\?php(.+?)\?>.*?[^<\?]/s', $file) || preg_match('/[\s\S]<\?php/', $file)) {
42 | $this->logError('В файле ' . $filePath . ' обнаружены лишние символы (пробелы, переносы и т.д.)');
43 | $this->result = false;
44 | }
45 | }
46 |
47 | /**
48 | * Запускаем проверку
49 | *
50 | * @return boolean
51 | */
52 | public function run()
53 | {
54 | $this->result = true;
55 | $bitrixDir = $_SERVER['DOCUMENT_ROOT'] . BX_PERSONAL_ROOT;
56 | $localDir = $_SERVER['DOCUMENT_ROOT'] . '/local';
57 |
58 | $this->checkFile($bitrixDir . '/php_interface/dbconn.php');
59 | $this->checkFile($bitrixDir . '/php_interface/init.php');
60 | $this->checkFile($bitrixDir . '/php_interface/after_connect.php');
61 | $this->checkFile($bitrixDir . '/php_interface/after_connect_d7.php');
62 | $this->checkFile($localDir . '/php_interface/init.php');
63 | foreach ($this->extraFiles as $extraFile) {
64 | $this->checkFile($extraFile);
65 | }
66 |
67 | return $this->result;
68 | }
69 |
70 | /**
71 | * Получаем название проверки
72 | *
73 | * @return string
74 | */
75 | public function name()
76 | {
77 | return 'Проверка лишнего вывода в файлах конфигурации...';
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Checks/Bitrix/DataBaseCheck.php:
--------------------------------------------------------------------------------
1 | [
30 | 'character' => 'utf8',
31 | 'collations' => [
32 | 'utf8_unicode_ci',
33 | 'utf8_general_ci'
34 | ]
35 | ],
36 | 'cp1251' => [
37 | 'character' => 'cp1251',
38 | 'collations' => [
39 | 'cp1251_unicode_ci',
40 | 'cp1251_general_ci'
41 | ]
42 | ]
43 | ];
44 |
45 | /** @var array $databaseCharsetInfo - Информация о кодировке базы данных */
46 | private $databaseCharsetInfo;
47 |
48 | /**
49 | * DataBaseCheck constructor.
50 | *
51 | * @param string $charset - Кодировка
52 | */
53 | public function __construct($charset = 'utf-8')
54 | {
55 | $this->charset = strtolower($charset);
56 | $this->connection = Application::getConnection();
57 | }
58 |
59 | /**
60 | * Проверяем версию MySQL
61 | *
62 | * @return void
63 | */
64 | private function mysqlVersionCheck()
65 | {
66 | $currentMysqlVersion = $this->connection->query('SELECT @@version')->fetch()['@@version'];
67 | if (strstr($currentMysqlVersion, '5.0.41') || strstr($currentMysqlVersion, '5.1.34')) {
68 | $this->logError('В текущей версии mysql возможны ошибки. Необходимо обновить версию');
69 | $this->result = false;
70 | }
71 | }
72 |
73 | /**
74 | * Сравниваем время, которое отдает веб-сервер, со временем, отдаваемым MySQL
75 | *
76 | * @return void
77 | */
78 | private function compareServerAndMysqlTimestamps()
79 | {
80 | $serverTimestamp = (new DateTime)->getTimestamp();
81 | $mysqlTimestamp = strtotime($this->connection->query('SELECT NOW()')->fetch()['NOW()']);
82 |
83 | if (($serverTimestamp - $mysqlTimestamp) > 10) {
84 | $this->logError('Время в MySQL отличается от времени в php более чем на 10 секунд');
85 | $this->result = false;
86 | }
87 | }
88 |
89 | /**
90 | * Проверяем параметр mysql_mode
91 | *
92 | * @return void
93 | */
94 | private function checkSqlMode()
95 | {
96 | if ($this->connection->query('SELECT @@sql_mode')->fetch()['@@sql_mode']) {
97 | $this->logError('Необходимо установить параметру sql_mode значение "" (пустая строка)');
98 | $this->result = false;
99 | }
100 | }
101 |
102 | /**
103 | * Проверяем кодировку подключения
104 | *
105 | * @return void
106 | */
107 | private function checkConnectionCharset()
108 | {
109 | // TODO переделать
110 | return;
111 | // $query = $this->connection->query("SHOW SESSION VARIABLES WHERE Variable_name = 'collation_connection'
112 | // OR Variable_name = 'character_set_connection'");
113 | // while ($info = $query->fetch()) {
114 | // if ($info['Variable_name'] == 'character_set_connection') {
115 | // if ($info['Value'] != $this->databaseCharsetSettings[$this->charset]['character']) {
116 | // $this->logError('Неверная кодировка подключения');
117 | // $this->result = false;
118 | // }
119 | // } elseif ($info['Variable_name'] == 'collation_connection') {
120 | // if (!in_array($info['Value'], $this->databaseCharsetSettings[$this->charset]['collations'])) {
121 | // $this->logError('Неверное сравнение подключения');
122 | // $this->result = false;
123 | // }
124 | // }
125 | // }
126 | }
127 |
128 | /**
129 | * Проверяем кодировку и сравнение БД
130 | *
131 | * @return void
132 | */
133 | private function checkDatabaseCharset()
134 | {
135 | // TODO переделать
136 | return;
137 | // $sortField = 'sort';
138 | // $sortOrder = 'asc';
139 | // $sitesQuery = CSite::GetList($sortField, $sortOrder, []);
140 | // while ($siteInfo = $sitesQuery->GetNext()) {
141 | // $charset = strtolower($siteInfo['CHARSET']);
142 | // if ($this->databaseCharsetInfo['DEFAULT_CHARACTER_SET_NAME']
143 | // != $this->databaseCharsetSettings[$charset]['character']) {
144 | // $this->logError('Кодировка сайта не совпадает с кодировкой БД');
145 | // $this->result = false;
146 | // }
147 | // if (!in_array(
148 | // $this->databaseCharsetInfo['DEFAULT_COLLATION_NAME'],
149 | // $this->databaseCharsetSettings[$charset]['collations']
150 | // )) {
151 | // $this->logError('Для сайта с кодировкой ' . $siteInfo['CHARSET']
152 | // . ' необходимо сравнение из списка'
153 | // . implode('; ', $this->databaseCharsetSettings[$charset]['collation']));
154 | // $this->result = false;
155 | // }
156 | // }
157 | }
158 |
159 | /**
160 | * Проверяем кодировку всех таблиц в БД
161 | *
162 | * @return void
163 | */
164 | private function checkTablesCharset()
165 | {
166 | // TODO доделать эту проверку, сейчас она реализована некорректно и зачем-то проверяет все БД.
167 | return;
168 | // /** @var array $typesNotForCheck - Массив типов полей таблицы, у которых не надо смотреть кодировку */
169 | // $typesNotForCheck = [
170 | // 'int',
171 | // 'timestamp',
172 | // 'datetime',
173 | // 'date',
174 | // 'blob',
175 | // 'mediumblob',
176 | // 'bigint',
177 | // 'decimal',
178 | // 'double',
179 | // 'tinyint',
180 | // 'smallint',
181 | // 'float',
182 | // 'longblob',
183 | // 'varbinary',
184 | // 'time'
185 | // ];
186 | //
187 | // /** @var \Bitrix\Main\DB\MysqliResult $tablesQuery - Все таблицы и их поля в базе данных */
188 | // $tablesQuery = $this->connection->query(
189 | // 'SELECT TABLE_NAME, TABLE_COLLATION, COLUMN_NAME, DATA_TYPE, COLLATION_NAME as COLUMN_COLLATION
190 | // FROM information_schema.columns
191 | // RIGHT JOIN information_schema.tables USING(TABLE_NAME)
192 | // WHERE table_type = "base table"'
193 | // );
194 | //
195 | // /**
196 | // * @var array $anotherCharsetTables - Массив таблиц, у которых кодировка (или кодировка каких-либо их полей)
197 | // * отличается от кодировки бд
198 | // */
199 | // $anotherCharsetTables = [];
200 | // while ($table = $tablesQuery->fetch()) {
201 | // if (!in_array($table['DATA_TYPE'], $typesNotForCheck)) {
202 | // if ($table['TABLE_COLLATION'] != $this->databaseCharsetInfo['DEFAULT_COLLATION_NAME']) {
203 | // if (!array_key_exists($table['TABLE_NAME'], $anotherCharsetTables)) {
204 | // $anotherCharsetTables[$table['TABLE_NAME']] = [];
205 | // }
206 | // }
207 | // if ($table['COLUMN_COLLATION'] != $this->databaseCharsetInfo['DEFAULT_COLLATION_NAME']) {
208 | // $anotherCharsetTables[$table['TABLE_NAME']][] = $table['COLUMN_NAME'];
209 | // }
210 | // }
211 | // }
212 | //
213 | // /** @var int $errorTablesCount - Количество таблиц, в которых кодировка отличается от кодировки БД */
214 | // $errorTablesCount = count($anotherCharsetTables);
215 | // if ($errorTablesCount) {
216 | // $this->logError('Найдено ' . $errorTablesCount . ' таблиц (и полей в них) с отличными от БД кодировками: '
217 | // . implode('; ', array_keys($anotherCharsetTables)));
218 | // $this->result = false;
219 | // }
220 | }
221 |
222 | /**
223 | * Запускаем проверку
224 | *
225 | * @return boolean
226 | */
227 | public function run()
228 | {
229 | // /** @var \stdClass $charsetInfo - Объект, описывающий кодировку БД */
230 | // try {
231 | // $this->databaseCharsetInfo = $this->connection->query('SELECT * FROM information_schema.SCHEMATA
232 | // WHERE schema_name = "' . $this->connection->getDatabase() . '"')->fetch();
233 | // } catch (\Exception $e) {
234 | // $this->logError($e->getMessage());
235 | // return false;
236 | // }
237 |
238 | // if (!$this->databaseCharsetInfo) {
239 | // $this->logError('Не удалось получить информацию о кодировки БД');
240 | // return false;
241 | // }
242 |
243 | $this->mysqlVersionCheck();
244 | $this->compareServerAndMysqlTimestamps();
245 | $this->checkSqlMode();
246 | $this->checkConnectionCharset();
247 | $this->checkDatabaseCharset();
248 | $this->checkTablesCharset();
249 |
250 | return $this->result;
251 | }
252 |
253 | /**
254 | * Получаем название проверки
255 | *
256 | * @return string
257 | */
258 | public function name()
259 | {
260 | return 'Проверка базы данных...';
261 | }
262 | }
263 |
--------------------------------------------------------------------------------
/src/Checks/Bitrix/DataBaseConfigsMatchForBothCores.php:
--------------------------------------------------------------------------------
1 | get('connections')['default'];
27 |
28 | $this->check('host', $connectionsSettings['host'], $DBHost);
29 | $this->check('database', $connectionsSettings['database'], $DBName);
30 | $this->check('login', $connectionsSettings['login'], $DBLogin);
31 | $this->check('password', $connectionsSettings['password'], $DBPassword);
32 |
33 | return $this->result;
34 | }
35 |
36 | /**
37 | * Производим сравнение параметров из dbconn.php и .settings.php
38 | *
39 | * @param $paramName - Название параметра
40 | * @param $settingsParam - Параметр в файле .settings.php
41 | * @param $dbconnParam - Параметр в файле dbconn.php
42 | */
43 | private function check($paramName, $settingsParam, $dbconnParam)
44 | {
45 | if ($settingsParam !== $dbconnParam) {
46 | $this->logError(
47 | 'Параметр ' . $paramName
48 | . ' в dbconn.php не соответствует этому же параметру в .settings.php'
49 | );
50 | $this->result = false;
51 | }
52 | }
53 |
54 | /**
55 | * Получаем название проверки
56 | *
57 | * @return string
58 | */
59 | public function name()
60 | {
61 | return 'Проверка совпадения параметров подключения к базе данных в старом и новом ядре...';
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Checks/Bitrix/EmailsCanBeSend.php:
--------------------------------------------------------------------------------
1 | check_mail()) {
24 | $this->logError('Отправка почтового сообщения не удалась');
25 | return false;
26 | }
27 |
28 | if (!$siteChecker->check_mail_big()) {
29 | $this->logError('Отправка большого почтового сообщения не удалась');
30 | return false;
31 | }
32 |
33 | return true;
34 | }
35 |
36 | /**
37 | * Получаем название проверки
38 | *
39 | * @return string
40 | */
41 | public function name()
42 | {
43 | return 'Проверка отправки email...';
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Checks/Bitrix/EncodingSettingsAreCorrect.php:
--------------------------------------------------------------------------------
1 | encoding = strtoupper($encoding);
35 | $this->skipFuncOverloadForConsole = $skipFuncOverloadForConsole;
36 | }
37 |
38 | /**
39 | * Проверяем настройки для кодировки utf-8
40 | *
41 | * @return void
42 | */
43 | private function checkUtfSettings()
44 | {
45 | /** @var array $parameters - Параметры, которые необходимо проверить */
46 | $parameters = [
47 | [
48 | 'name' => 'utf_mode',
49 | 'value' => true,
50 | 'action' => 'checkBitrixConfiguration'
51 | ],
52 | [
53 | 'name' => 'BX_UTF',
54 | 'value' => true,
55 | 'action' => 'checkConstants'
56 | ]
57 | ];
58 |
59 | if (!$this->inConsole() || !$this->skipFuncOverloadForConsole) {
60 | $parameters[] = [
61 | 'name' => 'mbstring.func_overload',
62 | 'value' => 2,
63 | 'action' => 'checkIniSettings'
64 | ];
65 | }
66 |
67 | $this->checkEncodingSettings($parameters);
68 | }
69 |
70 | /**
71 | * Проверяем настройки для кодировки cp1251
72 | *
73 | * @return void
74 | */
75 | private function checkCp1251Settings()
76 | {
77 | /** @var array $parameters - Параметры, которые необходимо проверить */
78 | $parameters = [
79 | [
80 | 'name' => 'utf_mode',
81 | 'value' => false,
82 | 'action' => 'checkBitrixConfiguration'
83 | ],
84 | ];
85 |
86 | if (!$this->inConsole() || !$this->skipFuncOverloadForConsole) {
87 | $parameters[] = [
88 | 'name' => 'mbstring.func_overload',
89 | 'value' => 0,
90 | 'action' => 'checkIniSettings'
91 | ];
92 | }
93 |
94 | $this->checkEncodingSettings($parameters);
95 | }
96 |
97 | /**
98 | * Проверяем настройки кодировки
99 | *
100 | * @param array $parameters - Массив параметров, который необходимо проверить
101 | * @return void
102 | */
103 | private function checkEncodingSettings($parameters)
104 | {
105 | /** @var array $commonParameters - Параметры, общие для всех кодировок */
106 | $commonParameters = [];
107 | // $commonParameters = [
108 | // [
109 | // 'name' => 'default_charset',
110 | // 'value' => $this->encoding,
111 | // 'action' => 'checkIniSettings'
112 | // ]
113 | // ];
114 |
115 | /** @var array $parameters - Массив параметров, которые необходимо проверить */
116 | $parameters = array_merge($parameters, $commonParameters);
117 | foreach ($parameters as $parameter) {
118 | $action = $parameter['action'];
119 | $this->$action($parameter['name'], $parameter['value']);
120 | }
121 | }
122 |
123 | /**
124 | * Выполняем проверки параметров в php.ini
125 | *
126 | * @param $name - Название параметра
127 | * @param $value - Значение, которому должен соответствовать параметр
128 | * @return void
129 | */
130 | private function checkIniSettings($name, $value)
131 | {
132 | if (ini_get($name) != $value) {
133 | $valueString = $this->formatValuesAsString($value);
134 | $this->logError("В php.ini параметр $name должен иметь значение $valueString");
135 | $this->result = false;
136 | }
137 | }
138 |
139 | /**
140 | * Проверяем конфигурацию
141 | *
142 | * @param $name - Название параметра
143 | * @param $value - Значение, которому должен соответствовать параметр
144 | * @return void
145 | */
146 | private function checkBitrixConfiguration($name, $value)
147 | {
148 | if (Configuration::getInstance()->get($name) != $value) {
149 | $valueString = $this->formatValuesAsString($value);
150 | $this->logError("В .settings.php параметр $name должен иметь значение $valueString");
151 | $this->result = false;
152 | }
153 | }
154 |
155 | /**
156 | * Проверяем константы
157 | *
158 | * @param $name - Название параметра
159 | * @param $value - Значение, которому должен соответствовать параметр
160 | * @return void
161 | */
162 | private function checkConstants($name, $value)
163 | {
164 | if (constant($name) != $value) {
165 | $valueString = $this->formatValuesAsString($value);
166 | $this->logError("В dbconn.php константа $name должна иметь значение $valueString");
167 | $this->result = false;
168 | }
169 | }
170 |
171 | /**
172 | * Выполняем проверку
173 | *
174 | * @return boolean
175 | */
176 | public function run()
177 | {
178 | $this->encoding === 'UTF-8' ? $this->checkUtfSettings() : $this->checkCp1251Settings();
179 |
180 | return $this->result;
181 | }
182 |
183 | /**
184 | * Получаем название проверки
185 | *
186 | * @return string
187 | */
188 | public function name()
189 | {
190 | return 'Проверка параметров зависящих от кодировки сайта...';
191 | }
192 |
193 | /**
194 | * @param $value
195 | * @return string
196 | */
197 | protected function formatValuesAsString($value)
198 | {
199 | if ($value === false) {
200 | return "false";
201 | }
202 |
203 | if ($value === null) {
204 | return "null";
205 | }
206 |
207 | if ($value === true) {
208 | return "true";
209 | }
210 |
211 | return (string) $value;
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/src/Checks/Bitrix/Mail.php:
--------------------------------------------------------------------------------
1 | logError('Почтовый сервер не настроен');
37 | }
38 |
39 | $largeMailResult = (new CSiteCheckerTest)->check_mail_big();
40 | if (!$largeMailResult) {
41 | $this->logError('Отправка большого почтового сообщения не удалась');
42 | }
43 | }
44 |
45 | return $defaultMailResult && $largeMailResult;
46 | }
47 |
48 | /**
49 | * Получаем название проверки
50 | *
51 | * @return string
52 | */
53 | public function name()
54 | {
55 | return 'Проверка почтового сервера...';
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Checks/Bitrix/PhpSettings.php:
--------------------------------------------------------------------------------
1 | logError('Версия PHP должна быть >=' . $minVersion);
20 | }
21 |
22 | $arRequiredParams = array(
23 | 'safe_mode' => 0,
24 | 'file_uploads' => 1,
25 | // 'session.cookie_httponly' => 0, # 14.0.1:main/include.php:ini_set("session.cookie_httponly", "1");
26 | 'wincache.chkinterval' => 0,
27 | 'session.auto_start' => 0,
28 | 'magic_quotes_runtime' => 0,
29 | 'magic_quotes_sybase' => 0,
30 | 'magic_quotes_gpc' => 0,
31 | 'arg_separator.output' => '&',
32 | 'register_globals' => 0,
33 | 'zend.multibyte' => 0
34 | );
35 |
36 | foreach($arRequiredParams as $param => $val) {
37 | if ($param === 'session.auto_start' && $this->inConsole()) {
38 | continue;
39 | }
40 |
41 | $cur = ini_get($param);
42 | if (strtolower($cur) == 'on')
43 | $cur = 1;
44 | elseif (strtolower($cur) == 'off') {
45 | $cur = 0;
46 | }
47 |
48 | if ($cur != $val) {
49 | $result = false;
50 | $this->logError(sprintf('Параметр %s = %s, требуется %s', $param, $cur ? htmlspecialcharsbx($cur) : 'off', $val ? 'on' : 'off'));
51 | }
52 | }
53 |
54 | $param = 'default_socket_timeout';
55 | if (($cur = ini_get($param)) < 60) {
56 | $result = false;
57 | $this->logError(sprintf('Параметр %s = %s, требуется %s', $param, $cur ? htmlspecialcharsbx($cur) : 'off', '60'));
58 | }
59 |
60 | if (!$this->inConsole() && ($m = ini_get('max_input_vars')) && $m < 10000) {
61 | $result = false;
62 | $this->logError(sprintf('Значение max_input_vars должно быть не ниже %s. Текущее значение: %s', 10000, $m));
63 | }
64 |
65 | if (($vm = getenv('BITRIX_VA_VER')) && version_compare($vm, '4.2.0','<')) {
66 | $result = false;
67 | $this->logError('Вы используете Битрикс веб-окружение старой версии, установите актуальную версию чтобы не было проблем с настройкой сервера.');
68 | }
69 |
70 | // check_divider
71 | $locale_info = localeconv();
72 | $delimiter = $locale_info['decimal_point'];
73 | if ($delimiter != '.') {
74 | $result = false;
75 | $this->logError(sprintf('Текущий разделитель: %s, требуется .', $delimiter));
76 | }
77 |
78 | // check_precision
79 | if (1234567891 != (string) doubleval(1234567891)) {
80 | $result = false;
81 | $this->logError('Параметр precision имеет неверное значение');
82 | }
83 |
84 | // check_suhosin
85 | if (in_array('suhosin',get_loaded_extensions()) && !ini_get('suhosin.simulation')) {
86 | $result = false;
87 | $this->logError(sprintf('Загружен модуль suhosin, возможны проблемы в работе административной части (suhosin.simulation=%s)', ini_get('suhosin.simulation') ? 1 : 0));
88 | }
89 |
90 | // check_backtrack_limit
91 | $param = 'pcre.backtrack_limit';
92 | $cur = CSiteCheckerTest::Unformat(ini_get($param));
93 | ini_set($param,$cur + 1);
94 | $new = ini_get($param);
95 | if ($new != $cur + 1) {
96 | $result = false;
97 | $this->logError('Нет возможности изменить значение pcre.backtrack_limit через ini_set');
98 | }
99 |
100 | return $result;
101 | }
102 |
103 | /**
104 | * @return string
105 | */
106 | public function name()
107 | {
108 | return "Корректность необходимых Битриксу настроек php...";
109 | }
110 | }
--------------------------------------------------------------------------------
/src/Checks/Bitrix/RequiredPhpModules.php:
--------------------------------------------------------------------------------
1 | requiredExtension as $extension) {
25 | if (!extension_loaded($extension)) {
26 | $this->logError("модуль $extension не подключен");
27 | $result = false;
28 | }
29 | }
30 |
31 | return $result;
32 | }
33 |
34 | /**
35 | * @return string
36 | */
37 | public function name()
38 | {
39 | return "Наличие необходимых Битриксу модулей php...";
40 | }
41 | }
--------------------------------------------------------------------------------
/src/Checks/Bitrix/ServiceScriptsAreRemoved.php:
--------------------------------------------------------------------------------
1 | documentRoot = realpath($_SERVER['DOCUMENT_ROOT'] . '/');
36 | $this->filesArray = scandir($this->documentRoot);
37 | }
38 |
39 | /**
40 | * Выполняем проверку
41 | *
42 | * @return boolean
43 | */
44 | public function run()
45 | {
46 | /** @var bool $result - Результат проверки */
47 | $result = true;
48 | foreach ($this->blackList as $file) {
49 | /** Если указано расширение файла, то смотрим все файл по регулярке, иначе по полному имени */
50 | if (preg_match('\'\\*.+\'', $file, $matches)) {
51 | /** @var string $extension - Расширение, по которому будет производиться поиск */
52 | $extension = substr($matches[0], 1);
53 | foreach ($this->filesArray as $key => $fileInDir) {
54 | if (preg_match('\'' . $extension . '\'', $fileInDir)) {
55 | $this->logErrorMessage($fileInDir);
56 | $result = false;
57 | unset($this->filesArray[$key]);
58 | }
59 | }
60 | } elseif (in_array($file, $this->filesArray)) {
61 | $this->logErrorMessage($file);
62 | $result = false;
63 | }
64 | }
65 |
66 | return $result;
67 | }
68 |
69 | /**
70 | * Логгируем ошибку
71 | *
72 | * @param $file - Название файла
73 | */
74 | private function logErrorMessage($file)
75 | {
76 | $this->logError('Необходимо удалить файл ' . $this->documentRoot . '/' . $file);
77 | }
78 |
79 | /**
80 | * Получаем название проверки
81 | *
82 | * @return string
83 | */
84 | public function name()
85 | {
86 | return 'Проверка наличия служебных скриптов в корне сайта...';
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/Checks/Bitrix/UpdateServerCheck.php:
--------------------------------------------------------------------------------
1 | check_update()) {
24 | $result = false;
25 | $this->logError('Нет соединения с сервером обновлений');
26 | }
27 |
28 | return $result;
29 | }
30 |
31 | /**
32 | * Получаем название проверки
33 | *
34 | * @return string
35 | */
36 | public function name()
37 | {
38 | return 'Проверка доступа к серверу обновлений...';
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Checks/Check.php:
--------------------------------------------------------------------------------
1 | errorMessages;
37 | }
38 |
39 | /**
40 | * @param $message
41 | * @return void
42 | */
43 | protected function logError($message)
44 | {
45 | $this->errorMessages[] = get_class($this) . ': ' . $message;
46 | }
47 |
48 | /**
49 | * Skip check.
50 | *
51 | * @param $message
52 | */
53 | protected function skip($message)
54 | {
55 | throw new SkipCheckException(get_class($this) . ': ' . $message);
56 | }
57 |
58 | /**
59 | * @param array|string $extensions
60 | * @return bool
61 | */
62 | public function checkPhpExtensionsLoaded($extensions)
63 | {
64 | foreach ((array)$extensions as $extension) {
65 | if (!extension_loaded($extension)) {
66 | $this->logError('Не подключен модуль php: ' . $extension);
67 | return false;
68 | }
69 | }
70 |
71 | return true;
72 | }
73 |
74 | /**
75 | * @param array|string $extensions
76 | * @return bool
77 | */
78 | public function checkPhpExtensionsNotLoaded($extensions)
79 | {
80 | foreach ((array)$extensions as $extension) {
81 | if (extension_loaded($extension)) {
82 | $this->logError('Подключен нежелательный для данного окружения модуль php: ' . $extension);
83 | return false;
84 | }
85 | }
86 |
87 | return true;
88 | }
89 |
90 | /**
91 | * Setter for data storage.
92 | *
93 | * @param DataStorage $dataStorage
94 | * @return Check
95 | */
96 | public function setDataStorage(DataStorage $dataStorage)
97 | {
98 | $this->dataStorage = $dataStorage;
99 |
100 | return $this;
101 | }
102 |
103 | /**
104 | * Get Data from lastCheck.
105 | *
106 | * @return array
107 | */
108 | public function getPreviousData()
109 | {
110 | if (!$this->dataStorage) {
111 | return [];
112 | }
113 |
114 | $row = (array)$this->dataStorage->getData(get_class());
115 | if (!$row) {
116 | return [];
117 | }
118 |
119 | $data = json_decode($row['DATA'], true);
120 | if ($data === null) {
121 | $data = [];
122 | }
123 |
124 | if (is_object($row['CREATED_AT'])) {
125 | $data['_created_at'] = $row['CREATED_AT']->getTimestamp();
126 | }
127 |
128 | return $data;
129 | }
130 |
131 | /**
132 | * Save current check Data to storage.
133 | *
134 | * @param array $data
135 | * @return $this
136 | */
137 | public function saveData($data)
138 | {
139 | if ($this->dataStorage) {
140 | $this->dataStorage->saveData(get_class(), $data);
141 | }
142 |
143 | return $this;
144 | }
145 |
146 | /**
147 | * @return bool
148 | */
149 | protected function inConsole()
150 | {
151 | return php_sapi_name() === 'cli';
152 | }
153 |
154 | /**
155 | * Attempt to run $callable $times times with $interval seconds interval until first successful attempt.
156 | *
157 | * @param callable $callable
158 | * @param int $times
159 | * @param int $interval - in seconds
160 | * @return mixed
161 | */
162 | protected function attempt(callable $callable, $times, $interval = 1)
163 | {
164 | $lastError = '';
165 | foreach (range(1, $times) as $i) {
166 | try {
167 | return $callable($i, $times);
168 | } catch (RuntimeException $e) {
169 | $lastError = $e->getMessage();
170 | sleep($interval);
171 | }
172 | }
173 |
174 | $this->logError($lastError);
175 |
176 | return null;
177 | }
178 |
179 | /**
180 | * @param string $url
181 | * @param null|string $basicAuth
182 | * @return bool|null|array
183 | */
184 | protected function getCurlInfo($url, $basicAuth = null)
185 | {
186 | return $this->attempt(function () use ($url, $basicAuth) {
187 | $ch = curl_init();
188 | curl_setopt($ch, CURLOPT_URL, $url);
189 | curl_setopt($ch, CURLOPT_TIMEOUT_MS, 10000);
190 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
191 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
192 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
193 |
194 | if ($basicAuth) {
195 | curl_setopt($ch, CURLOPT_USERPWD, $basicAuth);
196 | }
197 |
198 | $result = curl_exec($ch);
199 | if (!$result) {
200 | $this->logError('При curl запросе к ' . $url . ' произошла ошибка ' . curl_error($ch));
201 | curl_close($ch);
202 | return false;
203 | }
204 | $info = curl_getinfo($ch);
205 | curl_close($ch);
206 |
207 | return $info;
208 | }, 3);
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/src/Checks/Custom/BasicAuthIsTurnedOn.php:
--------------------------------------------------------------------------------
1 | mainPage = $mainPage;
22 | $this->basicAuth = $basicAuth;
23 | }
24 |
25 | /**
26 | * @return string
27 | */
28 | public function name()
29 | {
30 | return "Проверка на наличие basic-auth...";
31 | }
32 |
33 | /**
34 | * @return boolean
35 | */
36 | public function run()
37 | {
38 | $context = stream_context_create([
39 | 'http' => [
40 | 'header' => "Authorization: Basic " . base64_encode($this->basicAuth)
41 | ]
42 | ]);
43 |
44 | $page = $this->mainPage;
45 | $test1 = file_get_contents($page, false, $context);
46 | if ($test1 === false) {
47 | $this->logError('Не удалось открыть ' . $page . ' с реквизитами ' . $this->basicAuth);
48 | return false;
49 | }
50 |
51 | $test2 = @file_get_contents($page);
52 | if ($test2 !== false) {
53 | $this->logError('Страница ' . $page . ' не закрыта базовой авторизацией');
54 | return false;
55 | }
56 |
57 | return true;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Checks/Custom/BitrixDebugIsTurnedOff.php:
--------------------------------------------------------------------------------
1 | get('exception_handling');
24 |
25 | if (!empty($config['debug'])) {
26 | $this->logError('Значение конфигурации exception_handling.debug должно быть false в данном окружении');
27 | return false;
28 | }
29 |
30 | return true;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Checks/Custom/BitrixDebugIsTurnedOn.php:
--------------------------------------------------------------------------------
1 | get('exception_handling');
24 |
25 | if (empty($config['debug'])) {
26 | $this->logError('Значение конфигурации exception_handling.debug должно быть true в данном окружении');
27 | return false;
28 | }
29 |
30 | return true;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Checks/Custom/ComposerSecurityCheck.php:
--------------------------------------------------------------------------------
1 | pathToLockFile = $pathToLockFile;
18 | }
19 |
20 | /**
21 | * @return string
22 | */
23 | public function name()
24 | {
25 | return "Проверка зависимостей, подключенных через composer на предмет известных уязвимостей...";
26 | }
27 |
28 | /**
29 | * @return boolean
30 | */
31 | public function run()
32 | {
33 | if (!class_exists(SecurityChecker::class)) {
34 | $this->skip('Для выполнения данной проверки необходимо установить sensiolabs/security-checker');
35 | }
36 |
37 | $checker = new SecurityChecker();
38 | $result = $checker->check($this->pathToLockFile, 'json');
39 | $alerts = json_decode((string) $result, true);
40 |
41 | if ($alerts) {
42 | $this->logError('Найдены пакеты с известными уязвимостями ' . $result);
43 | return false;
44 | }
45 |
46 | return true;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Checks/Custom/DiskSpaceCheck.php:
--------------------------------------------------------------------------------
1 | limitMegaBytes = $limit;
28 | $this->limitBytes = $limit * pow(1024, 2);
29 | }
30 |
31 | /**
32 | * Запускаем проверку
33 | *
34 | * @return boolean
35 | */
36 | public function run()
37 | {
38 | $result = true;
39 | $freeSpace = disk_free_space('/');
40 | if ($freeSpace < $this->limitBytes) {
41 | $this->logError('На сервере заканчивается свободное место (' . intval($freeSpace / 1024 / 1024) . ' < ' . $this->limitMegaBytes . ' мб)');
42 | $result = false;
43 | }
44 |
45 | return $result;
46 | }
47 |
48 | /**
49 | * Получаем название проверки
50 | *
51 | * @return string
52 | */
53 | public function name()
54 | {
55 | return 'Проверка свободного места на жестком диске...';
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Checks/Custom/ElasticSearchConnection.php:
--------------------------------------------------------------------------------
1 | host = $host;
31 | $this->port = $port;
32 | }
33 |
34 | /**
35 | * @return string
36 | */
37 | public function name()
38 | {
39 | return "Проверка доступности соединения с ElasticSearch...";
40 | }
41 |
42 | /**
43 | * @return boolean
44 | */
45 | public function run()
46 | {
47 | $ch = curl_init();
48 | curl_setopt($ch, CURLOPT_URL, "http://{$this->host}:{$this->port}");
49 | curl_setopt($ch, CURLOPT_TIMEOUT_MS, 10000);
50 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
51 |
52 | $result = curl_exec($ch);
53 | if (!$result) {
54 | $this->logError('Отсутствует соединение с ElasticSearch (' . curl_error($ch) . ')');
55 | curl_close($ch);
56 | return false;
57 | }
58 | curl_close($ch);
59 |
60 | return true;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Checks/Custom/HttpsRedirect.php:
--------------------------------------------------------------------------------
1 | mainPage = $mainPage;
22 | $this->basicAuth = $basicAuth;
23 | }
24 |
25 | /**
26 | * @return string
27 | */
28 | public function name()
29 | {
30 | return "Проверка редиректа с http:// на https://...";
31 | }
32 |
33 | /**
34 | * @return boolean
35 | */
36 | public function run()
37 | {
38 | $url = str_replace('https://', 'http://', $this->mainPage);
39 | $info = $this->getCurlInfo($url, $this->basicAuth);
40 |
41 | if (is_null($info)) {
42 | return false;
43 | }
44 |
45 | if (!isset($info['http_code'])) {
46 | return false;
47 | }
48 |
49 | if ($info['http_code'] !== 301 && $info['http_code'] !== 302) {
50 | $this->logError('При curl запросе к ' . $url . ' получен код ответа ' . $info['http_code'] . ' вместо 301/302');
51 | return false;
52 | }
53 |
54 | if (rtrim($info['redirect_url'], '/') !== rtrim($this->mainPage, '/')) {
55 | $this->logError('При curl запросе к ' . $url . ' получен редирект на ' . $info['redirect_url'] . ' вместо ' . $this->mainPage);
56 | return false;
57 | }
58 |
59 | return true;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Checks/Custom/NewRelicIsLoaded.php:
--------------------------------------------------------------------------------
1 | checkPhpExtensionsLoaded('newrelic');
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Checks/Custom/RamCheck.php:
--------------------------------------------------------------------------------
1 | limitMegaBytes = $limit;
28 | $this->limitKiloBytes = $limit * 1024;
29 | }
30 |
31 | /**
32 | * Запускаем проверку
33 | *
34 | * @return boolean
35 | */
36 | public function run()
37 | {
38 | /** @var bool $result - Результат проверки */
39 | $result = true;
40 | /** @var resource $systemFile - Файл с информацией о памяти */
41 | $systemFile = fopen('/proc/meminfo', 'r');
42 | if (!$systemFile) {
43 | $this->skip('Не удалось открыть файл /proc/meminfo');
44 | }
45 |
46 | $memory = 0;
47 | while ($line = fgets($systemFile)) {
48 | $pieces = [];
49 | if (preg_match('/^MemFree:\s+(\d+)\skB$/', $line, $pieces)) {
50 | $memory = $pieces[1];
51 | break;
52 | }
53 | }
54 | fclose($systemFile);
55 |
56 | if ($memory < $this->limitKiloBytes) {
57 | $this->logError('На сервере мало свободной оперативной памяти (' . intval($memory / 1024) . ' < ' . $this->limitMegaBytes . ' мб)');
58 | $result = false;
59 | }
60 |
61 | return $result;
62 | }
63 |
64 | /**
65 | * Получаем название проверки
66 | *
67 | * @return string
68 | */
69 | public function name()
70 | {
71 | return 'Проверка свободной оперативной памяти...';
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Checks/Custom/RobotsTxt.php:
--------------------------------------------------------------------------------
1 | mainPage = $mainPage;
27 | $this->inProduction = $inProduction;
28 | $this->basicAuth = $basicAuth;
29 | }
30 |
31 | /**
32 | * @return string
33 | */
34 | public function name()
35 | {
36 | return "Проверка содержимого robots.txt...";
37 | }
38 |
39 | /**
40 | * @return boolean
41 | */
42 | public function run()
43 | {
44 | $this->skip('Not implemented yet');
45 | // $content = $this->getRobotsContent();
46 | // if (empty($content)) {
47 | // $this->logError('robots.txt пуст либо сломан');
48 | // return false;
49 | // }
50 | //
51 | // $contentAsArray = explode(PHP_EOL, $content);
52 | // if ($contentAsArray[0] === "# Данный файл не отдается никаким роботам и служит лишь как заглушка на случай неправильной настройки веб-сервера") {
53 | // $this->logError('Веб-сервер не настроен на корректную отдачу robots.txt');
54 | // return false;
55 | // }
56 | //
57 | // return $this->inProduction
58 | // ? $this->checkForProductionContent($content)
59 | // : $this->checkForDevContent($content);
60 | }
61 |
62 | /**
63 | * @param string $content
64 | * @return bool
65 | */
66 | protected function checkForProductionContent($content)
67 | {
68 | // TODO
69 | return true;
70 | }
71 |
72 | /**
73 | * @param string $content
74 | * @return bool
75 | */
76 | protected function checkForDevContent($content)
77 | {
78 | // TODO
79 | return true;
80 | // $index = array_search('User-agent: *', $content);
81 | //
82 | // return $index !== false && !empty($content[$index + 1]) && $content[$index + 1] === 'Disallow: /';
83 | }
84 |
85 | /**
86 | * @return string|false
87 | */
88 | protected function getRobotsContent()
89 | {
90 | $context = null;
91 | if ($this->basicAuth) {
92 | $context = stream_context_create([
93 | 'http' => [
94 | 'header' => "Authorization: Basic " . base64_encode($this->basicAuth)
95 | ]
96 | ]);
97 | }
98 |
99 | return @file_get_contents($this->mainPage. 'robots.txt', false, $context);
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/Checks/Custom/SSLCertificateIsValid.php:
--------------------------------------------------------------------------------
1 | domain = $domain;
23 | $this->days = $days;
24 | }
25 |
26 | /**
27 | * @return string
28 | */
29 | public function name()
30 | {
31 | return "Проверка валидности SSL сертификата...";
32 | }
33 |
34 | /**
35 | * @return boolean
36 | */
37 | public function run()
38 | {
39 | $certificate = $this->downloadCertificate($this->domain);
40 | if (!$certificate) {
41 | $this->logError('Не удалось получить сертификат для ' . $this->domain);
42 | return false;
43 | }
44 |
45 | if (empty($certificate['validTo_time_t'])) {
46 | $this->logError('Не удалось получить дату окончания валидности сертификата');
47 | return false;
48 | }
49 |
50 | if ($certificate['validTo_time_t'] < time() + $this->days * 24 * 60 * 60) {
51 | $error = sprintf(
52 | 'Сертификат для %s истечёт в течение следующих %s дней, а именно: %s',
53 | $this->domain,
54 | $this->days,
55 | date('d.m.Y', $certificate['validTo_time_t'])
56 | );
57 | $this->logError($error);
58 | return false;
59 | }
60 |
61 | return true;
62 | }
63 |
64 | /**
65 | * Download ssl certificate for domain.
66 | *
67 | * @param string $domain
68 | * @return array
69 | */
70 | protected function downloadCertificate($domain)
71 | {
72 | if (!function_exists('openssl_x509_parse')) {
73 | $this->skip('Функция openssl_x509_parse не объявлена');
74 | }
75 |
76 | $sslOptions = [
77 | 'capture_peer_cert' => true,
78 | 'SNI_enabled' => true,
79 | 'verify_peer' => true,
80 | 'verify_peer_name' => true,
81 | ];
82 | $streamContext = stream_context_create([
83 | 'ssl' => $sslOptions,
84 | ]);
85 | try {
86 | $client = stream_socket_client(
87 | "ssl://{$domain}:443",
88 | $errorNumber,
89 | $errorDescription,
90 | 10,
91 | STREAM_CLIENT_CONNECT,
92 | $streamContext
93 | );
94 |
95 | if ($client === false && $errorNumber == 0) {
96 | // Socket initialization problems
97 | throw new Exception('The resource (stream_socket_client) has not been created');
98 | }
99 | } catch (Exception $e) {
100 | $this->logError($e->getMessage());
101 | return [];
102 | }
103 |
104 | if ($errorNumber && $errorDescription) {
105 | $this->logError('stream_socket_client error ' . $errorNumber . ': ' . $errorDescription);
106 | }
107 |
108 | $response = stream_context_get_params($client);
109 | fclose($client);
110 |
111 | return !empty($response['options']['ssl']['peer_certificate'])
112 | ? openssl_x509_parse($response['options']['ssl']['peer_certificate']) : [];
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/Checks/Custom/WwwRedirect.php:
--------------------------------------------------------------------------------
1 | www редиректа вместо www -> no_www.
22 | *
23 | * @var bool
24 | */
25 | private $reverse;
26 |
27 | public function __construct($mainPage, $basicAuth = null, $reverse = false)
28 | {
29 | $this->mainPage = $mainPage;
30 | $this->basicAuth = $basicAuth;
31 | $this->reverse = $reverse;
32 | }
33 |
34 | /**
35 | * @return string
36 | */
37 | public function name()
38 | {
39 | return "Проверка редиректа с www на без www...";
40 | }
41 |
42 | /**
43 | * @return boolean
44 | */
45 | public function run()
46 | {
47 | $url = $this->reverse
48 | ? str_replace(['http://www.', 'https://www.'], ['http://', 'https://'], $this->mainPage)
49 | : str_replace(['http://', 'https://'], ['http://www.', 'https://www.'], $this->mainPage);
50 |
51 | $info = $this->getCurlInfo($url, $this->basicAuth);
52 | if (is_null($info)) {
53 | return false;
54 | }
55 |
56 | if ($info['http_code'] !== 301 && $info['http_code'] !== 302) {
57 | $this->logError('При curl запросе к '. $url . ' получен код ответа ' . $info['http_code'] . ' вместо 301/302');
58 | return false;
59 | }
60 |
61 | if (rtrim($info['redirect_url'], '/') !== rtrim($this->mainPage, '/')) {
62 | $this->logError('При curl запросе к '. $url . ' получен редирект на ' . $info['redirect_url'] . ' вместо ' . $this->mainPage);
63 | return false;
64 | }
65 |
66 | return true;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Checks/Custom/XDebugIsNotLoaded.php:
--------------------------------------------------------------------------------
1 | checkPhpExtensionsNotLoaded('xdebug');
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Checks/Greensight/FrontendBuildIsProduction.php:
--------------------------------------------------------------------------------
1 | manifestPath = $manifestPath;
17 | }
18 |
19 | /**
20 | * @return string
21 | */
22 | public function name()
23 | {
24 | return "Проверка, что frontend build собран в production режиме...";
25 | }
26 |
27 | public function run()
28 | {
29 | $manifestPath = $this->manifestPath;
30 | $manifestData = file_get_contents($manifestPath);
31 | if ($manifestData === false) {
32 | $this->logError('Не удалось прочитать файл ' . $manifestPath);
33 | return false;
34 | }
35 |
36 | $manifest = json_decode($manifestData, true);
37 | if ($manifest === null && json_last_error() !== JSON_ERROR_NONE) {
38 | $this->logError('Не удалось декодировать файл ' . $manifestPath . ': ' . json_last_error());
39 | return false;
40 | }
41 |
42 | if (empty($manifest['mode'])) {
43 | $this->logError('В манифесте ' . $manifestPath . ' отсутсвует ключ mode, похоже собрана dev сборка');
44 | return false;
45 | }
46 |
47 | if (!in_array($manifest['mode'], ['prod', 'production'])) {
48 | $this->logError('mode !== prod || production');
49 | return false;
50 | }
51 |
52 | return true;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Console/SystemCheckCommand.php:
--------------------------------------------------------------------------------
1 | setName('system:check')
60 | ->setDescription('Run a specific system monitoring')
61 | ->addArgument('monitoring', InputArgument::REQUIRED, 'Monitoring name');
62 | }
63 |
64 | /**
65 | * Executes the current command.
66 | *
67 | * @param InputInterface $input An InputInterface instance
68 | * @param OutputInterface $output An OutputInterface instance
69 | *
70 | * @return null|int null or 0 if everything went fine, or an error code.
71 | */
72 | protected function execute(InputInterface $input, OutputInterface $output)
73 | {
74 | $this->input = $input;
75 | $this->output = $output;
76 | $isVerbose = $this->output->isVerbose();
77 |
78 | try {
79 | if (!Loader::includeModule('arrilot.systemcheck')) {
80 | $this->output->writeln('Модуль arrilot.systemcheck не установлен');
81 | return 1;
82 | }
83 | } catch (LoaderException $e) {
84 | $this->output->writeln(''. $e->getMessage(). '');
85 | return 1;
86 | }
87 |
88 | $config = Configuration::getInstance()->get('bitrix-systemcheck');
89 | $monitoringCode = $input->getArgument('monitoring');
90 | $monitorings = [];
91 | foreach ((array) $config['monitorings'] as $monitoringClass) {
92 | /** @var Monitoring $monitoring */
93 | $monitoring = new $monitoringClass;
94 | $monitorings[$monitoring->code()] = $monitoring;
95 | }
96 | if (!isset($monitorings[$monitoringCode])) {
97 | $this->output->writeln('Мониторинг '.$monitoringCode.' не найден');
98 | return 1;
99 | }
100 | /** @var Monitoring $monitoring */
101 | $monitoring = $monitorings[$monitoringCode];
102 | $this->logger = $monitoring->logger();
103 | $title = !empty($config['env'])
104 | ? 'Запуск проверок мониторинга '.$monitoringCode.' для окружения '. $config['env'] . ''
105 | : 'Запуск проверок мониторинга '.$monitoringCode.'';
106 |
107 | $monitoring->getDataStorage()->cleanOutdatedData($monitoring->dataTtlDays);
108 | $this->runChecks($monitoring->checks(), $monitoring, $title, $isVerbose);
109 |
110 | if (count($this->skips) && $isVerbose) {
111 | $this->output->writeln('Журнал пропуска проверок:');
112 | $this->output->writeln('');
113 | foreach ($this->skips as $message) {
114 | $this->output->writeln(''.$message.'');
115 | }
116 | }
117 |
118 | if (count($this->errors)) {
119 | foreach ($this->errors as $message) {
120 | $this->output->writeln(''.$message.'');
121 | }
122 |
123 | $this->output->writeln('');
124 |
125 | $this->output->writeln('Некоторые проверки завершились ошибками');
126 |
127 | $this->raiseAlert();
128 | return 1;
129 | }
130 |
131 | $this->info('Все проверки успешно пройдены');
132 | return 0;
133 | }
134 |
135 | protected function runChecks(array $checks, Monitoring $monitoring, $title, $isVerbose)
136 | {
137 | $max = count($checks);
138 | if ($max === 0) {
139 | return;
140 | }
141 | $current = 1;
142 | $this->output->writeln('|-------------------------------------');
143 | $this->output->writeln('| '.$title);
144 | $this->output->writeln('|-------------------------------------');
145 | foreach ($checks as $check) {
146 | $check->setDataStorage($monitoring->getDataStorage());
147 | $message = sprintf(
148 | 'Проверка %s/%s:%s %s ',
149 | $current,
150 | $max,
151 | $isVerbose ? ' '. get_class($check) : '',
152 | $check->name()
153 | );
154 | $this->output->write($message);
155 | $this->runCheck($check);
156 | $current++;
157 | }
158 | $this->output->writeln('');
159 | }
160 |
161 | protected function runCheck(Check $check)
162 | {
163 | try {
164 | if ($check->run()) {
165 | $this->output->write('✔');
166 | } else {
167 | $this->output->write('✘');
168 | foreach ($check->getMessages() as $errorMessage) {
169 | $this->errors[] = $errorMessage;
170 | }
171 | }
172 | } catch (SkipCheckException $e) {
173 | $this->output->write('-');
174 | $this->skips[] = $e->getMessage();
175 | } catch (FailCheckException $e) {
176 | $this->output->write('✘');
177 | $this->errors[] = $e->getMessage();
178 | } catch (Exception $e) {
179 | $this->output->write('✘');
180 | $this->errors[] = $e->getMessage();
181 | }
182 |
183 | $this->output->write(PHP_EOL);
184 | }
185 |
186 | /**
187 | * Echo an error message.
188 | *
189 | * @param string$message
190 | */
191 | protected function error($message)
192 | {
193 | $this->output->writeln("{$message}");
194 | }
195 |
196 | /**
197 | * Echo an info.
198 | *
199 | * @param string $message
200 | */
201 | protected function info($message)
202 | {
203 | $this->output->writeln("{$message}");
204 | if ($this->logger) {
205 | $this->logger->info($message);
206 | }
207 | }
208 |
209 | protected function raiseAlert()
210 | {
211 | if ($this->logger) {
212 | $message = implode(PHP_EOL, $this->errors);
213 | $this->logger->alert($message);
214 | }
215 | }
216 | }
--------------------------------------------------------------------------------
/src/EventHandlers.php:
--------------------------------------------------------------------------------
1 | GetMessage("MAIN_MENU_SYSTEM_CHECKER"),
11 | "url" => "site_checker.php?lang=".LANGUAGE_ID,
12 | "more_url" => array(),
13 | "title" => GetMessage("MAIN_MENU_SITE_CHECKER_ALT"),
14 | );
15 | }
16 | }
--------------------------------------------------------------------------------
/src/Exceptions/FailCheckException.php:
--------------------------------------------------------------------------------
1 | connection = Application::getConnection();
31 | $this->helper = $this->connection->getSqlHelper();
32 | $this->monitoringName = $monitoringName;
33 | }
34 |
35 | /**
36 | * @param $check
37 | * @return false|array
38 | */
39 | public function getData($check)
40 | {
41 | $sql = sprintf(
42 | "SELECT * FROM arrilot_systemcheck_checks_data WHERE `MONITORING`='%s' AND `CHECK`='%s' ORDER BY `CREATED_AT` DESC LIMIT 1",
43 | $this->helper->forSql($this->monitoringName),
44 | $this->helper->forSql($check)
45 | );
46 |
47 | return $this->connection->query($sql)->fetch();
48 | }
49 |
50 | public function saveData($check, $data)
51 | {
52 | $sql = sprintf("INSERT INTO arrilot_systemcheck_checks_data (`MONITORING`, `CHECK`, `DATA`, `CREATED_AT`) VALUES ('%s', '%s', '%s', NOW())",
53 | $this->helper->forSql($this->monitoringName),
54 | $this->helper->forSql($check),
55 | $this->helper->forSql(json_encode($data))
56 | );
57 |
58 | $this->connection->query($sql);
59 |
60 | return $this;
61 | }
62 |
63 | /**
64 | * Delete all outdated data for this monitoring.
65 | *
66 | * @param int $days
67 | * @return DataStorage
68 | */
69 | public function cleanOutdatedData($days)
70 | {
71 | $sql = sprintf(
72 | "DELETE FROM arrilot_systemcheck_checks_data WHERE `MONITORING`='%s' AND `CREATED_AT` < NOW() - INTERVAL %s DAY",
73 | $this->helper->forSql($this->monitoringName),
74 | (int) $days
75 | );
76 |
77 | $this->connection->query($sql);
78 |
79 | return $this;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/Monitorings/Monitoring.php:
--------------------------------------------------------------------------------
1 | dataStorage = new DataStorage(get_class());
19 | }
20 |
21 | /**
22 | * Russian monitoring name
23 | *
24 | * @return string
25 | */
26 | abstract public function name();
27 |
28 | /**
29 | * Monitoring code (id)
30 | *
31 | * @return string
32 | */
33 | abstract public function code();
34 |
35 | /**
36 | * Array of checks.
37 | *
38 | * @return array
39 | */
40 | abstract public function checks();
41 |
42 | /**
43 | * @return LoggerInterface|null
44 | */
45 | abstract public function logger();
46 |
47 | /**
48 | * @return DataStorage
49 | */
50 | public function getDataStorage()
51 | {
52 | return $this->dataStorage;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------