Last active
June 26, 2022 11:41
-
-
Save aseemann/42041fccb784cf472349a7af9748fe9b to your computer and use it in GitHub Desktop.
TYPO3 Health-check Script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
/** | |
* This file/script is intended to be used as health-check endpoint for TYPO3. This script checks if all databases | |
* can be reached and all caches could be written. By the way the check is done, the script also checks implicit | |
* some configurations. | |
* | |
* This script is tested and developed with TYPO3 10.4.x in composer mode. | |
* | |
* How to use: | |
* | |
* Simply put this script in the public directory of your TYPO3 installation as `healthcheck.php`. Now you can use this | |
* script to check if your TYPO3 instance fulfill the minimum run requirements. | |
*/ | |
namespace ASeemann\HealthCheck; | |
use TYPO3\CMS\Core\Cache\CacheManager; | |
use TYPO3\CMS\Core\Core\SystemEnvironmentBuilder; | |
use TYPO3\CMS\Core\Database\ConnectionPool; | |
use TYPO3\CMS\Core\Utility\ArrayUtility; | |
use TYPO3\CMS\Core\Utility\GeneralUtility; | |
use TYPO3\CMS\Core\Utility\HttpUtility; | |
/** | |
* Class HealthCheck | |
* | |
* @package ASeemann\HealthCheck | |
* @author Axel Seemann <kummeraxel@gmail.com> | |
* @licence AGPL-v3 | |
* @link https://gist.github.com/aseemann/42041fccb784cf472349a7af9748fe9b | |
*/ | |
class HealthCheck | |
{ | |
private const PATH_DEFAULT_CONF = __DIR__ . '/typo3/sysext/core/Configuration/DefaultConfiguration.php'; | |
private const PATH_LOCALCONF = __DIR__ . '/typo3conf/LocalConfiguration.php'; | |
private const PATH_ADDITIONALCONF = __DIR__ . '/typo3conf/AdditionalConfiguration.php'; | |
private const PATH_AUTOLOAD = __DIR__ . '/../vendor/autoload.php'; | |
/** | |
* @var array TYPO3 Global Configuration Array | |
*/ | |
private array $configurationArray = []; | |
/** | |
* @var array<string> Error messages | |
*/ | |
private array $errors = []; | |
/** | |
* @var float | |
*/ | |
private float $startTime; | |
/** | |
* @var array<string> Success messages | |
*/ | |
private array $checks = []; | |
public function __construct() | |
{ | |
$this->startTime = microtime(true); | |
require_once self::PATH_AUTOLOAD; | |
global $GLOBALS; | |
$GLOBALS['TYPO3_CONF_VARS'] = require self::PATH_DEFAULT_CONF; | |
ArrayUtility::mergeRecursiveWithOverrule($GLOBALS['TYPO3_CONF_VARS'], require self::PATH_LOCALCONF); | |
if (file_exists(self::PATH_ADDITIONALCONF)) { | |
require_once self::PATH_ADDITIONALCONF; | |
} | |
SystemEnvironmentBuilder::run(); | |
$this->configurationArray = $GLOBALS['TYPO3_CONF_VARS']; | |
} | |
public function run(): void | |
{ | |
$this->checkDatabaseConnections(); | |
$this->checkCaches(); | |
$this->setResponseHeader(); | |
if ($this->hasErrors()) { | |
echo "Some errors occurred instance might not be available" . PHP_EOL; | |
} else { | |
echo "All fine" . PHP_EOL; | |
} | |
$this->printHeadline('Checks'); | |
foreach ($this->checks as $check) { | |
echo "OK: $check" . PHP_EOL; | |
} | |
if ($this->hasErrors()) { | |
$this->printHeadline('Errors'); | |
foreach ($this->errors as $error) { | |
echo "Error: $error" . PHP_EOL; | |
} | |
} | |
$this->printHeadline('Time'); | |
echo round(microtime(true) - $this->startTime, 3 ) . " Seconds"; | |
} | |
private function setResponseHeader(): void | |
{ | |
header('Content-Type: text/plain; charset=utf-8'); | |
header('Pragma: no-cache'); | |
header('Cache-Control: private, max-age=0, no-store'); | |
if ($this->hasErrors()) { | |
header(HttpUtility::HTTP_STATUS_503); | |
} | |
} | |
private function checkDatabaseConnections(): void | |
{ | |
$pool = GeneralUtility::makeInstance(ConnectionPool::class); | |
foreach ($pool->getConnectionNames() as $name) { | |
try { | |
$connection = $pool->getConnectionByName($name); | |
$result = $connection->executeQuery('SHOW TABLES'); | |
if (false == $connection->isConnected()) { | |
$this->errors[] = "Database connection $name could not be established"; | |
} else { | |
$this->checks[] = "Database connection $name established"; | |
} | |
if ($result->rowCount() == 0) { | |
$this->errors[] = "No tables in database for connection $name"; | |
} | |
} catch (\Throwable $throwable) { | |
$this->errors[] = $throwable->getMessage(); | |
} | |
} | |
} | |
private function checkCaches(): void | |
{ | |
try { | |
$caches = $this->configurationArray['SYS']['caching']['cacheConfigurations']; | |
$cacheManager = GeneralUtility::makeInstance(CacheManager::class); | |
$cacheManager->setCacheConfigurations($caches); | |
$cacheKey = md5(microtime() . '.health-check'); | |
foreach ($caches as $identifier => $configuration) { | |
$cache = $cacheManager->getCache($identifier); | |
$cache->set($cacheKey, "test"); | |
if ($cache->has($cacheKey)) { | |
$this->checks[] = "Cache $identifier"; | |
$cache->remove($cacheKey); | |
} else { | |
$this->errors[] = "Cache $identifier"; | |
} | |
} | |
} catch (\Throwable $throwable) { | |
$this->errors[] = $throwable->getMessage(); | |
} | |
} | |
/** | |
* @return bool | |
*/ | |
private function hasErrors(): bool | |
{ | |
return count($this->errors) > 0; | |
} | |
/** | |
* @param string $headline Headline to print | |
* | |
* @return void | |
*/ | |
private function printHeadline(string $headline): void | |
{ | |
echo PHP_EOL . str_pad("$headline ", 60, "=", STR_PAD_RIGHT) . PHP_EOL . PHP_EOL; | |
} | |
} | |
$healthCheck = new HealthCheck(); | |
$healthCheck->run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment