Skip to content

Instantly share code, notes, and snippets.

@aseemann
Last active June 26, 2022 11:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aseemann/42041fccb784cf472349a7af9748fe9b to your computer and use it in GitHub Desktop.
Save aseemann/42041fccb784cf472349a7af9748fe9b to your computer and use it in GitHub Desktop.
TYPO3 Health-check Script
<?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