From 34eea7a235e00335d6f325b496ba5231b810b582 Mon Sep 17 00:00:00 2001
From: "Hahn Axel (hahn)" <axel.hahn@unibe.ch>
Date: Thu, 25 Jul 2024 17:14:47 +0200
Subject: [PATCH] update appmonitor client v1.137

---
 .../classes/appmonitor-checks.class.php       | 299 +++++++-------
 .../classes/appmonitor-client.class.php       | 376 +++++++++---------
 .../appmonitor/classes/client_all_in_one.php  |  33 +-
 .../appmonitor/git_update_appmonitor.sh       |  29 +-
 public_html/appmonitor/local.php              |   6 +
 .../plugins/apps/iml-appmonitor-server.php    | 135 +++----
 .../plugins/checks/apacheprocesses.php        | 161 +++++---
 .../appmonitor/plugins/checks/cert.php        |  86 ++--
 .../appmonitor/plugins/checks/diskfree.php    |  60 +--
 .../appmonitor/plugins/checks/exec.php        |  92 +++--
 .../appmonitor/plugins/checks/file.php        |  46 ++-
 .../appmonitor/plugins/checks/hello.php       |  17 +-
 .../appmonitor/plugins/checks/httpcontent.php | 188 ++++-----
 .../appmonitor/plugins/checks/loadmeter.php   |  76 ++--
 .../plugins/checks/mysqlconnect.php           |  38 +-
 .../appmonitor/plugins/checks/pdoconnect.php  |  38 +-
 .../appmonitor/plugins/checks/phpmodules.php  |  67 ++--
 .../appmonitor/plugins/checks/ping.php        |  35 +-
 .../appmonitor/plugins/checks/porttcp.php     |  31 +-
 .../appmonitor/plugins/checks/simple.php      |  21 +-
 .../plugins/checks/sqliteconnect.php          |  51 ++-
 21 files changed, 1000 insertions(+), 885 deletions(-)
 create mode 100644 public_html/appmonitor/local.php

diff --git a/public_html/appmonitor/classes/appmonitor-checks.class.php b/public_html/appmonitor/classes/appmonitor-checks.class.php
index dfff0ea6..865a0e8e 100755
--- a/public_html/appmonitor/classes/appmonitor-checks.class.php
+++ b/public_html/appmonitor/classes/appmonitor-checks.class.php
@@ -1,6 +1,6 @@
 <?php
 
-if(!defined('RESULT_OK')){
+if (!defined('RESULT_OK')) {
     define("RESULT_OK", 0);
     define("RESULT_UNKNOWN", 1);
     define("RESULT_WARNING", 2);
@@ -51,76 +51,79 @@ if(!defined('RESULT_OK')){
  * 2021-10-28  0.93  axel.hahn@iml.unibe.ch  add plugins<br>
  * 2021-12-14  0.93  axel.hahn@iml.unibe.ch  split plugins into single files; added key group in a check<br>
  * 2023-06-02  0.125 axel.hahn@unibe.ch      replace array_key_exists for better readability
+ * 2024-07-22  0.137 axel.hahn@unibe.ch      php 8 only: use typed variables
  * --------------------------------------------------------------------------------<br>
- * @version 0.125
+ * @version 0.137
  * @author Axel Hahn
  * @link TODO
  * @license GPL
  * @license http://www.gnu.org/licenses/gpl-3.0.html GPL 3.0
  * @package IML-Appmonitor
  */
-class appmonitorcheck {
+class appmonitorcheck
+{
     // ----------------------------------------------------------------------
     // CONFIG
     // ----------------------------------------------------------------------
-    
+
     /**
      * starting time using microtime
-     * @var float|string
+     * @var float
      */
-    protected $_iStart=0;
+    protected float $_iStart = 0;
 
     /**
      * config container
      * @var array
      */
-    protected $_aConfig = [];
+    protected array $_aConfig = [];
 
     /**
      * data of all checks
      * @var array
      */
-    protected $_aData = [];
-    
+    protected array $_aData = [];
 
     /**
      * flat array with units for sizes
      * @var array
      */
-    protected $_units = [ 'B', 'KB', 'MB', 'GB', 'TB' ];
-    
+    protected array $_units = ['B', 'KB', 'MB', 'GB', 'TB'];
+
     /**
      * timeout in sec for tcp socket connections
-     * @var type 
+     * @var integer
      */
-    protected $_iTimeoutTcp=5;
-    
+    protected int $_iTimeoutTcp = 5;
+
     /**
      * point to the plugin directory
      * @var string
      */
-    protected $_sPluginDir=__DIR__.'/../plugins';
+    protected string $_sPluginDir = __DIR__ . '/../plugins';
 
     // ----------------------------------------------------------------------
     // CONSTRUCTOR
     // ----------------------------------------------------------------------
 
     /**
-     * constructor (nothing)
+     * Constructor (nothing here)
      */
-    public function __construct() {
-        
+    public function __construct()
+    {
+
     }
-    
+
     // ----------------------------------------------------------------------
     // PRIVATE FUNCTIONS
     // ----------------------------------------------------------------------
 
     /**
-     * internal: create basic array values for metadata
+     * Internal: create basic array values for metadata
      * @return boolean
      */
-    protected function _createDefaultMetadata() {
+    protected function _createDefaultMetadata(): bool
+    {
 
         $this->_aData = [
             "name" => $this->_aConfig["name"],
@@ -136,33 +139,38 @@ class appmonitorcheck {
     }
 
     /**
-     * set a result value of a check
-     * @param type $iResult
-     * @return type
+     * Set the result value of a check
+     * @param integer  $iResult  result code; one of RESULT_OK|RESULT_WARNING|RESULT_ERROR|RESULT_UNKNOWN
+     * @return bool
      */
-    protected function _setResult($iResult) {
-        return $this->_aData["result"] = (int) $iResult;
+    protected function _setResult(int $iResult): bool
+    {
+        $this->_aData["result"] = (int) $iResult;
+        return true;
     }
 
     /**
-     * set a result value of a check
-     * @param type $iResult
-     * @return type
+     * Set a result value of a check
+     * @param string  $s  value; message text for this result
+     * @return bool
      */
-    protected function _setOutput($s) {
-        return $this->_aData["value"] = (string) $s;
+    protected function _setOutput(string $s): bool
+    {
+        $this->_aData["value"] = $s;
+        return true;
     }
-    
+
     /**
-     * put counter data to result set
-     * @param type $aParams
+     * Put counter data to result set
+     * @param array  $aParams  array  with possible keys type, count, visual
      * @return boolean
      */
-    protected function _setCounter($aParams){
-        if(is_array($aParams) && count($aParams)){
-            foreach([ 'type', 'count', 'visual' ] as $sMyKey){
-                if(isset($aParams[$sMyKey])){
-                    $this->_aData[$sMyKey]=$aParams[$sMyKey];
+    protected function _setCounter(array $aParams): bool
+    {
+        if (is_array($aParams) && count($aParams)) {
+            foreach (['type', 'count', 'visual'] as $sMyKey) {
+                if (isset($aParams[$sMyKey])) {
+                    $this->_aData[$sMyKey] = $aParams[$sMyKey];
                 }
             }
         }
@@ -170,41 +178,43 @@ class appmonitorcheck {
     }
 
     /**
-     * set result and output 
-     * @param integer  $iResult   value; use a RESULT_XYZ constant
+     * Set result and output 
+     * @param integer  $iResult   result code; one of RESULT_OK|RESULT_WARNING|RESULT_ERROR|RESULT_UNKNOWN
      * @param string   $s         message text
      * @param array    $aCounter  optional: counter with array keys type, count, visual
      * @return boolean
      */
-    protected function _setReturn($iResult, $s, $aCounter=[]) {
+    protected function _setReturn(int $iResult, string $s, array $aCounter = [])
+    {
         $this->_setResult($iResult);
         $this->_setOutput($s);
         $this->_setCounter($aCounter);
         return true;
     }
-    
+
     /**
-     * check a given array if it contains wanted keys
+     * Check a given array if it contains wanted keys
      * @param array  $aConfig   array to verify
      * @param string $sKeyList  key or keys as comma seprated list 
      * @return boolean
      */
-    protected function _checkArrayKeys($aConfig, $sKeyList) {
+    protected function _checkArrayKeys($aConfig, $sKeyList)
+    {
         foreach (explode(",", $sKeyList) as $sKey) {
             if (!isset($aConfig[$sKey])) {
                 header('HTTP/1.0 503 Service Unavailable');
                 die('<h1>503 Service Unavailable</h1>'
-                        . '<h2>Details</h2>'
-                        .__METHOD__ . " - array of check parameters requires the keys [$sKeyList] - but key <code>$sKey</code> was not found in config array."
-                        . "<pre>" . print_r($aConfig, true) .'</pre>'
+                    . '<h2>Details</h2>'
+                    . __METHOD__ . " - array of check parameters requires the keys [$sKeyList] - but key <code>$sKey</code> was not found in config array."
+                    . "<pre>" . print_r($aConfig, true) . '</pre>'
                 );
             }
             if (is_null($aConfig[$sKey])) {
                 header('HTTP/1.0 503 Service Unavailable');
                 die('<h1>503 Service Unavailable</h1>'
-                        . '<h2>Details</h2>'
-                        .__METHOD__ . " - key <code>$sKey</code> is empty in config array"
-                        . "<pre>" . print_r($aConfig, true) .'</pre>'
+                    . '<h2>Details</h2>'
+                    . __METHOD__ . " - key <code>$sKey</code> is empty in config array"
+                    . "<pre>" . print_r($aConfig, true) . '</pre>'
                 );
             }
         }
@@ -216,9 +226,9 @@ class appmonitorcheck {
     // ----------------------------------------------------------------------
 
     /**
-     * perform a check
-     * @param type $aConfig
-     * Array
+     * Perform a check
+     * @param array $aConfig  configuration array for a check, eg.
+     * <code>
      * [
      *     [name] => Dummy
      *     [description] => Dummy Test
@@ -228,10 +238,11 @@ class appmonitorcheck {
      *                                        // its keys depend on the function  
      *     ]
      * ]
-     * 
+     * </code>
      * @return array
      */
-    public function makeCheck($aConfig) {
+    public function makeCheck(array $aConfig): array
+    {
         $this->_iStart = microtime(true);
         $this->_checkArrayKeys($aConfig, "name,description,check");
         $this->_checkArrayKeys($aConfig["check"], "function");
@@ -241,53 +252,53 @@ class appmonitorcheck {
 
         $sCheck = preg_replace('/[^a-zA-Z0-9]/', '', $this->_aConfig["check"]["function"]);
         $aParams = $this->_aConfig["check"]["params"] ?? [];
-        
+
         // try to load as plugin from a plugin file
-        $sPluginFile= strtolower($this->_sPluginDir.'/checks/'.$sCheck.'.php');
+        $sPluginFile = strtolower($this->_sPluginDir . '/checks/' . $sCheck . '.php');
         // echo "plugin file: $sPluginFile<br>\n";
-        $sCheckClass = 'check'.$sCheck;
-        if (!class_exists($sCheckClass)){
-            if (file_exists($sPluginFile)) {   
-                require_once($sPluginFile);
+        $sCheckClass = 'check' . $sCheck;
+        if (!class_exists($sCheckClass)) {
+            if (file_exists($sPluginFile)) {
+                require_once ($sPluginFile);
             }
         }
 
-        if (!class_exists($sCheckClass)){
+        if (!class_exists($sCheckClass)) {
             header('HTTP/1.0 503 Service Unavailable');
             die('<h1>503 Service Unavailable</h1>'
-                    . '<h2>Details</h2>'
-                    .__METHOD__ . " - check class not found: <code>$sCheckClass</code>"
-                    . "<pre>" . print_r($aConfig, true) .'</pre>'
-                    ."<h2>Known checks</h2>\n".print_r($this->listChecks(), 1)
+                . '<h2>Details</h2>'
+                . __METHOD__ . " - check class not found: <code>$sCheckClass</code>"
+                . "<pre>" . print_r($aConfig, true) . '</pre>'
+                . "<h2>Known checks</h2>\n" . print_r($this->listChecks(), 1)
             );
         }
-            
+
         $oPlugin = new $sCheckClass;
-        $aResponse=$oPlugin->run($aParams); 
-        if(!is_array($aResponse)){
+        $aResponse = $oPlugin->run($aParams);
+        if (!is_array($aResponse)) {
             header('HTTP/1.0 503 Service Unavailable');
             die('<h1>503 Service Unavailable</h1>'
-                    . '<h2>Details</h2>'
-                    .__METHOD__ . " - plugin : $sCheck does not responses an array"
-                    . "<pre>INPUT " . print_r($aConfig, true) .'</pre>'
-                    . "<pre>RESPONSE " . print_r($aResponse, true) .'</pre>'
+                . '<h2>Details</h2>'
+                . __METHOD__ . " - plugin : $sCheck does not responses an array"
+                . "<pre>INPUT " . print_r($aConfig, true) . '</pre>'
+                . "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>'
             );
         }
-        if(count($aResponse)<2){
+        if (count($aResponse) < 2) {
             header('HTTP/1.0 503 Service Unavailable');
             die('<h1>503 Service Unavailable</h1>'
-                    . '<h2>Details</h2>'
-                    .__METHOD__ . " - plugin : $sCheck does not responses the minimum of 2 array values"
-                    . "<pre>INPUT " . print_r($aConfig, true) .'</pre>'
-                    . "<pre>RESPONSE " . print_r($aResponse, true) .'</pre>'
+                . '<h2>Details</h2>'
+                . __METHOD__ . " - plugin : $sCheck does not responses the minimum of 2 array values"
+                . "<pre>INPUT " . print_r($aConfig, true) . '</pre>'
+                . "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>'
             );
         }
-        if(!isset($aResponse[2])){
-            $aResponse[2]=[];
+        if (!isset($aResponse[2]) || !$aResponse[2]) {
+            $aResponse[2] = [];
         }
         $this->_setReturn($aResponse[0], $aResponse[1], $aResponse[2]);
-        if (!$this->_aData['group'] && method_exists($oPlugin, "getGroup")){
-            $this->_aData['group']=$oPlugin->getGroup($aParams);
+        if (!$this->_aData['group'] && method_exists($oPlugin, "getGroup")) {
+            $this->_aData['group'] = $oPlugin->getGroup($aParams);
         }
 
         $this->_aData['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms';
@@ -296,22 +307,23 @@ class appmonitorcheck {
     }
 
     /**
-     * list all available check functions. This is a helper class you can call
+     * List all available checks. This is a helper class you can call
      * to get an overview over built in functions and plugins. 
      * You get a flat array with all function names.
      * @return array
      */
-    public function listChecks() {
+    public function listChecks(): array
+    {
         $aReturn = [];
         // return internal protected fuctions named "check[whatever]"
         $class = new ReflectionClass($this);
         foreach ($class->getMethods(ReflectionMethod::IS_PROTECTED) as $oReflectionMethod) {
             if (strpos($oReflectionMethod->name, "check") === 0) {
-                $aReturn[(string) $oReflectionMethod->name]=1;
+                $aReturn[(string) $oReflectionMethod->name] = 1;
             }
         }
         // return checks from plugins subdir
-        foreach(glob($this->_sPluginDir.'/checks/*.php') as $sPluginFile){
+        foreach (glob($this->_sPluginDir . '/checks/*.php') as $sPluginFile) {
             $aReturn[str_replace('.php', '', basename($sPluginFile))] = 1;
         }
         ksort($aReturn);
@@ -319,10 +331,11 @@ class appmonitorcheck {
     }
 
     /**
-     * final call of class: send response (data array)
-     * @return type
+     * Final call of class: send response (data array)
+     * @return array
      */
-    public function respond() {
+    public function respond()
+    {
         return $this->_aData;
     }
 
@@ -331,65 +344,70 @@ class appmonitorcheck {
     // ----------------------------------------------------------------------
 
     /**
-     * helper function: read certificate data
+     * Helper function: read certificate data
      * called in checkCert()
+     * 
      * @param string  $sUrl         url to connect
      * @param boolean $bVerifyCert  flag: verify certificate; default: no check
      * @return array
      */
-    protected function _certGetInfos($sUrl, $bVerifyCert) {
-        $iTimeout=10;
-        $aUrldata=parse_url($sUrl);
+    protected function _certGetInfos(string $sUrl, bool $bVerifyCert): array
+    {
+        $iTimeout = 10;
+        $aUrldata = parse_url($sUrl);
         $sHost = isset($aUrldata['host']) ? $aUrldata['host'] : false;
         $iPort = isset($aUrldata['port']) ? $aUrldata['port'] : ((isset($aUrldata['scheme']) && $aUrldata['scheme'] === 'https') ? 443 : false);
 
-        $aSsl=['capture_peer_cert' => true ];
-        if($bVerifyCert){
-            $aSsl['verify_peer']=false;
-            $aSsl['verify_peer_name']=false;
-        };
-        $get = stream_context_create([ 'ssl' => $aSsl ]);
-        if(!$get){
-            return [ '_error' => 'Error: Cannot create stream_context' ];
+        $aSsl = ['capture_peer_cert' => true];
+        if ($bVerifyCert) {
+            $aSsl['verify_peer'] = false;
+            $aSsl['verify_peer_name'] = false;
         }
-        $errno=-1;
-        $errstr="stream_socket_client failed.";
+        ;
+        $get = stream_context_create(['ssl' => $aSsl]);
+        if (!$get) {
+            return ['_error' => 'Error: Cannot create stream_context'];
+        }
+        $errno = -1;
+        $errstr = "stream_socket_client failed.";
         $read = stream_socket_client("ssl://$sHost:$iPort", $errno, $errstr, $iTimeout, STREAM_CLIENT_CONNECT, $get);
-        if(!$read){
-            return [ '_error' => "Error $errno: $errstr; cannot create stream_socket_client with given stream_context to ssl://$sHost:$iPort; you can try to set the flag [verify] to false to check expiration date only." ];
+        if (!$read) {
+            return ['_error' => "Error $errno: $errstr; cannot create stream_socket_client with given stream_context to ssl://$sHost:$iPort; you can try to set the flag [verify] to false to check expiration date only."];
         }
         $cert = stream_context_get_params($read);
-        if(!$cert){
-            return [ '_error' => "Error: socket was connected to ssl://$sHost:$iPort - but I cannot read certificate infos with stream_context_get_params " ];
+        if (!$cert) {
+            return ['_error' => "Error: socket was connected to ssl://$sHost:$iPort - but I cannot read certificate infos with stream_context_get_params "];
         }
-        $certinfo = openssl_x509_parse($cert['options']['ssl']['peer_certificate']);
-        return $certinfo;
+        return openssl_x509_parse($cert['options']['ssl']['peer_certificate']);
     }
 
 
     /**
-     * get human readable space value
+     * Get human readable space value
      * @param integer $size
      * @return string
      */
-    protected function _getHrSize($size){
+    protected function _getHrSize(int $size): string
+    {
         $power = $size > 0 ? floor(log($size, 1024)) : 0;
         return number_format($size / pow(1024, $power), 2, '.', ',') . ' ' . $this->_units[$power];
     }
+
     /**
      * get a space in a real value if an integer has added MB|GB|...
      * @param string $sValue
      * @return integer
      */
-    protected function _getSize($sValue){
-        if(is_int($sValue)){
+    protected function _getSize(string $sValue): int
+    {
+        if (is_int($sValue)) {
             return $sValue;
         }
-        $power=0;
-        foreach($this->_units as $sUnit){
-            if (preg_match('/^[0-9\.\ ]*'.$sUnit.'/', $sValue)){
-                $i=preg_replace('/([0-9\.]*).*/', '$1', $sValue);
-                $iReal=$i*pow(1024, $power);
+        $power = 0;
+        foreach ($this->_units as $sUnit) {
+            if (preg_match('/^[0-9\.\ ]*' . $sUnit . '/', $sValue)) {
+                $i = preg_replace('/([0-9\.]*).*/', '$1', $sValue);
+                $iReal = $i * pow(1024, $power);
                 // die("FOUND: $sValue with unit ${sUnit} - 1024^$power * $i = $iReal");
                 return $iReal;
             }
@@ -397,48 +415,9 @@ class appmonitorcheck {
         }
         header('HTTP/1.0 503 Service Unavailable');
         die('<h1>503 Service Unavailable</h1>'
-                . '<h2>Details</h2>'
-                .__METHOD__ . " ERROR in space value parameter - there is no size unit in [$sValue] - allowed size units are " . implode('|', $this->_units)
+            . '<h2>Details</h2>'
+            . __METHOD__ . " ERROR in space value parameter - there is no size unit in [$sValue] - allowed size units are " . implode('|', $this->_units)
         );
     }
 
-
-    
-    /**
-     * compare function for 2 values
-     * @param string   $verifyValue  search value
-     * @param string   $sCompare     compare function; it is one of
-     *                               IS - 
-     *                               GE - greater or equal
-     *                               GT - greater
-     * @param string   $value        existing value
-     * @return boolean
-     */
-    protected function _compare($value, $sCompare, $verifyValue){
-        switch ($sCompare){
-            case "IS":
-                return $value===$verifyValue;
-                break;
-            case "GE":
-                return $value>=$verifyValue;
-                break;
-            case "GT":
-                return $value>$verifyValue;
-                break;
-            case "HAS":
-                return !!(strstr($value, $verifyValue)!==false);
-                break;
-            default:
-                header('HTTP/1.0 503 Service Unavailable');
-                die('<h1>503 Service Unavailable</h1>'
-                        . '<h2>Details</h2>'
-                        .__METHOD__ . " - FATAL ERROR: a compare function [$sCompare] is not implemented (yet)."
-                );
-                break;
-        }
-        return false;
-    }
-
-
-
 }
diff --git a/public_html/appmonitor/classes/appmonitor-client.class.php b/public_html/appmonitor/classes/appmonitor-client.class.php
index 08a58424..bdf70db9 100755
--- a/public_html/appmonitor/classes/appmonitor-client.class.php
+++ b/public_html/appmonitor/classes/appmonitor-client.class.php
@@ -1,5 +1,5 @@
 <?php
-if (!class_exists('appmonitorcheck')){
+if (!class_exists('appmonitorcheck')) {
     require_once 'appmonitor-checks.class.php';
 }
 
@@ -41,60 +41,62 @@ if (!class_exists('appmonitorcheck')){
  * 2019-05-31  0.87   axel.hahn@iml.unibe.ch  add timeout as param in connective checks (http, tcp, databases)<br>
  * 2020-05-03  0.110  axel.hahn@iml.unibe.ch  update renderHtmloutput<br>
  * 2023-07-06  0.128  axel.hahn@unibe.ch      update httpcontent check<br>
+ * 2024-07-19  0.137  axel.hahn@unibe.ch      php 8 only: use typed variables
  * --------------------------------------------------------------------------------<br>
- * @version 0.128
+ * @version 0.137
  * @author Axel Hahn
  * @link TODO
  * @license GPL
  * @license http://www.gnu.org/licenses/gpl-3.0.html GPL 3.0
  * @package IML-Appmonitor
  */
-class appmonitor {
+class appmonitor
+{
 
     /**
-     * config: default ttl for server before requesting the client check again
-     * value is in seconds
-     * @var int
+     * Name and Version number
+     * @var string
      */
-    protected $_sVersion = 'php-client-v0.128';
+    protected string $_sVersion = 'php-client-v0.137';
 
     /**
      * config: default ttl for server before requesting the client check again
      * value is in seconds
      * @var int
      */
-    protected $_iDefaultTtl = 300;
+    protected int $_iDefaultTtl = 300;
 
     /**
      * internal counter: greatest return value of all checks
-     * @var type 
+     * @var integer 
      */
-    protected $_iMaxResult = false;
+    protected int $_iMaxResult = -1;
 
     /**
      * responded metadata of a website
      * @see _createDefaultMetadata()
      * @var array
      */
-    protected $_aMeta = [];
+    protected array $_aMeta = [];
 
     /**
-     * repended array of all checks
+     * Response array of all checks
      * @see addCheck()
      * @var array
      */
-    protected $_aChecks = [];
-    
+    protected array $_aChecks = [];
+
     /**
      * for time measurements: start time
      * @var float 
      */
-    protected $_iStart = false;
-    
+    protected float $_iStart = 0;
+
     /**
      * constructor: init data
      */
-    public function __construct() {
+    public function __construct()
+    {
         $this->_createDefaultMetadata();
     }
 
@@ -103,10 +105,11 @@ class appmonitor {
     // ----------------------------------------------------------------------
 
     /**
-     * create basic array values for metadata
+     * Create basic array values for metadata
      * @return boolean
      */
-    protected function _createDefaultMetadata() {
+    protected function _createDefaultMetadata(): bool
+    {
         $this->_iStart = microtime(true);
         $this->_aMeta = [
             "host" => false,
@@ -129,21 +132,26 @@ class appmonitor {
     // ----------------------------------------------------------------------
 
     /**
-     * set the physical hostname for metadata; if no host is given then
+     * Set the physical hostname for metadata; if no host is given then
      * the php_uname("n") will be used to set one.
      * 
      * @param string $s  hostname
      * @return bool
      */
-    public function setHost($s = false) {
+    public function setHost(string $s = ''): bool
+    {
         if (!$s) {
             $s = php_uname("n");
         }
-        return $this->_aMeta["host"] = $s;
+        if (!$s) {
+            return false;
+        }
+        $this->_aMeta["host"] = $s;
+        return true;
     }
 
     /**
-     * set a name for this website or application and its environment 
+     * Set a name for this website or application and its environment 
      * (dev, test, prod); 
      * 
      * If you have several application in subdirectories, i.e. /blog,  /shop...
@@ -151,14 +159,19 @@ class appmonitor {
      * 
      * if no argument is given the name of HTTP_HOST will be used
      * 
-     * @param string $sNewHost  hostname
-     * @return bool
+     * @param string $sWebsite  Name of the website or web application
+     * @return boolean
      */
-    public function setWebsite($s = false) {
-        if (!$s && isset($_SERVER["HTTP_HOST"])) {
-            $s = $_SERVER["HTTP_HOST"];
+    public function setWebsite($sWebsite = ''): bool
+    {
+        if (!$sWebsite && isset($_SERVER["HTTP_HOST"])) {
+            $sWebsite = $_SERVER["HTTP_HOST"];
+        }
+        if (!$sWebsite) {
+            return false;
         }
-        return $this->_aMeta["website"] = $s;
+        $this->_aMeta["website"] = $sWebsite;
+        return true;
     }
 
     /**
@@ -168,58 +181,67 @@ class appmonitor {
      * @param int $iTTl TTL value in sec
      * @return boolean
      */
-    public function setTTL($iTTl = false) {
-        if (!$iTTl) {
+    public function setTTL($iTTl = 0)
+    {
+        if ($iTTl == 0) {
             $iTTl = $this->_iDefaultTtl;
         }
         return $this->_aMeta["ttl"] = $iTTl;
     }
 
     /**
-     * set final result in meta data; if no value was given then it
+     * Set final result in meta data; if no value was given then it
      * sets the biggest value of any check.
-     * @param integer  $iResult  set resultcode
+     * 
+     * @param integer  $iResult  set resultcode; one of RESULT_OK|RESULT_WARNING|RESULT_ERROR|RESULT_UNKNOWN
      * @return boolean
      */
-    public function setResult($iResult = false) {
-        if ($iResult === false) {
+    public function setResult(int $iResult = -1): bool
+    {
+        if ($iResult === -1) {
             $iResult = $this->_iMaxResult; // see addCheck()
         }
-        return $this->_aMeta["result"] = $iResult;
+        $this->_aMeta["result"] = $iResult;
+        return true;
     }
 
     /**
-     * add a check array;
-     * @param type $aJob
-     * @return type
+     * Add a check array
+     * @param array  $aJob  array with check data
+     * @return boolean
      */
-    public function addCheck($aJob = []) {
+    public function addCheck($aJob = []): bool
+    {
 
         require_once 'appmonitor-checks.class.php';
         $oCheck = new appmonitorcheck();
         $aCheck = $oCheck->makecheck($aJob);
-        
+
         // limit result code
-        $iMyResult=isset($aJob['worstresult']) 
-                ? min($aCheck["result"], $aJob['worstresult'])
-                : $aCheck["result"]
-                ;
+        $iMyResult = isset($aJob['worstresult'])
+            ? min($aCheck["result"], $aJob['worstresult'])
+            : $aCheck["result"]
+        ;
 
         if (!$this->_iMaxResult || $iMyResult > $this->_iMaxResult) {
             $this->_iMaxResult = $iMyResult;
         }
-        return $this->_aChecks[] = $aCheck;
+        $this->_aChecks[] = $aCheck;
+        return true;
     }
 
     /**
-     * add an item to notifications meta data
+     * Add an item to notifications meta data
+     * @see addEmail()
+     * @see addSlack()
      * 
      * @param string $sType   type ... one of email|slack
-     * @param type $sValue    value
-     * @param type $sKey      optional key (for key->value instead of list of values)
+     * @param string $sValue    value
+     * @param string $sKey      optional key (for key->value instead of list of values)
      * @return boolean
      */
-    protected function _addNotification($sType, $sValue, $sKey = false) {
+    protected function _addNotification(string $sType, string $sValue, string $sKey = ''): bool
+    {
         $sTypeCleaned = preg_replace('/[^a-z]/', '', strtolower($sType));
         if (!isset($this->_aMeta['notifications'])) {
             $this->_aMeta['notifications'] = [];
@@ -236,12 +258,13 @@ class appmonitor {
     }
 
     /**
-     * add an email to notifications list
+     * Add an email to notifications list
      * 
      * @param string $sEmailAddress  email address to add
      * @return boolean
      */
-    public function addEmail($sEmailAddress) {
+    public function addEmail(string $sEmailAddress)
+    {
         return $this->_addNotification('email', $sEmailAddress);
     }
 
@@ -249,36 +272,41 @@ class appmonitor {
      * Add slack channel for notification
      * @param string  $sLabel
      * @param string  $sSlackWebhookUrl
-     * @return type
+     * @return boolean
      */
-    public function addSlackWebhook($sLabel, $sSlackWebhookUrl) {
+    public function addSlackWebhook(string $sLabel, string $sSlackWebhookUrl): bool
+    {
         return $this->_addNotification('slack', $sSlackWebhookUrl, $sLabel);
     }
+
     /**
-     * add a tag for grouping in the server gui
+     * Add a tag for grouping in the server gui.
+     * Spaces will be replaced with underscore
      * 
-     * @param string  $sLabel
-     * @param string  $sSlackWebhookUrl
-     * @return type
+     * @param string  $sTag  tag to add
+     * @return boolean
      */
-    public function addTag($sTag) {
-        if(!isset($this->_aMeta['tags'])){
-            $this->_aMeta['tags']=[];
+    public function addTag(string $sTag): bool
+    {
+        if (!isset($this->_aMeta['tags'])) {
+            $this->_aMeta['tags'] = [];
         }
-        $this->_aMeta['tags'][]=$sTag;
+        $this->_aMeta['tags'][] = str_replace(' ', '_', $sTag);
         return true;
     }
 
     /**
-     * check referers IP address if it matches any entry in the list
+     * Check referers IP address if it matches any entry in the list
      * requires http request; CLI is always allowed
      * On deny this method exits with 403 response
      * 
      * @param array $aAllowedIps  array of allowed ip addresses / ranges
      *                            the ip must match from the beginning, i.e.
      *                            "127.0." will allow requests from 127.0.X.Y
+     * @return boolean
      */
-    public function checkIp($aAllowedIps = []) {
+    public function checkIp(array $aAllowedIps = []): bool
+    {
         if (!isset($_SERVER['REMOTE_ADDR']) || !count($aAllowedIps)) {
             return true;
         }
@@ -297,11 +325,12 @@ class appmonitor {
      * requires http request; CLI is always allowed
      * On deny this method exits with 403 response
      * 
-     * @param type $sVarname
-     * @param type $sToken
+     * @param string  $sVarname  name of GET variable
+     * @param string  $sToken    value
      * @return boolean
      */
-    public function checkToken($sVarname, $sToken) {
+    public function checkToken(string $sVarname, string $sToken): bool
+    {
         if (!isset($_GET)) {
             return true;
         }
@@ -322,7 +351,8 @@ class appmonitor {
      * all function names.
      * @return array
      */
-    public function listChecks() {
+    public function listChecks(): array
+    {
         require_once 'appmonitor-checks.class.php';
         $oCheck = new appmonitorcheck();
         return $oCheck->listChecks();
@@ -334,8 +364,10 @@ class appmonitor {
 
     /**
      * verify array values and in case of an error abort and show all found errors
+     * @return boolean
      */
-    protected function _checkData() {
+    protected function _checkData(): bool
+    {
         $aErrors = [];
 
         if (!count($this->_aChecks)) {
@@ -360,19 +392,22 @@ class appmonitor {
     // ----------------------------------------------------------------------
 
     /**
-     * stop processing the client checks and abort with an error
-     * @param string $sMessage
+     * Stop processing the client checks and abort with an error
+     * @param string $sMessage  text to show after a 503 headline
+     * @return void
      */
-    public function abort($sMessage){
+    public function abort(string $sMessage): void
+    {
         header('HTTP/1.0 503 Service Unavailable');
-        die('<h1>503 Service Unavailable</h1>'.$sMessage);
+        die('<h1>503 Service Unavailable</h1>' . $sMessage);
     }
-    
+
     /**
-     * get full array for response with metadata and Checks
-     * @return type
+     * Get full array for response with metadata and checks
+     * @return array
      */
-    public function getResults() {
+    public function getResults(): array
+    {
         return [
             "meta" => $this->_aMeta,
             "checks" => $this->_aChecks,
@@ -380,74 +415,24 @@ class appmonitor {
     }
 
     /**
-     * output appmonitor values as JSON
-     * @param bool  $bPretty     turn on pretty print; default is false
-     * @param bool  $bHighlight  print syntax highlighted html code; $bPretty must be true to enable
+     * Send http response with header and appmonitor JSON data
+     * @return string
      */
-    public function render($bPretty = false, $bHighlight = false) {
+    public function render(): string
+    {
         $this->_checkData();
         $this->_aMeta['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms';
+        $sOut=json_encode($this->getResults());
 
-        // JSON_PRETTY_PRINT reqires PHP 5.4
-        if (!defined('JSON_PRETTY_PRINT')) {
-            $bPretty = false;
-        }
-        if (!$bPretty) {
-            $bHighlight = false;
-            $sOut = json_encode($this->getResults());
-        } else {
-            $sOut = json_encode($this->getResults(), JSON_PRETTY_PRINT);
-            if ($bHighlight) {
-                $aMsg = [
-                    0 => "OK",
-                    1 => "UNKNOWN",
-                    2 => "WARNING",
-                    3 => "ERROR"
-                ];
-                foreach (array_keys($aMsg) as $iCode) {
-                    $sOut = preg_replace('/(\"result\":\ ' . $iCode . ')/', '$1 <span class="result' . $iCode . '"> &lt;--- ' . $aMsg[$iCode] . ' </span>', $sOut);
-                }
-
-                $sOut = preg_replace('/:\ \"(.*)\"/U', ': "<span style="color:#66e;">$1</span>"', $sOut);
-                $sOut = preg_replace('/:\ ([0-9]*)/', ': <span style="color:#3a3; font-weight: bold;">$1</span>', $sOut);
-                $sOut = preg_replace('/\"(.*)\":/U', '"<span style="color:#840;">$1</span>":', $sOut);
-
-                $sOut = preg_replace('/([{\[])/', '$1<blockquote>', $sOut);
-                $sOut = preg_replace('/([}\]])/', '</blockquote>$1', $sOut);
-                $sOut = str_replace('    ', '', $sOut);
-                // $sOut = preg_replace('/([{}])/', '<span style="color:#a00; ">$1</span>', $sOut);
-                // $sOut = preg_replace('/([\[\]])/', '<span style="color:#088; ">$1</span>', $sOut);
-
-                $sOut = '<!DOCTYPE html><html><head>'
-                        . '<style>'
-                        . 'body{background:#e0e8f8; color:#235; font-family: verdana,arial;}'
-                        . 'blockquote{background:rgba(0,0,0,0.03); border-left: 0px solid rgba(0,0,0,0.06); margin: 0 0 0 3em; padding: 0; border-radius: 1em; border-top-left-radius: 0;}'
-                        . 'blockquote blockquote:hover{; }'
-                        . 'blockquote blockquote blockquote:hover{border-color: #808;}'
-                        . 'pre{background:rgba(0,0,0,0.05); padding: 1em; border-radius: 1em;}'
-                        . '.result0{background:#aca; border-right: 0em solid #080;}'
-                        . '.result1{background:#666; border-right: 0em solid #ccc;}'
-                        . '.result2{background:#fc9; border-right: 0em solid #860;}'
-                        . '.result3{background:#800; border-right: 0em solid #f00;}'
-                        . '</style>'
-                        . '<title>' . __CLASS__ . '</title>'
-                        . '</head><body>'
-                        . '<h1>' . __CLASS__ . ' :: debug</h1>'
-                        . '<pre>'
-                        . $sOut
-                        . '</pre></body></html>';
-            }
-        }
-        if (!$bHighlight) {
-            header('Content-type: application/json');
-            header('Cache-Control: cache');
-            header('max-age: ' . $this->_aMeta["ttl"]);
-        }
+        header('Content-type: application/json');
+        header('Cache-Control: cache');
+        header('max-age: ' . $this->_aMeta["ttl"]);
         echo $sOut;
         return $sOut;
     }
+
     /**
-     * output appmonitor client status as single html page
+     * Output appmonitor client status as single html page
      * 
      * @example <code>
      * ob_start();<br>
@@ -458,9 +443,10 @@ class appmonitor {
      * </code>
      * 
      * @param string  $sJson  JSON of client output
+     * @return string
      */
-    public function renderHtmloutput($sJson) {
-
+    public function renderHtmloutput(string $sJson): string
+    {
 
         header('Content-type: text/html');
         header('Cache-Control: cache');
@@ -473,61 +459,79 @@ class appmonitor {
         ];
 
         // $sOut = print_r($sJson, 1);
-        $aData= json_decode($sJson, 1);
+        $aData = json_decode($sJson, 1);
 
         // ----- Ausgabe human readable
-        $sOut='';
-        $sOut.='<h2>Metadata</h2>'
-                . '<div class="meta'.(isset($aData['meta']['result'])  ? ' result'.$aData['meta']['result'] : '' ) .'">'
-                . 'Status: '  . (isset($aData['meta']['result'])  ? $aMsg[$aData['meta']['result']] : '?').'<br>'
-                . '</div>'
-                . 'Host: '    . (isset($aData['meta']['host'])    ? '<span class="string">' . $aData['meta']['host']   .'</span>'    : '?').'<br>'
-                . 'Website: ' . (isset($aData['meta']['website']) ? '<span class="string">' . $aData['meta']['website'].'</span>'  : '?').'<br>'
-                . 'Execution time: '    . (isset($aData['meta']['time'])    ? '<span class="float">'  . $aData['meta']['time']  .'</span>'  : '?').'<br>'
-                . 'Client: '    . (isset($aData['meta']['version'])    ? '<span class="float">'  . $aData['meta']['version']  .'</span>'  : '?').'<br>'
-
-                .'<h2>Checks</h2>'
-                ;
-        if (isset($aData['checks'][0]) && count($aData['checks'])){
-            foreach($aData['checks'] as $aCheck){
-               $sOut.= ''
-               . '<span class="result'.$aCheck['result'].'"> <strong>'.$aCheck['name'].'</strong></span> <br>'
-               . '<div class="check">'
+        $sOut = '';
+        $sOut .= ''
+            . '<h2>Metadata</h2>'
+            . '<div class="meta' . (isset($aData['meta']['result']) ? ' result' . $aData['meta']['result'] : '') . '">'
+            . 'Status: ' . (isset($aData['meta']['result']) ? $aMsg[$aData['meta']['result']] : '?') . '<br>'
+            . '</div>'
+            . 'Host: ' . (isset($aData['meta']['host']) ? '<span class="string">' . $aData['meta']['host'] . '</span>' : '?') . '<br>'
+            . 'Website: ' . (isset($aData['meta']['website']) ? '<span class="string">' . $aData['meta']['website'] . '</span>' : '?') . '<br>'
+            . 'Execution time: ' . (isset($aData['meta']['time']) ? '<span class="float">' . $aData['meta']['time'] . '</span>' : '?') . '<br>'
+            . 'Client: ' . (isset($aData['meta']['version']) ? '<span class="string">' . $aData['meta']['version'] . '</span>' : '?') . '<br>'
+
+            . '<h2>Checks</h2>'
+        ;
+        if (isset($aData['checks'][0]) && count($aData['checks'])) {
+            foreach ($aData['checks'] as $aCheck) {
+                $sOut .= ''
+                    . '<span class="result' . $aCheck['result'] . '"> <strong>' . $aCheck['name'] . '</strong></span> <br>'
+                    . '<div class="check">'
                     . '<div class="description">'
-                        . $aCheck['description'].'<br>'
-                        . $aCheck['value'].'<br>'
+                    . $aCheck['description'] . '<br>'
+                    . $aCheck['value'] . '<br>'
+                    . '</div>'
+                    . 'Execution time: <span class="float">' . (isset($aCheck['time']) ? $aCheck['time'] : ' - ') . '</span><br>'
+                    . 'Group: <span class="string">' . (isset($aCheck['group']) ? $aCheck['group'] : '-') . '</span><br>'
+                    . 'parent: <span class="string">' . (isset($aCheck['parent']) ? $aCheck['parent'] : '-') . '</span><br>'
+                    . 'Status: ' . $aMsg[$aCheck['result']] . '<br>'
                     . '</div>'
-                    . 'Execution time: ' . (isset($aCheck['time']) ? $aCheck['time'] : ' - ').'<br>'
-                    . 'Group: '  . (isset($aCheck['group']) ? $aCheck['group'] : '-').'<br>'
-                    . 'parent: ' . (isset($aCheck['parent']) ? $aCheck['parent']: '-').'<br>'
-                    . 'Status: ' . $aMsg[$aCheck['result']].'<br>'
-                   . '</div>'
-                   ;
+                ;
             }
         }
-        $sOut.= '<h2>List of farbcodes</h2>';
-        foreach ($aMsg as $i=>$sText){
-            $sOut.= '<span class="result'.$i.'">'. $sText.'</span> ';
+        $sOut .= '<h2>List of farbcodes</h2>';
+        foreach ($aMsg as $i => $sText) {
+            $sOut .= '<span class="result' . $i . '">' . $sText . '</span> ';
         }
-        $sOut.='<h2>Raw result data</h2><pre>'.json_encode($aData, JSON_PRETTY_PRINT).'</pre>';
+
+        $sRaw=json_encode($aData, JSON_PRETTY_PRINT);
+        $sRaw = preg_replace('/:\ \"(.*)\"/U', ': "<span class="string">$1</span>"', $sRaw);
+        $sRaw = preg_replace('/:\ ([0-9]*)/', ': <span class="int">$1</span>', $sRaw);
+        $sRaw = preg_replace('/\"(.*)\":/U', '"<span class="key">$1</span>":', $sRaw);
+
+        $sOut .= '<h2>Raw result data</h2><pre id="raw">' . $sRaw . '</pre>';
+
+
         $sOut = '<!DOCTYPE html><html><head>'
-                . '<style>'
-                . 'body{background:#fff; color:#444; font-family: verdana,arial; margin: 3em;}'
-                . 'h1{color:#346;}'
-                . 'h2{color:#569; margin-top: 1.5em;}'
-                . '.check{border: 1px solid; padding: 0.4em; margin-bottom: 2em;}'
-                . '.description{font-style: italic; padding: 0.4em 1em;}'
-                . '.result0{background:#aca; border-left: 1em solid #080; padding: 0.5em; }'
-                . '.result1{background:#ccc; border-left: 1em solid #aaa; padding: 0.5em; }'
-                . '.result2{background:#fc9; border-left: 1em solid #860; padding: 0.5em; }'
-                . '.result3{background:#f88; border-left: 1em solid #f00; padding: 0.5em; }'
-                . '</style>'
-                . '<title>' . __CLASS__ . '</title>'
-                . '</head><body>'
-                . '<h1>' . __CLASS__ . ' :: client status</h1>'
-                . $sOut
-                . '</body></html>';
-        echo $sOut;
+            . '<style>'
+            . 'body{background:#eee; color:#444; font-family: verdana,arial; margin: 0; }'
+            . 'body>div#content{background: #fff; border-radius: 2em; border: 4px solid #abc; box-shadow: 0.5em 0.5em 2em #aaa; margin: 2em 10%; padding: 2em;}'
+            . 'h1{color:#346; margin: 0;}'
+            . 'h2{color:#569; margin-top: 2em;}'
+            . 'pre{background:#f4f4f8; padding: 1em; overflow-x:auto; }'
+            . '#raw .key{color:#808;}'
+            . '#raw .int{color:#3a3; font-weight: bold;}'
+            . '#raw .string{color:#66e;}'
+            . '.check{border: 1px solid #ccc; padding: 0.4em; margin-bottom: 2em;}'
+            . '.description{font-style: italic; padding: 0.4em 1em;}'
+            . '.float{color:#080;}'
+            . '.meta{margin-bottom: 1em;}'
+            . '.result0{background:#aca; border-left: 1em solid #080; padding: 0.5em; }'
+            . '.result1{background:#ccc; border-left: 1em solid #aaa; padding: 0.5em; }'
+            . '.result2{background:#fc9; border-left: 1em solid #860; padding: 0.5em; }'
+            . '.result3{background:#f88; border-left: 1em solid #f00; padding: 0.5em; }'
+            . '.string{color:#338;}'
+            . '</style>'
+            . '<title>' . __CLASS__ . '</title>'
+            . '</head><body>'
+            . '<div id="content">'
+            . '<h1>' . __CLASS__ . ' :: client status</h1>'
+            . $sOut
+            . '</div>'
+            . '</body></html>';
         return $sOut;
     }
 
diff --git a/public_html/appmonitor/classes/client_all_in_one.php b/public_html/appmonitor/classes/client_all_in_one.php
index 874d1bd0..b17aa09c 100644
--- a/public_html/appmonitor/classes/client_all_in_one.php
+++ b/public_html/appmonitor/classes/client_all_in_one.php
@@ -3,21 +3,24 @@
 
     MERGED APPMONITOR CLIENT :: WORK IN PROGRESS
 
-    generated Do 25 Nov 2021 15:45:27 CET
+    generated Tue Jul 23 16:47:01 CEST 2024
 
 */
 
- class checkApacheProcesses extends appmonitorcheck{ protected $_sServerStatusUrl = 'http://localhost/server-status'; protected $_iWarn = 50; protected $_iError = 75; protected function _getApacheProcesses() { $sBody = file_get_contents($this->_sServerStatusUrl); if(!$sBody){ return false; } $sRegexScoreboard = '/<pre>(.*)\<\/pre\>/U'; $aScore=array(); $sStatusNobr = str_replace("\n", "", $sBody); if (preg_match_all($sRegexScoreboard, $sStatusNobr, $aTmpTable)) { $sScoreString=$aTmpTable[1][0]; $aScore['total']=strlen($sScoreString); $aScore['free']=substr_count($sScoreString, '.'); $aScore['waiting']=substr_count($sScoreString, '_'); $aScore['active']=$aScore['total']-$aScore['free']-$aScore['waiting']; } return $aScore; } public function run($aParams){ if(isset($aParams['url']) && $aParams['url']){ $this->_sServerStatusUrl=$aParams['url']; } if(isset($aParams['warning']) && (int)$aParams['warning']){ $this->_iWarn=(int)$aParams['warning']; } if(isset($aParams['error']) && (int)$aParams['error']){ $this->_iError=(int)$aParams['error']; } $aProcesses=$this->_getApacheProcesses(); $iActive=$aProcesses ? $aProcesses['active'] : false; if($iActive===false){ $iResult=RESULT_UNKNOWN; } else { $sComment=''; $iTotal=$aProcesses['total']; $iResult=RESULT_OK; if(($iActive/$iTotal*100)>$this->_iWarn){ $iResult=RESULT_WARNING; $sComment='more than warning level '.$this->_iWarn.'%'; } else { $sComment='less than warning level '.$this->_iWarn.'%'; } if(($iActive/$iTotal*100)>$this->_iError){ $iResult=RESULT_ERROR; $sComment='more than error level '.$this->_iError.'%'; } } return array( $iResult, ($iActive===false ? 'Apache httpd server status is not available' : 'apache processes: '.print_r($aProcesses, 1)).' '.$sComment, ($iActive===false ? array() : array( 'type'=>'counter', 'count'=>$iActive, 'visual'=>'line', ) ) ); } } 
- class checkCert extends appmonitorcheck{ public function run($aParams) { $sUrl = isset($aParams["url"]) ? $aParams["url"] : 'http'. ($_SERVER['HTTPS'] ? 's' : '') . '://' . $_SERVER['SERVER_NAME'] .':' . $_SERVER['SERVER_PORT'] ; $bVerify = isset($aParams["verify"]) ? !!$aParams["verify"] : true; $iWarn = isset($aParams["warning"]) ? (int)($aParams["warning"]) : 30; $sMessage="Checked url: $sUrl ... "; $certinfo=$this->_certGetInfos($sUrl, $bVerify); if(isset($certinfo['_error'])){ return [ RESULT_ERROR, $certinfo['_error'] . $sMessage ]; } $sDNS=isset($certinfo['extensions']['subjectAltName']) ? $certinfo['extensions']['subjectAltName'] : false; $sHost=parse_url($sUrl,PHP_URL_HOST); if(strstr($sDNS, 'DNS:'.$sHost)===false){ return [ RESULT_ERROR, 'Wrong certificate: '.$sHost.' is not listed as DNS alias in ['.$sDNS.']  ' . $sMessage ]; } $iDaysleft = round(($certinfo['validTo_time_t'] - date('U')) / 60 / 60 / 24); $sMessage.= 'Issuer: '. $sIssuer=$certinfo['issuer']['O'] . '; valid from: '. date("Y-m-d H:i", $certinfo['validFrom_time_t']) . ' to '.date("Y-m-d H:i", $certinfo['validTo_time_t']).' ' . ( $iDaysleft ? "($iDaysleft days left)" : "expired since ".(-$iDaysleft)." days.") ; if ($iDaysleft<0) { return [ RESULT_ERROR, 'Expired! ' . $sMessage ]; } if ($iDaysleft<=$iWarn) { return [ RESULT_WARNING, 'Expires soon. ' . $sMessage ]; } return [ RESULT_OK, 'OK. ' .($bVerify ? 'Certificate is valid. ' : '(Verification is disabled; Check for expiration only.) ' ) . $sMessage ]; } } 
- class checkDiskfree extends appmonitorcheck{ public function run($aParams) { $this->_checkArrayKeys($aParams, "directory", "critical"); $sDirectory = $aParams["directory"]; if(!is_dir($sDirectory)){ return [ RESULT_ERROR, 'directory [' . $sDirectory . '] does not exist. Maybe it is wrong or is not mounted.' ]; } $iWarn = isset($aParams["warning"]) ? $this->_getSize($aParams["warning"]) : false; $iCritical = $this->_getSize($aParams["critical"]); $iSpaceLeft=disk_free_space($sDirectory); $sMessage='[' . $sDirectory . '] has '.$this->_getHrSize($iSpaceLeft).' left.'; if($iWarn){ if($iWarn<=$iCritical){ header('HTTP/1.0 503 Service Unavailable'); die("ERROR in a Diskfree check - warning value must be larger than critical.<pre>" . print_r($aParams, true)); } if ($iWarn<$iSpaceLeft){ return [ RESULT_OK, $sMessage.' Warning level is not reached yet (still '.$this->_getHrSize($iSpaceLeft-$iWarn).' over warning limit).' ]; } if ($iWarn>$iSpaceLeft && $iCritical<$iSpaceLeft){ return [ RESULT_WARNING, $sMessage.' Warning level '.$this->_getHrSize($iWarn).' was reached (space is '.$this->_getHrSize($iWarn-$iSpaceLeft).' below warning limit; still '.$this->_getHrSize($iSpaceLeft-$iCritical).' over critical limit).' ]; } } if ($iCritical<$iSpaceLeft){ return [RESULT_OK, $sMessage .' Minimum is not reached yet (still '.$this->_getHrSize($iSpaceLeft-$iCritical).' over critical limit).']; } else { return [RESULT_ERROR, $sMessage]; } } } 
- class checkFile extends appmonitorcheck{ public function run($aParams) { $aOK = array(); $aErrors = array(); $this->_checkArrayKeys($aParams, "filename"); $sFile = $aParams["filename"]; if (isset($aParams['exists'])) { $sMyflag = 'exists=' . ($aParams['exists'] ? 'yes' : 'no'); if (file_exists($sFile) && $aParams['exists']) { $aOK[] = $sMyflag; } else { $aErrors[] = $sMyflag; } } foreach (array('dir', 'executable', 'file', 'link', 'readable', 'writable') as $sFiletest) { if (isset($aParams[$sFiletest])) { $sTestCmd = 'return is_' . $sFiletest . '("' . $sFile . '");'; if (eval($sTestCmd) && $aParams[$sFiletest]) { $aOK[] = $sFiletest . '=' . ($aParams[$sFiletest] ? 'yes' : 'no'); } else { $aErrors[] = $sFiletest . '=' . ($aParams[$sFiletest] ? 'yes' : 'no'); } } } $sMessage = (count($aOK) ? ' flags OK: ' . implode('|', $aOK) : '') . ' ' . (count($aErrors) ? ' flags FAILED: ' . implode('|', $aErrors) : '') ; if (count($aErrors)) { return [ RESULT_ERROR, 'file test [' . $sFile . '] ' . $sMessage ]; } else { return[ RESULT_OK, 'file test [' . $sFile . '] ' . $sMessage ]; } } } 
- class checkHello extends appmonitorcheck{ public function run($aParams){ $this->_checkArrayKeys($aParams, "message"); return array( RESULT_OK, 'Hello world! My message is: ' .$aParams['message'] ); } } 
- class checkHttpContent extends appmonitorcheck{ public function run($aParams) { $this->_checkArrayKeys($aParams, "url"); if (!function_exists("curl_init")) { header('HTTP/1.0 503 Service Unavailable'); die("ERROR: PHP CURL module is not installed."); } $bShowContent = (isset($aParams["content"]) && $aParams["content"]) ? true : false; $ch = curl_init($aParams["url"]); curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_NOBODY, isset($aParams["headeronly"]) && $aParams["headeronly"]); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, isset($aParams["follow"]) && $aParams["follow"]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($ch, CURLOPT_TIMEOUT, (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp); $res = curl_exec($ch); if (!$res) { return [ RESULT_ERROR, 'ERROR: failed to fetch ' . $aParams["url"] . '.' ]; } $sOut=''; $bError=false; $aInfos = curl_getinfo($ch); curl_close($ch); $aTmp=explode("\r\n\r\n", $res, 2); $sHttpHeader=$aTmp[0]; $sHttpBody=isset($aTmp[1]) ? $aTmp[1] : false; $sOut.="Http status: ".$aInfos['http_code']." - "; if(isset($aParams["status"])){ if($aInfos['http_code'] === $aParams["status"]){ $sOut.="compare OK<br>"; } else { $sOut.="compare failed<br>"; $bError=true; } } else { if($aInfos['http_code'] >= 400){ $sOut.="Error page detected<br>"; $bError=true; } else { $sOut.="request successful<br>"; } } if(isset($aParams["headercontains"]) && $aParams["headercontains"]){ $sOut.="Http header contains &quot;".$aParams["headercontains"]."&quot; - "; if(!strstr($sHttpHeader, $aParams["headercontains"])===false){ $sOut.="compare OK<br>"; } else { $sOut.="compare failed<br>"; $bError=true; } } if(isset($aParams["headernotcontains"]) && $aParams["headernotcontains"]){ $sOut.="Http header does not contain &quot;".$aParams["headernotcontains"]."&quot; - "; if(strstr($sHttpHeader, $aParams["headernotcontains"])===false){ $sOut.="compare OK<br>"; } else { $sOut.="compare failed<br>"; $bError=true; } } if(isset($aParams["headerregex"]) && $aParams["headerregex"]){ $sOut.="Http header regex test &quot;".$aParams["headerregex"]."&quot; - "; try{ $bRegex=preg_match($aParams["headerregex"], $sHttpHeader); if($bRegex){ $sOut.="compare OK<br>"; } else { $sOut.="compare failed<br>"; $bError=true; } } catch(Exception $e){ $sOut.="Wrong REGEX<br>" . print_r($e, 1).'<br>'; $bError=true; } } if(isset($aParams["bodycontains"]) && $aParams["bodycontains"]){ $sOut.="Http body contains &quot;".$aParams["bodycontains"]."&quot; - "; if(!strstr($sHttpBody, $aParams["bodycontains"])===false){ $sOut.="compare OK<br>"; } else { $sOut.="compare failed<br>"; $bError=true; } } if(isset($aParams["bodynotcontains"]) && $aParams["bodynotcontains"]){ $sOut.="Http body does not contain &quot;".$aParams["bodynotcontains"]."&quot; - "; if(strstr($sHttpBody, $aParams["bodynotcontains"])===false){ $sOut.="compare OK<br>"; } else { $sOut.="compare failed<br>"; $bError=true; } } if(isset($aParams["bodyregex"]) && $aParams["bodyregex"]){ $sOut.="Http body regex test &quot;".$aParams["bodyregex"]."&quot; - "; try{ $bRegex=preg_match($aParams["bodyregex"], $sHttpBody); if($bRegex){ $sOut.="compare OK<br>"; } else { $sOut.="compare failed<br>"; $bError=true; } } catch(Exception $e){ $sOut.="Wrong REGEX<br>" . print_r($e, 1).'<br>'; $bError=true; } } if (!$bError) { return [ RESULT_OK, 'OK: http check "' . $aParams["url"] . '".<br>'.$sOut ]; } else { return [ RESULT_ERROR, 'ERROR: http check "' . $aParams["url"] . '".<br>'.$sOut ]; } } } 
- class checkLoadmeter extends appmonitorcheck{ protected function _getLoad() { if (function_exists('sys_getloadavg')){ $load = sys_getloadavg(); return $load[0]; } else { if(class_exists('COM')){ $wmi=new COM('WinMgmts:\\\\.'); $cpus=$wmi->InstancesOf('Win32_Processor'); $load=0; if(version_compare('4.50.0', PHP_VERSION) == 1){ while($cpu = $cpus->Next()){ $load += $cpu->LoadPercentage; } }else{ foreach($cpus as $cpu){ $load += $cpu->LoadPercentage; } } return $load; } return false; } } public function run($aParams){ $fLoad=$this->_getLoad(); if($fLoad===false){ $iResult=RESULT_UNKNOWN; } else { $iResult=RESULT_OK; if(isset($aParams['warning']) && $aParams['warning'] && $fLoad>$aParams['warning']){ $iResult=RESULT_WARNING; } if(isset($aParams['error']) && $aParams['error'] && $fLoad>$aParams['error']){ $iResult=RESULT_ERROR; } } return array( $iResult, ($fLoad===false ? 'load value is not available' : 'current load is: '.$fLoad), ($fLoad===false ? array() : array( 'type'=>'counter', 'count'=>$fLoad, 'visual'=>'line', ) ) ); } } 
- class checkMysqlConnect extends appmonitorcheck{ public function run($aParams) { $this->_checkArrayKeys($aParams, "server,user,password,db"); $mysqli=mysqli_init(); if(!$mysqli){ return [RESULT_ERROR, 'ERROR: mysqli_init failed.']; } if (!$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp)) { return [RESULT_ERROR, 'ERROR: setting mysqli_options failed.']; } $db = (isset($aParams["port"]) && $aParams["port"]) ? $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"], $aParams["port"]) : $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"]) ; if ($db) { $mysqli->close(); return [RESULT_OK, "OK: Mysql database " . $aParams["db"] . " was connected"]; return true; } else { return [ RESULT_ERROR, "ERROR: Mysql database " . $aParams["db"] . " was not connected. Error ".mysqli_connect_errno() .": ". mysqli_connect_error() ]; } } } 
- class checkPdoConnect extends appmonitorcheck{ public function run($aParams) { $this->_checkArrayKeys($aParams, "connect,user,password"); try{ $db = new PDO( $aParams['connect'], $aParams['user'], $aParams['password'], array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp, ) ); $db=null; return [RESULT_OK, "OK: Database was connected with PDO " . $aParams['connect']]; } catch(PDOException $e) { return [RESULT_ERROR, "ERROR: Database was not connected " . $aParams['connect'] . " was not connected. Error ".$e->getMessage()]; } } } 
- class checkPortTcp extends appmonitorcheck{ public function run($aParams) { $this->_checkArrayKeys($aParams, "port"); $sHost = array_key_exists('host', $aParams) ? $aParams['host'] : '127.0.0.1'; $iPort = (int) $aParams['port']; $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if ($socket === false) { return [RESULT_UNKNOWN, "ERROR: $sHost:$iPort was not checked. socket_create() failed: " . socket_strerror(socket_last_error())]; } socket_set_option( $socket, SOL_SOCKET, SO_SNDTIMEO, array( "sec"=>(isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp, "usec"=>0 ) ); $result = socket_connect($socket, $sHost, $iPort); if ($result === false) { socket_close($socket); return [RESULT_ERROR, "ERROR: $sHost:$iPort failed. " . socket_strerror(socket_last_error($socket))]; } else { socket_close($socket); return [RESULT_OK, "OK: $sHost:$iPort was connected."]; } } } 
- class checkSimple extends appmonitorcheck{ public function run($aParams) { $this->_checkArrayKeys($aParams, "result,value"); $aData=[]; foreach(array('type', 'count', 'visual') as $sMyKey){ if(isset($aParams[$sMyKey])){ $aData[$sMyKey]=$aParams[$sMyKey]; } } return [ $aParams["result"], $aParams["value"], count($aData) ? $aData : false ]; } } 
- class checkSqliteConnect extends appmonitorcheck{ public function run($aParams) { $this->_checkArrayKeys($aParams, "db"); if (!file_exists($aParams["db"])) { return [RESULT_ERROR, "ERROR: Sqlite database file " . $aParams["db"] . " does not exist."]; } if(!isset($aParams['user'])){ $aParams['user']=''; } if(!isset($aParams['password'])){ $aParams['password']=''; } try { $o = new PDO("sqlite:" . $aParams["db"], $aParams['user'], $aParams['password'], array( PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp, ) ); return [RESULT_OK, "OK: Sqlite database " . $aParams["db"] . " was connected"]; } catch (Exception $e) { return [RESULT_ERROR, "ERROR: Sqlite database " . $aParams["db"] . " was not connected. " . $e->getMessage()]; } } } 
- if(!defined('RESULT_OK')){ define("RESULT_OK", 0); define("RESULT_UNKNOWN", 1); define("RESULT_WARNING", 2); define("RESULT_ERROR", 3); } class appmonitorcheck { protected $_aConfig = array(); protected $_aData = array(); protected $_units = array( 'B', 'KB', 'MB', 'GB', 'TB'); protected $_iTimeoutTcp=5; protected $_sPluginDir=__DIR__.'/../plugins'; public function __construct() { } protected function _createDefaultMetadata() { $this->_aData = array( "name" => $this->_aConfig["name"], "description" => $this->_aConfig["description"], "result" => RESULT_UNKNOWN, "value" => false, "type" => false, "time" => false, ); return true; } protected function _setResult($iResult) { return $this->_aData["result"] = (int) $iResult; } protected function _setOutput($s) { return $this->_aData["value"] = (string) $s; } protected function _setCounter($aParams){ if(count($aParams)){ foreach(array('type', 'count', 'visual') as $sMyKey){ if(isset($aParams[$sMyKey])){ $this->_aData[$sMyKey]=$aParams[$sMyKey]; } } } return true; } protected function _setReturn($iResult, $s, $aCounter=array()) { $this->_setResult($iResult); $this->_setOutput($s); $this->_setCounter($aCounter); return true; } protected function _checkArrayKeys($aConfig, $sKeyList) { foreach (explode(",", $sKeyList) as $sKey) { if (!array_key_exists($sKey, $aConfig)) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' .__METHOD__ . " - array of check parameters requires the keys [$sKeyList] - but key <code>$sKey</code> was not found in config array." . "<pre>" . print_r($aConfig, true) .'</pre>' ); } if (is_null($aConfig[$sKey])) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' .__METHOD__ . " - key <code>$sKey</code> is empty in config array" . "<pre>" . print_r($aConfig, true) .'</pre>' ); } } return true; } public function makeCheck($aConfig) { $this->_iStart = microtime(true); $this->_checkArrayKeys($aConfig, "name,description,check"); $this->_checkArrayKeys($aConfig["check"], "function"); $this->_aConfig = $aConfig; $this->_createDefaultMetadata(); $sCheck = preg_replace('/[^a-zA-Z0-9]/', '', $this->_aConfig["check"]["function"]); $aParams = array_key_exists("params", $this->_aConfig["check"]) ? $this->_aConfig["check"]["params"] : array(); $sPluginFile=$this->_sPluginDir.'/checks/'.$sCheck.'.php'; $sCheckClass = 'check'.$sCheck; if (!class_exists($sCheckClass)){ if (file_exists($sPluginFile)) { } } if (!class_exists($sCheckClass)){ header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' .__METHOD__ . " - check class not found: <code>$sCheckClass</code>" . "<pre>" . print_r($aConfig, true) .'</pre>' ."<h2>Known checks</h2>\n".print_r($this->listChecks(), 1) ); } $oPlogin = new $sCheckClass; $aResponse=$oPlogin->run($aParams); if(!is_array($aResponse)){ header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' .__METHOD__ . " - plugin : $sCheck does not responses an array" . "<pre>INPUT " . print_r($aConfig, true) .'</pre>' . "<pre>RESPONSE " . print_r($aResponse, true) .'</pre>' ); } if(count($aResponse)<2){ header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' .__METHOD__ . " - plugin : $sCheck does not responses the minimum of 2 array values" . "<pre>INPUT " . print_r($aConfig, true) .'</pre>' . "<pre>RESPONSE " . print_r($aResponse, true) .'</pre>' ); } if(!isset($aResponse[2])){ $aResponse[2]=array(); } $this->_setReturn($aResponse[0], $aResponse[1], $aResponse[2]); $this->_aData['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms'; return $this->respond(); } public function listChecks() { $aReturn = array(); $class = new ReflectionClass($this); foreach ($class->getMethods(ReflectionMethod::IS_PROTECTED) as $oReflectionMethod) { if (strpos($oReflectionMethod->name, "check") === 0) { $aReturn[(string) $oReflectionMethod->name]=1; } } foreach(glob($this->_sPluginDir.'/checks/*.php') as $sPluginFile){ $aReturn[str_replace('.php', '', basename($sPluginFile))] = 1; } ksort($aReturn); return array_keys($aReturn); } public function respond() { return $this->_aData; } protected function _certGetInfos($sUrl, $bVerifyCert) { $iTimeout=10; $aUrldata=parse_url($sUrl); $sHost = isset($aUrldata['host']) ? $aUrldata['host'] : false; $iPort = isset($aUrldata['port']) ? $aUrldata['port'] : ((isset($aUrldata['scheme']) && $aUrldata['scheme'] === 'https') ? 443 : false); $aSsl=array('capture_peer_cert' => true); if($bVerifyCert){ $aSsl['verify_peer']=false; $aSsl['verify_peer_name']=false; }; $get = stream_context_create(array('ssl' => $aSsl)); if(!$get){ return array('_error' => 'Error: Cannot create stream_context'); } $errno=-1; $errstr="stream_socket_client failed."; $read = stream_socket_client("ssl://$sHost:$iPort", $errno, $errstr, $iTimeout, STREAM_CLIENT_CONNECT, $get); if(!$read){ return array('_error' => "Error $errno: $errstr; cannot create stream_socket_client with given stream_context to ssl://$sHost:$iPort; you can try to set the flag [verify] to false to check expiration date only."); } $cert = stream_context_get_params($read); if(!$cert){ return array('_error' => "Error: socket was connected to ssl://$sHost:$iPort - but I cannot read certificate infos with stream_context_get_params "); } $certinfo = openssl_x509_parse($cert['options']['ssl']['peer_certificate']); return $certinfo; } protected function _getHrSize($size){ $power = $size > 0 ? floor(log($size, 1024)) : 0; return number_format($size / pow(1024, $power), 2, '.', ',') . ' ' . $this->_units[$power]; } protected function _getSize($sValue){ if(is_int($sValue)){ return $sValue; } $power=0; foreach($this->_units as $sUnit){ if (preg_match('/^[0-9\.\ ]*'.$sUnit.'/', $sValue)){ $i=preg_replace('/([0-9\.]*).*/', '$1', $sValue); $iReal=$i*pow(1024, $power); return $iReal; } $power++; } header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' .__METHOD__ . " ERROR in space value parameter - there is no size unit in [$sValue] - allowed size units are " . implode('|', $this->_units) ); } protected function _compare($value, $sCompare, $verifyValue){ switch ($sCompare){ case "IS": return $value===$verifyValue; break; case "GE": return $value>=$verifyValue; break; case "GT": return $value>$verifyValue; break; case "HAS": return !!(strstr($value, $verifyValue)!==false); break; default: header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' .__METHOD__ . " - FATAL ERROR: a compare function [$sCompare] is not implemented (yet)." ); break; } return false; } } 
-if (!class_exists('appmonitorcheck')){ } class appmonitor { protected $_sVersion = 'php-client-v0.93'; protected $_iDefaultTtl = 300; protected $_iMaxResult = false; protected $_aMeta = array(); protected $_aChecks = array(); protected $_iStart = false; public function __construct() { $this->_createDefaultMetadata(); } protected function _createDefaultMetadata() { $this->_iStart = microtime(true); $this->_aMeta = array( "host" => false, "website" => false, "ttl" => false, "result" => false, "time" => false, "version" => $this->_sVersion, ); $this->setHost(); $this->setWebsite(); $this->setTTL(); return true; } public function setHost($s = false) { if (!$s) { $s = php_uname("n"); } return $this->_aMeta["host"] = $s; } public function setWebsite($s = false) { if (!$s && isset($_SERVER["HTTP_HOST"])) { $s = $_SERVER["HTTP_HOST"]; } return $this->_aMeta["website"] = $s; } public function setTTL($iTTl = false) { if (!$iTTl) { $iTTl = $this->_iDefaultTtl; } return $this->_aMeta["ttl"] = $iTTl; } public function setResult($iResult = false) { if ($iResult === false) { $iResult = $this->_iMaxResult; } return $this->_aMeta["result"] = $iResult; } public function addCheck($aJob = array()) { $oCheck = new appmonitorcheck(); $aCheck = $oCheck->makecheck($aJob); $iMyResult=isset($aJob['worstresult']) ? min($aCheck["result"], $aJob['worstresult']) : $aCheck["result"] ; if (!$this->_iMaxResult || $iMyResult > $this->_iMaxResult) { $this->_iMaxResult = $iMyResult; } return $this->_aChecks[] = $aCheck; } protected function _addNotification($sType, $sValue, $sKey = false) { $sTypeCleaned = preg_replace('/[^a-z]/', '', strtolower($sType)); if (!isset($this->_aMeta['notifications'])) { $this->_aMeta['notifications'] = array(); } if (!isset($this->_aMeta['notifications'][$sTypeCleaned])) { $this->_aMeta['notifications'][$sTypeCleaned] = array(); } if ($sKey) { $this->_aMeta['notifications'][$sTypeCleaned][$sKey] = $sValue; } else { $this->_aMeta['notifications'][$sTypeCleaned][] = $sValue; } return true; } public function addEmail($sEmailAddress) { return $this->_addNotification('email', $sEmailAddress); } public function addSlackWebhook($sLabel, $sSlackWebhookUrl) { return $this->_addNotification('slack', $sSlackWebhookUrl, $sLabel); } public function addTag($sTag) { if(!isset($this->_aMeta['tags'])){ $this->_aMeta['tags']=array(); } $this->_aMeta['tags'][]=$sTag; return true; } public function checkIp($aAllowedIps = array()) { if (!isset($_SERVER['REMOTE_ADDR']) || !count($aAllowedIps)) { return true; } $sIP = $_SERVER['REMOTE_ADDR']; foreach ($aAllowedIps as $sIp2Check) { if (strpos($sIP, $sIp2Check) === 0) { return true; } } header('HTTP/1.0 403 Forbidden'); die('ERROR: Your ip address [' . $sIP . '] has no access.'); } public function checkToken($sVarname, $sToken) { if (!isset($_GET)) { return true; } if (isset($_GET[$sVarname]) && $_GET[$sVarname] === $sToken) { return true; } header('HTTP/1.0 403 Forbidden'); die('ERROR: A token is required.'); } public function listChecks() { $oCheck = new appmonitorcheck(); return $oCheck->listChecks(); } protected function _checkData() { $aErrors = array(); if (!count($this->_aChecks)) { $aErrors[] = "No checks have been defined."; } if ($this->_aMeta["result"] === false) { $aErrors[] = "method setResult was not used to set a final result for all checks."; } if (count($aErrors)) { $this->abort( '<h2>Error: client check is not complete</h2><p>Found errors:</p><ol><li>' . implode('<li>', $aErrors) . '</ol><br><br>' ); } return true; } public function abort($sMessage){ header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>'.$sMessage); } public function getResults() { return array( "meta" => $this->_aMeta, "checks" => $this->_aChecks, ); } public function render($bPretty = false, $bHighlight = false) { $this->_checkData(); $this->_aMeta['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms'; if (!defined('JSON_PRETTY_PRINT')) { $bPretty = false; } if (!$bPretty) { $bHighlight = false; $sOut = json_encode($this->getResults()); } else { $sOut = json_encode($this->getResults(), JSON_PRETTY_PRINT); if ($bHighlight) { $aMsg = array( 0 => "OK", 1 => "UNKNOWN", 2 => "WARNING", 3 => "ERROR" ); foreach (array_keys($aMsg) as $iCode) { $sOut = preg_replace('/(\"result\":\ ' . $iCode . ')/', '$1 <span class="result' . $iCode . '"> &lt;--- ' . $aMsg[$iCode] . ' </span>', $sOut); } $sOut = preg_replace('/:\ \"(.*)\"/U', ': "<span style="color:#66e;">$1</span>"', $sOut); $sOut = preg_replace('/:\ ([0-9]*)/', ': <span style="color:#3a3; font-weight: bold;">$1</span>', $sOut); $sOut = preg_replace('/\"(.*)\":/U', '"<span style="color:#840;">$1</span>":', $sOut); $sOut = preg_replace('/([{\[])/', '$1<blockquote>', $sOut); $sOut = preg_replace('/([}\]])/', '</blockquote>$1', $sOut); $sOut = str_replace('    ', '', $sOut); $sOut = '<!DOCTYPE html><html><head>' . '<style>' . 'body{background:#e0e8f8; color:#235; font-family: verdana,arial;}' . 'blockquote{background:rgba(0,0,0,0.03); border-left: 0px solid rgba(0,0,0,0.06); margin: 0 0 0 3em; padding: 0; border-radius: 1em; border-top-left-radius: 0;}' . 'blockquote blockquote:hover{; }' . 'blockquote blockquote blockquote:hover{border-color: #808;}' . 'pre{background:rgba(0,0,0,0.05); padding: 1em; border-radius: 1em;}' . '.result0{background:#aca; border-right: 0em solid #080;}' . '.result1{background:#666; border-right: 0em solid #ccc;}' . '.result2{background:#fc9; border-right: 0em solid #860;}' . '.result3{background:#800; border-right: 0em solid #f00;}' . '</style>' . '<title>' . __CLASS__ . '</title>' . '</head><body>' . '<h1>' . __CLASS__ . ' :: debug</h1>' . '<pre>' . $sOut . '</pre></body></html>'; } } if (!$bHighlight) { header('Content-type: application/json'); header('Cache-Control: cache'); header('max-age: ' . $this->_aMeta["ttl"]); } echo $sOut; return $sOut; } public function renderHtmloutput($sJson) { header('Content-type: text/html'); header('Cache-Control: cache'); header('max-age: ' . $this->_aMeta["ttl"]); $aMsg = array( 0 => "OK", 1 => "UNKNOWN", 2 => "WARNING", 3 => "ERROR" ); $aData= json_decode($sJson, 1); $sOut=''; $sOut.='<h2>Metadata</h2>' . '<div class="meta'.(isset($aData['meta']['result']) ? ' result'.$aData['meta']['result'] : '' ) .'">' . 'Status: ' . (isset($aData['meta']['result']) ? $aMsg[$aData['meta']['result']] : '?').'<br>' . '</div>' . 'Host: ' . (isset($aData['meta']['host']) ? '<span class="string">' . $aData['meta']['host'] .'</span>' : '?').'<br>' . 'Website: ' . (isset($aData['meta']['website']) ? '<span class="string">' . $aData['meta']['website'].'</span>' : '?').'<br>' . 'Execution time: ' . (isset($aData['meta']['time']) ? '<span class="float">' . $aData['meta']['time'] .'</span>' : '?').'<br>' .'<h2>Checks</h2>' ; if (isset($aData['checks'][0]) && count($aData['checks'])){ foreach($aData['checks'] as $aCheck){ $sOut.= '<span class="result'.$aCheck['result'].'"> <strong>'.$aCheck['name'].'</strong></span> <br>' . $aCheck['description'].'<br>' . $aCheck['value'].'<br>' . 'Execution time: ' . $aCheck['time'].'<br>' . 'Status: ' . $aMsg[$aCheck['result']].'<br>' . '<br>' ; } } $sOut.= '<hr>List of farbcodes: '; foreach ($aMsg as $i=>$sText){ $sOut.= '<span class="result'.$i.'">'. $sText.'</span> '; } $sOut = '<!DOCTYPE html><html><head>' . '<style>' . 'body{background:#fff; color:#444; font-family: verdana,arial; margin: 3em;}' . '.result0{background:#aca; border-left: 1em solid #080; padding: 0 0.5em; }' . '.result1{background:#ccc; border-left: 1em solid #aaa; padding: 0 0.5em; }' . '.result2{background:#fc9; border-left: 1em solid #860; padding: 0 0.5em; }' . '.result3{background:#f88; border-left: 1em solid #f00; padding: 0 0.5em; }' . '</style>' . '<title>' . __CLASS__ . '</title>' . '</head><body>' . '<h1>' . __CLASS__ . ' :: client status</h1>' . $sOut . '</body></html>'; echo $sOut; return $sOut; } } 
\ No newline at end of file
+ class checkApacheProcesses extends appmonitorcheck { protected string $_sServerStatusUrl = 'http://localhost/server-status'; protected float $_iWarn = 50; protected float $_iError = 75; public function explain(): array { return [ 'name' => 'Plugin ApacheProcesses', 'descriptionm' => 'Check count running Apache processes', 'parameters' => [ 'url' => [ 'type' => 'string', 'required' => false, 'decsription' => 'Override https server-status page; default is http://localhost/server-status; Use it if the protocol to localhost is not http, but https or if it requires an authentication', 'default' => $this->_sServerStatusUrl, 'example' => '', ], 'warning' => [ 'type' => 'float', 'required' => false, 'decsription' => 'Limit to switch to warning (in percent)', 'default' => $this->_iWarn, 'example' => 30, ], 'error' => [ 'type' => 'float', 'required' => false, 'decsription' => 'Limit to switch to critical (in percent)', 'default' => $this->_iError, 'example' => 50, ], ], ]; } protected function _getApacheProcesses(): bool|array { $sBody = file_get_contents($this->_sServerStatusUrl); if (!$sBody) { return false; } $sRegexScoreboard = '/<pre>(.*)\<\/pre\>/U'; $aScore = []; $sStatusNobr = str_replace("\n", "", $sBody); if (preg_match_all($sRegexScoreboard, $sStatusNobr, $aTmpTable)) { $sScoreString = $aTmpTable[1][0]; $aScore['total'] = strlen($sScoreString); $aScore['free'] = substr_count($sScoreString, '.'); $aScore['waiting'] = substr_count($sScoreString, '_'); $aScore['active'] = $aScore['total'] - $aScore['free'] - $aScore['waiting']; } return $aScore; } public function getGroup(): string { return 'monitor'; } public function run(array $aParams): array { if (isset($aParams['url']) && $aParams['url']) { $this->_sServerStatusUrl = $aParams['url']; } if (isset($aParams['warning']) && (int) $aParams['warning']) { $this->_iWarn = (int) $aParams['warning']; } if (isset($aParams['error']) && (int) $aParams['error']) { $this->_iError = (int) $aParams['error']; } $aProcesses = $this->_getApacheProcesses(); $iActive = $aProcesses ? $aProcesses['active'] : false; if ($iActive === false) { $iResult = RESULT_UNKNOWN; } else { $sComment = ''; $iTotal = $aProcesses['total']; $iResult = RESULT_OK; if (($iActive / $iTotal * 100) > $this->_iWarn) { $iResult = RESULT_WARNING; $sComment = "more than warning level $this->_iWarn %"; } else { $sComment = "less than warning level $this->_iWarn %"; } if (($iActive / $iTotal * 100) > $this->_iError) { $iResult = RESULT_ERROR; $sComment = "more than error level $this->_iError %"; } } return [ $iResult, ($iActive === false ? 'Apache httpd server status is not available' : 'apache processes: ' . print_r($aProcesses, 1)) . ' ' . $sComment, ($iActive === false ? [] : [ 'type' => 'counter', 'count' => $iActive, 'visual' => 'line', ] ) ]; } } 
+ class checkCert extends appmonitorcheck { public function getGroup(): string { return 'security'; } public function run(array $aParams): array { $sUrl = $aParams["url"] ?? 'http' . ($_SERVER['HTTPS'] ? 's' : '') . '://' . $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT']; $bVerify = isset($aParams["verify"]) ? !!$aParams["verify"] : true; $iWarn = isset($aParams["warning"]) ? (int) ($aParams["warning"]) : 21; $iCrtitcal = isset($aParams["critical"]) ? (int) ($aParams["critical"]) : 5; $sMessage = "Checked url: $sUrl ... "; $certinfo = $this->_certGetInfos($sUrl, $bVerify); if (isset($certinfo['_error'])) { return [ RESULT_ERROR, $certinfo['_error'] . $sMessage ]; } $sDNS = $certinfo['extensions']['subjectAltName'] ?? false; $sHost = parse_url($sUrl, PHP_URL_HOST); if (strstr($sDNS, "DNS:$sHost") === false) { return [ RESULT_ERROR, "Wrong certificate: $sHost is not listed as DNS alias in [$sDNS]. $sMessage" ]; } $iDaysleft = round(($certinfo['validTo_time_t'] - date('U')) / 60 / 60 / 24); $sMessage .= 'Issuer: ' . $certinfo['issuer']['O'] . '; valid from: ' . date("Y-m-d H:i", $certinfo['validFrom_time_t']) . ' to ' . date("Y-m-d H:i", $certinfo['validTo_time_t']) . ' ' . ($iDaysleft ? "($iDaysleft days left)" : "expired since " . (-$iDaysleft) . " days.") ; if ($iDaysleft <= 0) { return [ RESULT_ERROR, 'Expired! ' . $sMessage ]; } if ($iDaysleft <= $iWarn) { return [ RESULT_WARNING, ($iDaysleft <= $iCrtitcal ? 'Expires very soon! ' : 'Expires soon. ' ) . $sMessage ]; } return [ RESULT_OK, 'OK. ' . ($bVerify ? 'Certificate is valid. ' : '(Verification is disabled; Check for expiration only.) ') . $sMessage ]; } } 
+ class checkDiskfree extends appmonitorcheck { public function getGroup(): string { return 'disk'; } public function run(array $aParams): array { $this->_checkArrayKeys($aParams, "directory,critical"); $sDirectory = $aParams["directory"]; if (!is_dir($sDirectory)) { return [ RESULT_ERROR, "directory [$sDirectory] does not exist. Maybe it is wrong or is not mounted." ]; } $iWarn = isset($aParams["warning"]) ? $this->_getSize($aParams["warning"]) : false; $iCritical = $this->_getSize($aParams["critical"]); $iSpaceLeft = disk_free_space($sDirectory); $sMessage = '[' . $sDirectory . '] has ' . $this->_getHrSize($iSpaceLeft) . ' left.'; if ($iWarn) { if ($iWarn <= $iCritical) { header('HTTP/1.0 503 Service Unavailable'); die("ERROR in a Diskfree check - warning value must be larger than critical.<pre>" . print_r($aParams, true)); } if ($iWarn < $iSpaceLeft) { return [ RESULT_OK, "$sMessage Warning level is not reached yet (still " . $this->_getHrSize($iSpaceLeft - $iWarn) . "over warning limit)." ]; } if ($iWarn > $iSpaceLeft && $iCritical < $iSpaceLeft) { return [ RESULT_WARNING, $sMessage . ' Warning level ' . $this->_getHrSize($iWarn) . ' was reached (space is ' . $this->_getHrSize($iWarn - $iSpaceLeft) . ' below warning limit; still ' . $this->_getHrSize($iSpaceLeft - $iCritical) . ' over critical limit).' ]; } } if ($iCritical < $iSpaceLeft) { return [RESULT_OK, $sMessage . ' Minimum is not reached yet (still ' . $this->_getHrSize($iSpaceLeft - $iCritical) . ' over critical limit).']; } else { return [RESULT_ERROR, $sMessage]; } } } 
+ class checkExec extends appmonitorcheck { public function getGroup() { return 'service'; } public function run(array $aParams): array { $this->_checkArrayKeys($aParams, "command"); $_sCmd = $aParams['command']; $_bShowOutput = isset($aParams['output']) ? !!$aParams['output'] : true; $_aRcOK = isset($aParams['exitOK']) ? $aParams['exitOK'] : []; $_aRcWarning = isset($aParams['exitWarn']) ? $aParams['exitWarn'] : []; $_aRcCritical = isset($aParams['exitCritical']) ? $aParams['exitCritical'] : []; $_sMode = 'default'; if (count($_aRcOK) + count($_aRcWarning) + count($_aRcCritical)) { $_sMode = 'exitcode'; } exec($_sCmd, $aOutput, $iRc); $_sOut = $_bShowOutput ? '<br>' . implode("<br>", $aOutput) : ''; switch ($_sMode) { case "default": if ($iRc) { return [ RESULT_ERROR, 'command failed with exitcode ' . $iRc . ': [' . $_sCmd . ']' . $_sOut ]; } else { return [ RESULT_OK, "OK [$_sCmd] $_sOut" ]; } ; ; case "exitcode": if (in_array($iRc, $_aRcCritical)) { return [ RESULT_ERROR, "Critical exitcode $iRc detected: [$_sCmd] $_sOut" ]; } if (in_array($iRc, $_aRcWarning)) { return [ RESULT_WARNING, "Warning exitcode $iRc detected: [$_sCmd] $_sOut" ]; } if ($iRc == 0 || in_array($iRc, $_aRcOK)) { return [ RESULT_OK, "OK exitcode $iRc detected: [$_sCmd] $_sOut" ]; } return [ RESULT_UNKNOWN, "UNKNOWN - unhandled exitcode $iRc detected: [$_sCmd] $_sOut" ]; case "search": return [ RESULT_UNKNOWN, "UNKNOWN method [$_sMode] - is not implemented yet." ]; ; default: return [ RESULT_UNKNOWN, 'UNKNOWN mode [' . htmlentities($_sMode) . '].' ]; } } } 
+ class checkFile extends appmonitorcheck { public function getGroup(array $aParams = []): string { $sReturn = 'file'; if (isset($aParams['dir'])) { $sReturn = 'folder'; } foreach (['exists', 'executable', 'readable', 'writable'] as $sFlag) { if (isset($aParams[$sFlag]) && !$aParams[$sFlag]) { $sReturn = 'deny'; } } return $sReturn; } public function run(array $aParams): array { $aOK = []; $aErrors = []; $this->_checkArrayKeys($aParams, "filename"); $sFile = $aParams["filename"]; if (isset($aParams['exists'])) { $sMyflag = 'exists=' . ($aParams['exists'] ? 'yes' : 'no'); if (file_exists($sFile) && $aParams['exists']) { $aOK[] = $sMyflag; } else { $aErrors[] = $sMyflag; } } foreach (['dir', 'executable', 'file', 'link', 'readable', 'writable'] as $sFiletest) { if (isset($aParams[$sFiletest])) { $sTestCmd = 'return is_' . $sFiletest . '("' . $sFile . '");'; if (eval ($sTestCmd) && $aParams[$sFiletest]) { $aOK[] = $sFiletest . '=' . ($aParams[$sFiletest] ? 'yes' : 'no'); } else { $aErrors[] = $sFiletest . '=' . ($aParams[$sFiletest] ? 'yes' : 'no'); } } } $sMessage = (count($aOK) ? ' flags OK: ' . implode('|', $aOK) : '') . ' ' . (count($aErrors) ? ' flags FAILED: ' . implode('|', $aErrors) : '') ; if (count($aErrors)) { return [ RESULT_ERROR, "file test [$sFile] $sMessage" ]; } else { return [ RESULT_OK, "file test [$sFile] $sMessage" ]; } } } 
+ class checkHello extends appmonitorcheck { public function run(array $aParams): array { $this->_checkArrayKeys($aParams, "message"); return [ RESULT_OK, 'Hello world! My message is: ' . $aParams['message'] ]; } } 
+ class checkHttpContent extends appmonitorcheck { public function getGroup(array $aParams=[]): string { $sReturn = 'service'; if (isset($aParams['status']) && $aParams['status'] > 300 && $aParams['status'] < 500) { $sReturn = 'deny'; } return $sReturn; } public function run(array $aParams) { $this->_checkArrayKeys($aParams, "url"); if (!function_exists("curl_init")) { header('HTTP/1.0 503 Service Unavailable'); die("ERROR: PHP CURL module is not installed."); } $bShowContent = (isset($aParams["content"]) && $aParams["content"]) ? true : false; $ch = curl_init($aParams["url"]); curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_NOBODY, isset($aParams["headeronly"]) && $aParams["headeronly"]); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, isset($aParams["follow"]) && $aParams["follow"]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, isset($aParams["sslverify"]) ? !!$aParams["sslverify"] : 1); curl_setopt($ch, CURLOPT_TIMEOUT, (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp); if (isset($aParams["userpwd"])) { curl_setopt($ch, CURLOPT_USERPWD, $aParams["userpwd"]); } $res = curl_exec($ch); if (!$res) { $iErrorCode = curl_errno($ch); $sErrorMsg = curl_error($ch); curl_close($ch); return [ RESULT_ERROR, 'ERROR: failed to fetch ' . $aParams["url"] . ' - curl error #' . $iErrorCode . ': ' . $sErrorMsg ]; } $sOut = ''; $bError = false; $aInfos = curl_getinfo($ch); curl_close($ch); $aTmp = explode("\r\n\r\n", $res, 2); $sHttpHeader = $aTmp[0]; $sHttpBody = $aTmp[1] ?? false; $sOut .= "Http status: " . $aInfos['http_code'] . " - "; if (isset($aParams["status"])) { if ($aInfos['http_code'] === $aParams["status"]) { $sOut .= "compare OK<br>"; } else { $sOut .= "compare failed<br>"; $bError = true; } } else { if ($aInfos['http_code'] >= 400) { $sOut .= "Error page detected<br>"; $bError = true; } else { $sOut .= "request successful<br>"; } } if (isset($aParams["headercontains"]) && $aParams["headercontains"]) { $sOut .= "Http header contains &quot;" . $aParams["headercontains"] . "&quot; - "; if (!strstr($sHttpHeader, $aParams["headercontains"]) === false) { $sOut .= "compare OK<br>"; } else { $sOut .= "compare failed<br>"; $bError = true; } } if (isset($aParams["headernotcontains"]) && $aParams["headernotcontains"]) { $sOut .= "Http header does not contain &quot;" . $aParams["headernotcontains"] . "&quot; - "; if (strstr($sHttpHeader, $aParams["headernotcontains"]) === false) { $sOut .= "compare OK<br>"; } else { $sOut .= "compare failed<br>"; $bError = true; } } if (isset($aParams["headerregex"]) && $aParams["headerregex"]) { $sOut .= "Http header regex test &quot;" . $aParams["headerregex"] . "&quot; - "; try { $bRegex = preg_match($aParams["headerregex"], $sHttpHeader); if ($bRegex) { $sOut .= "compare OK<br>"; } else { $sOut .= "compare failed<br>"; $bError = true; } } catch (Exception $e) { $sOut .= "Wrong REGEX<br>" . print_r($e, 1) . '<br>'; $bError = true; } } if (isset($aParams["bodycontains"]) && $aParams["bodycontains"]) { $sOut .= "Http body contains &quot;" . $aParams["bodycontains"] . "&quot; - "; if (!strstr($sHttpBody, $aParams["bodycontains"]) === false) { $sOut .= "compare OK<br>"; } else { $sOut .= "compare failed<br>"; $bError = true; } } if (isset($aParams["bodynotcontains"]) && $aParams["bodynotcontains"]) { $sOut .= "Http body does not contain &quot;" . $aParams["bodynotcontains"] . "&quot; - "; if (strstr($sHttpBody, $aParams["bodynotcontains"]) === false) { $sOut .= "compare OK<br>"; } else { $sOut .= "compare failed<br>"; $bError = true; } } if (isset($aParams["bodyregex"]) && $aParams["bodyregex"]) { $sOut .= "Http body regex test &quot;" . $aParams["bodyregex"] . "&quot; - "; try { $bRegex = preg_match($aParams["bodyregex"], $sHttpBody); if ($bRegex) { $sOut .= "compare OK<br>"; } else { $sOut .= "compare failed<br>"; $bError = true; } } catch (Exception $e) { $sOut .= "Wrong REGEX<br>" . print_r($e, 1) . '<br>'; $bError = true; } } if (!$bError) { return [ RESULT_OK, 'OK: http check "' . $aParams["url"] . '".<br>' . $sOut ]; } else { return [ RESULT_ERROR, 'ERROR: http check "' . $aParams["url"] . '".<br>' . $sOut ]; } } } 
+ class checkLoadmeter extends appmonitorcheck { public function getGroup(): string { return 'monitor'; } protected function _getLoad(): float { if (function_exists('sys_getloadavg')) { $load = sys_getloadavg(); return $load[0]; } else { if (class_exists('COM')) { $wmi = new COM('WinMgmts:\\\\.'); $cpus = $wmi->InstancesOf('Win32_Processor'); $load = 0; if (version_compare('4.50.0', PHP_VERSION) == 1) { while ($cpu = $cpus->Next()) { $load += $cpu->LoadPercentage; } } else { foreach ($cpus as $cpu) { $load += $cpu->LoadPercentage; } } return $load; } return false; } } public function run(array $aParams): array { $fLoad = $this->_getLoad(); if ($fLoad === false) { $iResult = RESULT_UNKNOWN; } else { $iResult = RESULT_OK; if (isset($aParams['warning']) && $aParams['warning'] && $fLoad > $aParams['warning']) { $iResult = RESULT_WARNING; } if (isset($aParams['error']) && $aParams['error'] && $fLoad > $aParams['error']) { $iResult = RESULT_ERROR; } } return [ $iResult, ($fLoad === false ? 'load value is not available' : 'current load is: ' . $fLoad), ($fLoad === false ? [] : [ 'type' => 'counter', 'count' => $fLoad, 'visual' => 'line', ] ) ] ; } } 
+ class checkMysqlConnect extends appmonitorcheck { public function getGroup(): string { return 'database'; } public function run(array $aParams): array { $this->_checkArrayKeys($aParams, "server,user,password,db"); $mysqli = mysqli_init(); if (!$mysqli) { return [RESULT_ERROR, 'ERROR: mysqli_init failed.']; } if (!$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp)) { return [RESULT_ERROR, 'ERROR: setting mysqli_options failed.']; } $db = (isset($aParams["port"]) && $aParams["port"]) ? $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"], $aParams["port"]) : $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"]) ; if ($db) { $mysqli->close(); return [RESULT_OK, "OK: Mysql database " . $aParams["db"] . " was connected"]; } else { return [ RESULT_ERROR, "ERROR: Mysql database " . $aParams["db"] . " was not connected. Error " . mysqli_connect_errno() . ": " . mysqli_connect_error() ]; } } } 
+ class checkPdoConnect extends appmonitorcheck { public function getGroup(): string { return 'database'; } public function run(array $aParams): array { $this->_checkArrayKeys($aParams, "connect,user,password"); try { $db = new PDO( $aParams['connect'], $aParams['user'], $aParams['password'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp, ] ); $db = null; return [RESULT_OK, "OK: Database was connected with PDO " . $aParams['connect']]; } catch (PDOException $e) { return [RESULT_ERROR, "ERROR: Database was not connected " . $aParams['connect'] . " was not connected. Error " . $e->getMessage()]; } } } 
+ class checkPhpmodules extends appmonitorcheck { public function getGroup(): string { return 'service'; } public function run(array $aParams): array { $sOut = ''; $bHasError = false; $bHasWarning = false; $aAllMods = get_loaded_extensions(false); if (isset($aParams['required']) && count($aParams['required'])) { $sOut .= 'Required: '; foreach ($aParams['required'] as $sMod) { $sOut .= $sMod . '='; if (!array_search($sMod, $aAllMods) === false) { $sOut .= 'OK;'; } else { $bHasError = true; $sOut .= 'MISS;'; } } } if (isset($aParams['optional']) && count($aParams['optional'])) { $sOut .= ($sOut ? '|' : '') . 'Optional: '; foreach ($aParams['optional'] as $sMod) { $sOut .= $sMod . '='; if (!array_search($sMod, $aAllMods) === false) { $sOut .= 'OK;'; } else { $bHasWarning = true; $sOut .= 'MISS;'; } } } if ($bHasError) { return [RESULT_ERROR, "ERROR: " . $sOut]; } if ($bHasWarning) { return [RESULT_WARNING, "WARNING: " . $sOut]; } return [RESULT_OK, "OK: " . $sOut]; } } 
+ class checkPing extends appmonitorcheck { public function getGroup(): string { return 'network'; } public function run(array $aParams): array { $sHost = $aParams['host'] ?? '127.0.0.1'; $sParamCount = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? "n" : "c"; $iRepeat = 1; $sCommand = "ping -$sParamCount $iRepeat $sHost 2>&1"; exec($sCommand, $aOut, $iRc); $sOut = implode("\n", $aOut); if ($iRc > 0) { return [RESULT_ERROR, "ERROR: ping to $sHost failed.\n" . $sOut]; } return [RESULT_OK, "OK: ping to $sHost\n" . $sOut]; } } 
+ class checkPortTcp extends appmonitorcheck { public function getGroup(): string { return 'network'; } public function run(array $aParams): array { $this->_checkArrayKeys($aParams, "port"); $sHost = $aParams['host'] ?? '127.0.0.1'; $iPort = (int) $aParams['port']; if (!function_exists('socket_create')) { return [RESULT_UNKNOWN, "UNKNOWN: Unable to perform tcp test. The socket module is not enabled in the php installation."]; } $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if ($socket === false) { return [RESULT_UNKNOWN, "ERROR: $sHost:$iPort was not checked. socket_create() failed: " . socket_strerror(socket_last_error())]; } socket_set_option( $socket, SOL_SOCKET, SO_SNDTIMEO, [ "sec" => (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp, "usec" => 0 ] ); $result = @socket_connect($socket, $sHost, $iPort); if ($result === false) { $aResult = [RESULT_ERROR, "ERROR: $sHost:$iPort failed. " . socket_strerror(socket_last_error($socket))]; socket_close($socket); return $aResult; } else { socket_close($socket); return [RESULT_OK, "OK: $sHost:$iPort was connected."]; } } } 
+ class checkSimple extends appmonitorcheck { public function run(array $aParams): array { $this->_checkArrayKeys($aParams, "result,value"); $aData = []; foreach (['type', 'count', 'visual'] as $sMyKey) { if (isset($aParams[$sMyKey])) { $aData[$sMyKey] = $aParams[$sMyKey]; } } return [ $aParams["result"], $aParams["value"], count($aData) ? $aData : false ]; } } 
+ class checkSqliteConnect extends appmonitorcheck { public function getGroup() { return 'database'; } public function run($aParams): array { $this->_checkArrayKeys($aParams, "db"); if (!file_exists($aParams["db"])) { return [ RESULT_ERROR, "ERROR: Sqlite database file " . $aParams["db"] . " does not exist." ]; } if (!isset($aParams['user'])) { $aParams['user'] = ''; } if (!isset($aParams['password'])) { $aParams['password'] = ''; } try { $o = new PDO( "sqlite:" . $aParams["db"], $aParams['user'], $aParams['password'], [ PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp, ] ); return [ RESULT_OK, "OK: Sqlite database " . $aParams["db"] . " was connected" ]; } catch (Exception $e) { return [ RESULT_ERROR, "ERROR: Sqlite database " . $aParams["db"] . " was not connected. " . $e->getMessage() ]; } } } 
+ if (!defined('RESULT_OK')) { define("RESULT_OK", 0); define("RESULT_UNKNOWN", 1); define("RESULT_WARNING", 2); define("RESULT_ERROR", 3); } class appmonitorcheck { protected float $_iStart = 0; protected array $_aConfig = []; protected array $_aData = []; protected array $_units = ['B', 'KB', 'MB', 'GB', 'TB']; protected int $_iTimeoutTcp = 5; protected string $_sPluginDir = __DIR__ . '/../plugins'; public function __construct() { } protected function _createDefaultMetadata(): bool { $this->_aData = [ "name" => $this->_aConfig["name"], "description" => $this->_aConfig["description"], "group" => isset($this->_aConfig["group"]) ? $this->_aConfig["group"] : false, "parent" => isset($this->_aConfig["parent"]) ? $this->_aConfig["parent"] : false, "result" => RESULT_UNKNOWN, "value" => false, "type" => false, "time" => false, ]; return true; } protected function _setResult(int $iResult): true { $this->_aData["result"] = (int) $iResult; return true; } protected function _setOutput(string $s): bool { $this->_aData["value"] = $s; return true; } protected function _setCounter(array $aParams): bool { if (is_array($aParams) && count($aParams)) { foreach (['type', 'count', 'visual'] as $sMyKey) { if (isset($aParams[$sMyKey])) { $this->_aData[$sMyKey] = $aParams[$sMyKey]; } } } return true; } protected function _setReturn(int $iResult, string $s, array $aCounter = []) { $this->_setResult($iResult); $this->_setOutput($s); $this->_setCounter($aCounter); return true; } protected function _checkArrayKeys($aConfig, $sKeyList) { foreach (explode(",", $sKeyList) as $sKey) { if (!isset($aConfig[$sKey])) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' . __METHOD__ . " - array of check parameters requires the keys [$sKeyList] - but key <code>$sKey</code> was not found in config array." . "<pre>" . print_r($aConfig, true) . '</pre>' ); } if (is_null($aConfig[$sKey])) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' . __METHOD__ . " - key <code>$sKey</code> is empty in config array" . "<pre>" . print_r($aConfig, true) . '</pre>' ); } } return true; } public function makeCheck(array $aConfig): array { $this->_iStart = microtime(true); $this->_checkArrayKeys($aConfig, "name,description,check"); $this->_checkArrayKeys($aConfig["check"], "function"); $this->_aConfig = $aConfig; $this->_createDefaultMetadata(); $sCheck = preg_replace('/[^a-zA-Z0-9]/', '', $this->_aConfig["check"]["function"]); $aParams = $this->_aConfig["check"]["params"] ?? []; $sPluginFile = strtolower($this->_sPluginDir . '/checks/' . $sCheck . '.php'); $sCheckClass = 'check' . $sCheck; if (!class_exists($sCheckClass)) { if (file_exists($sPluginFile)) { } } if (!class_exists($sCheckClass)) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' . __METHOD__ . " - check class not found: <code>$sCheckClass</code>" . "<pre>" . print_r($aConfig, true) . '</pre>' . "<h2>Known checks</h2>\n" . print_r($this->listChecks(), 1) ); } $oPlugin = new $sCheckClass; $aResponse = $oPlugin->run($aParams); if (!is_array($aResponse)) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' . __METHOD__ . " - plugin : $sCheck does not responses an array" . "<pre>INPUT " . print_r($aConfig, true) . '</pre>' . "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>' ); } if (count($aResponse) < 2) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' . __METHOD__ . " - plugin : $sCheck does not responses the minimum of 2 array values" . "<pre>INPUT " . print_r($aConfig, true) . '</pre>' . "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>' ); } if (!isset($aResponse[2]) || !$aResponse[2]) { $aResponse[2] = []; } $this->_setReturn($aResponse[0], $aResponse[1], $aResponse[2]); if (!$this->_aData['group'] && method_exists($oPlugin, "getGroup")) { $this->_aData['group'] = $oPlugin->getGroup($aParams); } $this->_aData['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms'; return $this->respond(); } public function listChecks(): array { $aReturn = []; $class = new ReflectionClass($this); foreach ($class->getMethods(ReflectionMethod::IS_PROTECTED) as $oReflectionMethod) { if (strpos($oReflectionMethod->name, "check") === 0) { $aReturn[(string) $oReflectionMethod->name] = 1; } } foreach (glob($this->_sPluginDir . '/checks/*.php') as $sPluginFile) { $aReturn[str_replace('.php', '', basename($sPluginFile))] = 1; } ksort($aReturn); return array_keys($aReturn); } public function respond() { return $this->_aData; } protected function _certGetInfos(string $sUrl, bool $bVerifyCert): array { $iTimeout = 10; $aUrldata = parse_url($sUrl); $sHost = isset($aUrldata['host']) ? $aUrldata['host'] : false; $iPort = isset($aUrldata['port']) ? $aUrldata['port'] : ((isset($aUrldata['scheme']) && $aUrldata['scheme'] === 'https') ? 443 : false); $aSsl = ['capture_peer_cert' => true]; if ($bVerifyCert) { $aSsl['verify_peer'] = false; $aSsl['verify_peer_name'] = false; } ; $get = stream_context_create(['ssl' => $aSsl]); if (!$get) { return ['_error' => 'Error: Cannot create stream_context']; } $errno = -1; $errstr = "stream_socket_client failed."; $read = stream_socket_client("ssl://$sHost:$iPort", $errno, $errstr, $iTimeout, STREAM_CLIENT_CONNECT, $get); if (!$read) { return ['_error' => "Error $errno: $errstr; cannot create stream_socket_client with given stream_context to ssl://$sHost:$iPort; you can try to set the flag [verify] to false to check expiration date only."]; } $cert = stream_context_get_params($read); if (!$cert) { return ['_error' => "Error: socket was connected to ssl://$sHost:$iPort - but I cannot read certificate infos with stream_context_get_params "]; } return openssl_x509_parse($cert['options']['ssl']['peer_certificate']); } protected function _getHrSize(int $size): string { $power = $size > 0 ? floor(log($size, 1024)) : 0; return number_format($size / pow(1024, $power), 2, '.', ',') . ' ' . $this->_units[$power]; } protected function _getSize(string $sValue): int { if (is_int($sValue)) { return $sValue; } $power = 0; foreach ($this->_units as $sUnit) { if (preg_match('/^[0-9\.\ ]*' . $sUnit . '/', $sValue)) { $i = preg_replace('/([0-9\.]*).*/', '$1', $sValue); $iReal = $i * pow(1024, $power); return $iReal; } $power++; } header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' . __METHOD__ . " ERROR in space value parameter - there is no size unit in [$sValue] - allowed size units are " . implode('|', $this->_units) ); } } 
+if (!class_exists('appmonitorcheck')) { } class appmonitor { protected string $_sVersion = 'php-client-v0.137'; protected int $_iDefaultTtl = 300; protected int $_iMaxResult = -1; protected array $_aMeta = []; protected array $_aChecks = []; protected float $_iStart = 0; public function __construct() { $this->_createDefaultMetadata(); } protected function _createDefaultMetadata(): bool { $this->_iStart = microtime(true); $this->_aMeta = [ "host" => false, "website" => false, "ttl" => false, "result" => false, "time" => false, "version" => $this->_sVersion, ]; $this->setHost(); $this->setWebsite(); $this->setTTL(); return true; } public function setHost(string $s = ''): bool { if (!$s) { $s = php_uname("n"); } if (!$s) { return false; } $this->_aMeta["host"] = $s; return true; } public function setWebsite($sWebsite = ''): bool { if (!$sWebsite && isset($_SERVER["HTTP_HOST"])) { $sWebsite = $_SERVER["HTTP_HOST"]; } if (!$sWebsite) { return false; } $this->_aMeta["website"] = $sWebsite; return true; } public function setTTL($iTTl = 0) { if ($iTTl == 0) { $iTTl = $this->_iDefaultTtl; } return $this->_aMeta["ttl"] = $iTTl; } public function setResult(int $iResult = -1): bool { if ($iResult === -1) { $iResult = $this->_iMaxResult; } $this->_aMeta["result"] = $iResult; return true; } public function addCheck($aJob = []): bool { $oCheck = new appmonitorcheck(); $aCheck = $oCheck->makecheck($aJob); $iMyResult = isset($aJob['worstresult']) ? min($aCheck["result"], $aJob['worstresult']) : $aCheck["result"] ; if (!$this->_iMaxResult || $iMyResult > $this->_iMaxResult) { $this->_iMaxResult = $iMyResult; } $this->_aChecks[] = $aCheck; return true; } protected function _addNotification(string $sType, string $sValue, string $sKey = ''): bool { $sTypeCleaned = preg_replace('/[^a-z]/', '', strtolower($sType)); if (!isset($this->_aMeta['notifications'])) { $this->_aMeta['notifications'] = []; } if (!isset($this->_aMeta['notifications'][$sTypeCleaned])) { $this->_aMeta['notifications'][$sTypeCleaned] = []; } if ($sKey) { $this->_aMeta['notifications'][$sTypeCleaned][$sKey] = $sValue; } else { $this->_aMeta['notifications'][$sTypeCleaned][] = $sValue; } return true; } public function addEmail(string $sEmailAddress) { return $this->_addNotification('email', $sEmailAddress); } public function addSlackWebhook(string $sLabel, string $sSlackWebhookUrl): bool { return $this->_addNotification('slack', $sSlackWebhookUrl, $sLabel); } public function addTag(string $sTag): bool { if (!isset($this->_aMeta['tags'])) { $this->_aMeta['tags'] = []; } $this->_aMeta['tags'][] = str_replace(' ', '_', $sTag); return true; } public function checkIp(array $aAllowedIps = []): bool { if (!isset($_SERVER['REMOTE_ADDR']) || !count($aAllowedIps)) { return true; } $sIP = $_SERVER['REMOTE_ADDR']; foreach ($aAllowedIps as $sIp2Check) { if (strpos($sIP, $sIp2Check) === 0) { return true; } } header('HTTP/1.0 403 Forbidden'); die('ERROR: Your ip address [' . $sIP . '] has no access.'); } public function checkToken(string $sVarname, string $sToken): bool { if (!isset($_GET)) { return true; } if (isset($_GET[$sVarname]) && $_GET[$sVarname] === $sToken) { return true; } header('HTTP/1.0 403 Forbidden'); die('ERROR: A token is required.'); } public function listChecks(): array { $oCheck = new appmonitorcheck(); return $oCheck->listChecks(); } protected function _checkData(): bool { $aErrors = []; if (!count($this->_aChecks)) { $aErrors[] = "No checks have been defined."; } if ($this->_aMeta["result"] === false) { $aErrors[] = "method setResult was not used to set a final result for all checks."; } if (count($aErrors)) { $this->abort( '<h2>Error: client check is not complete</h2><p>Found errors:</p><ol><li>' . implode('<li>', $aErrors) . '</ol><br><br>' ); } return true; } public function abort(string $sMessage): void { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . $sMessage); } public function getResults(): array { return [ "meta" => $this->_aMeta, "checks" => $this->_aChecks, ]; } public function render(): string { $this->_checkData(); $this->_aMeta['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms'; $sOut=json_encode($this->getResults()); header('Content-type: application/json'); header('Cache-Control: cache'); header('max-age: ' . $this->_aMeta["ttl"]); echo $sOut; return $sOut; } public function renderHtmloutput(string $sJson): string { header('Content-type: text/html'); header('Cache-Control: cache'); header('max-age: ' . $this->_aMeta["ttl"]); $aMsg = [ 0 => "OK", 1 => "UNKNOWN", 2 => "WARNING", 3 => "ERROR" ]; $aData = json_decode($sJson, 1); $sOut = ''; $sOut .= '' . '<h2>Metadata</h2>' . '<div class="meta' . (isset($aData['meta']['result']) ? ' result' . $aData['meta']['result'] : '') . '">' . 'Status: ' . (isset($aData['meta']['result']) ? $aMsg[$aData['meta']['result']] : '?') . '<br>' . '</div>' . 'Host: ' . (isset($aData['meta']['host']) ? '<span class="string">' . $aData['meta']['host'] . '</span>' : '?') . '<br>' . 'Website: ' . (isset($aData['meta']['website']) ? '<span class="string">' . $aData['meta']['website'] . '</span>' : '?') . '<br>' . 'Execution time: ' . (isset($aData['meta']['time']) ? '<span class="float">' . $aData['meta']['time'] . '</span>' : '?') . '<br>' . 'Client: ' . (isset($aData['meta']['version']) ? '<span class="string">' . $aData['meta']['version'] . '</span>' : '?') . '<br>' . '<h2>Checks</h2>' ; if (isset($aData['checks'][0]) && count($aData['checks'])) { foreach ($aData['checks'] as $aCheck) { $sOut .= '' . '<span class="result' . $aCheck['result'] . '"> <strong>' . $aCheck['name'] . '</strong></span> <br>' . '<div class="check">' . '<div class="description">' . $aCheck['description'] . '<br>' . $aCheck['value'] . '<br>' . '</div>' . 'Execution time: <span class="float">' . (isset($aCheck['time']) ? $aCheck['time'] : ' - ') . '</span><br>' . 'Group: <span class="string">' . (isset($aCheck['group']) ? $aCheck['group'] : '-') . '</span><br>' . 'parent: <span class="string">' . (isset($aCheck['parent']) ? $aCheck['parent'] : '-') . '</span><br>' . 'Status: ' . $aMsg[$aCheck['result']] . '<br>' . '</div>' ; } } $sOut .= '<h2>List of farbcodes</h2>'; foreach ($aMsg as $i => $sText) { $sOut .= '<span class="result' . $i . '">' . $sText . '</span> '; } $sRaw=json_encode($aData, JSON_PRETTY_PRINT); $sRaw = preg_replace('/:\ \"(.*)\"/U', ': "<span class="string">$1</span>"', $sRaw); $sRaw = preg_replace('/:\ ([0-9]*)/', ': <span class="int">$1</span>', $sRaw); $sRaw = preg_replace('/\"(.*)\":/U', '"<span class="key">$1</span>":', $sRaw); $sOut .= '<h2>Raw result data</h2><pre id="raw">' . $sRaw . '</pre>'; $sOut = '<!DOCTYPE html><html><head>' . '<style>' . 'body{background:#eee; color:#444; font-family: verdana,arial; margin: 0; }' . 'body>div#content{background: #fff; border-radius: 2em; border: 4px solid #abc; box-shadow: 0.5em 0.5em 2em #aaa; margin: 2em 10%; padding: 2em;}' . 'h1{color:#346; margin: 0;}' . 'h2{color:#569; margin-top: 2em;}' . 'pre{background:#f4f4f8; padding: 1em; overflow-x:auto; }' . '#raw .key{color:#808;}' . '#raw .int{color:#3a3; font-weight: bold;}' . '#raw .string{color:#66e;}' . '.check{border: 1px solid #ccc; padding: 0.4em; margin-bottom: 2em;}' . '.description{font-style: italic; padding: 0.4em 1em;}' . '.float{color:#080;}' . '.meta{margin-bottom: 1em;}' . '.result0{background:#aca; border-left: 1em solid #080; padding: 0.5em; }' . '.result1{background:#ccc; border-left: 1em solid #aaa; padding: 0.5em; }' . '.result2{background:#fc9; border-left: 1em solid #860; padding: 0.5em; }' . '.result3{background:#f88; border-left: 1em solid #f00; padding: 0.5em; }' . '.string{color:#338;}' . '</style>' . '<title>' . __CLASS__ . '</title>' . '</head><body>' . '<div id="content">' . '<h1>' . __CLASS__ . ' :: client status</h1>' . $sOut . '</div>' . '</body></html>'; return $sOut; } } 
\ No newline at end of file
diff --git a/public_html/appmonitor/git_update_appmonitor.sh b/public_html/appmonitor/git_update_appmonitor.sh
index fdc069e9..32f5b785 100755
--- a/public_html/appmonitor/git_update_appmonitor.sh
+++ b/public_html/appmonitor/git_update_appmonitor.sh
@@ -1,14 +1,21 @@
 #!/bin/bash
 # ======================================================================
 #
-# UPDATE APPMONITOR CLIENT
+#   A P P M O N I T O R  ::  CLIENT - UPDATE
 #
-# requires git, rsync
+# This script will install or update the appmonitor client only.
+#
+# Below the document root of a website create a new directory, 
+# i.e. [webroot]/appmonitor/ and copy this script there.
+# Change the directory "cd [webroot]/appmonitor/" and execute it.
 #
 # ----------------------------------------------------------------------
-# 2022-04-11  <axel.hahn@iml.unibe.ch>  first lines
-# 2022-04-12  <axel.hahn@iml.unibe.ch>  add help; exclude unneeded files
-# 2022-05-03  <axel.hahn@iml.unibe.ch>  create general_include.php
+# requires git, rsync
+# ----------------------------------------------------------------------
+# 2022-04-11  0.1  <axel.hahn@iml.unibe.ch>  first lines
+# 2022-04-12  0.2  <axel.hahn@iml.unibe.ch>  add help; exclude unneeded files
+# 2022-05-03  0.3  <axel.hahn@iml.unibe.ch>  create general_include.php
+# 2024-07-25  0.4  <axel.hahn@iml.unibe.ch>  update quoting and comments
 # ======================================================================
 
 # ----------------------------------------------------------------------
@@ -16,14 +23,14 @@
 # ----------------------------------------------------------------------
 
 readonly git_repo_url="https://github.com/iml-it/appmonitor.git"
-readonly line="____________________________________________________________"
-readonly version="0.3"
+readonly line="______________________________________________________________________________"
+readonly version="0.4"
 
 git_target=/tmp/git_data__appmonitor
 client_from="${git_target}/public_html/client"
 client_to="."
 
-cd $( dirname "$0" ) || exit 1
+cd "$( dirname "$0" )" || exit 1
 
 # ----------------------------------------------------------------------
 # FUNCTIONS
@@ -99,6 +106,9 @@ case "$1" in
     This is a helper script to get the files of the IML Appmonitor
     client part only.
 
+    Below the document root of a website create a new directory, 
+    i.e. [webroot]/appmonitor/ and copy this script there.
+
     This script clones and updates the repository in the /tmp 
     directory and syncs the client files of it to a given directory.
 
@@ -157,6 +167,7 @@ rsync -rav \
     --exclude "*.sample.*" \
     --exclude "example.json" \
     --exclude "check-appmonitor-server.php" \
+    --exclude "local.php" \
     $client_from/* "$client_to"
 echo
 
@@ -165,7 +176,7 @@ _fileupdate general_include.sample.php
 echo $line
 echo ">>> #3 of 3 >>> Diff"
 echo
-diff -r "$client_from" "$client_to"
+diff --color -r "$client_from" "$client_to"
 echo
 
 
diff --git a/public_html/appmonitor/local.php b/public_html/appmonitor/local.php
new file mode 100644
index 00000000..8fcc94e1
--- /dev/null
+++ b/public_html/appmonitor/local.php
@@ -0,0 +1,6 @@
+<?php
+
+require __DIR__ . '/check-appmonitor-server.php';
+$sJson=ob_get_contents();
+ob_end_clean();
+echo $oMonitor->renderHtmloutput($sJson);
diff --git a/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php b/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php
index d85938a9..0c468221 100755
--- a/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php
+++ b/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php
@@ -12,6 +12,7 @@
  * 2019-05-17  aded check http to config- and tmp dir
  * 2021-11-nn  removed all checks ... created as single files
  * 2022-03-28  move checks into plugins/apps/
+ * 2024-07-23  php 8: short array syntax
  */
 
 // ----------------------------------------------------------------------
@@ -19,65 +20,65 @@
 // ----------------------------------------------------------------------
 
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "write to ./tmp/",
         "description" => "Check cache storage",
         // "group" => "folder",
-        "check" => array(
+        "check" => [
             "function" => "File",
-            "params" => array(
-                "filename" => $sApproot . "/server/tmp",
+            "params" => [
+                "filename" => "$sApproot/server/tmp",
                 "dir" => true,
                 "writable" => true,
-            ),
-        ),
-    )
+            ],
+        ],
+    ]
 );
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "write to ./config/",
         "description" => "Check config target directory",
         // "group" => "folder",
-        "check" => array(
+        "check" => [
             "function" => "File",
-            "params" => array(
-                "filename" => $sApproot . "/server/config",
+            "params" => [
+                "filename" => "$sApproot/server/config",
                 "dir" => true,
                 "writable" => true,
-            ),
-        ),
-    )
+            ],
+        ],
+    ]
 );
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "check config file",
         "description" => "The config file must be writable",
         "parent" => "write to ./config/",
         // "group" => "file",
-        "check" => array(
+        "check" => [
             "function" => "File",
-            "params" => array(
-                "filename" => $sApproot . "/server/config/appmonitor-server-config.json",
+            "params" => [
+                "filename" => "$sApproot/server/config/appmonitor-server-config.json",
                 "file" => true,
                 "writable" => true,
-            ),
-        ),
-    )
+            ],
+        ],
+    ]
 );
 
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "PHP modules",
         "description" => "Check needed PHP modules",
         // "group" => "folder",
-        "check" => array(
+        "check" => [
             "function" => "Phpmodules",
-            "params" => array(
+            "params" => [
                 "required" => ["curl"],
                 "optional" => [],
-            ),
-        ),
-    )
+            ],
+        ],
+    ]
 );
 
 // ----------------------------------------------------------------------
@@ -89,21 +90,21 @@ $sBaseUrl = 'http'.(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] ? 's' : '')
         .'://'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT']
         .dirname(dirname($_SERVER['REQUEST_URI']));
 
-foreach(array('server/config', 'server/tmp') as $sMyDir){
+foreach(['server/config', 'server/tmp'] as $sMyDir){
     $oMonitor->addCheck(
-        array(
+        [
             "name" => "http to $sMyDir",
             "description" => "Check if the $sMyDir directory is not accessible (counts as warning on fail)",
             "group" => "deny",
-            "check" => array(
+            "check" => [
                 "function" => "HttpContent",
-                "params" => array(
-                    "url" => $sBaseUrl . "/$sMyDir/readme.md",
+                "params" => [
+                    "url" => "$sBaseUrl/$sMyDir/readme.md",
                     "status" => 403,
-                ),
-            ),
+                ],
+            ],
             "worstresult" => RESULT_WARNING
-        )
+        ]
     );
 }
 
@@ -114,48 +115,48 @@ require_once($sApproot.'/server/classes/appmonitor-server.class.php');
 $oServer=new appmonitorserver();
 $iCount=count($oServer->getAppIds());
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "appcounter",
         "description" => "Monitored apps",
         "group" => "monitor",
         "parent" => "check config file",
-        "check" => array(
+        "check" => [
             "function" => "Simple",
-            "params" => array(
+            "params" => [
                 "result" => RESULT_OK,
                 "value" => "Found monitored web apps: $iCount",
                 "count" => $iCount,
                 "visual" => "simple",
-            ),
-        ),
-    )
+            ],
+        ],
+    ]
 );
 // ----------------------------------------------------------------------
 // check running service
 // ----------------------------------------------------------------------
 require_once($sApproot.'/server/classes/tinyservice.class.php');
 ob_start();
-$oService = new tinyservice($sApproot.'/server/service.php', 15, $sApproot.'/server/tmp');
+$oService = new tinyservice("$sApproot/server/service.php", 15, "$sApproot/server/tmp");
 $sIsStopped=$oService->canStart();
 $out=ob_get_contents();
 ob_clean();
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "running service",
         "description" => "Check if the service is running",
         "group" => "service",
-        "check" => array(
+        "check" => [
             "function" => "Simple",
-            "params" => array(
+            "params" => [
                 "result" => ($sIsStopped ? RESULT_WARNING : RESULT_OK),
                 "value" => ($sIsStopped 
                     ? "Info: Service is NOT running. Apps are checked interactively only (if the appmonitor web ui is running). | Output: $out" 
                     : "OK, service is running. | Output: $out"
-                ),
-            ),
-        ),
+                )
+            ],
+        ],
         "worstresult" => RESULT_OK        
-    )
+    ]
 );
 // ----------------------------------------------------------------------
 // check certificate if https is used
@@ -170,42 +171,42 @@ include 'shared_check_ssl.php';
  * AS A DEMO: using a custom plugin:
  * 
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "plugin test",
         "description" => "minimal test of the plugin plugins/checkHello.php",
-        "check" => array(
+        "check" => [
             "function" => "Hello",
-            "params" => array(
+            "params" => []
                 "message" => "Here I am",
-            ),
-        ),
-    )
+            ],
+        ],
+    ]
 );
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "plugin Load",
         "description" => "check current load",
-        "check" => array(
+        "check" => [
             "function" => "Loadmeter",
-            "params" => array(
+            "params" => [
                 "warning" => 1.0,
                 "error" => 3,
-            ),
-        ),
+            ],
+        ],
         "worstresult" => RESULT_OK
-    )
+    ]
 );
 $oMonitor->addCheck(
-    array(
+    [
         "name" => "plugin ApacheProcesses",
         "description" => "check count running Apache processes",
-        "check" => array(
+        "check" => [
             "function" => "ApacheProcesses",
-            "params" => array(
-            ),
-        ),
+            "params" => [
+            ],
+        ],
         "worstresult" => RESULT_OK
-    )
+    ]
 );
 */
 
diff --git a/public_html/appmonitor/plugins/checks/apacheprocesses.php b/public_html/appmonitor/plugins/checks/apacheprocesses.php
index 2b6809be..400c880b 100755
--- a/public_html/appmonitor/plugins/checks/apacheprocesses.php
+++ b/public_html/appmonitor/plugins/checks/apacheprocesses.php
@@ -44,94 +44,145 @@
  * 
  * 2019-06-07  <axel.hahn@iml.unibe.ch>
  * 2022-07-06  <axel.hahn@iml.unibe.ch>  set group "monitor"
+ * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 
  */
-class checkApacheProcesses extends appmonitorcheck{
-    
-    protected $_sServerStatusUrl = 'http://localhost/server-status';
-    protected $_iWarn = 50;
-    protected $_iError = 75;
-    
+class checkApacheProcesses extends appmonitorcheck
+{
+
     /**
-     * fetch http server status and return slots, active and waiting processes
-     * as array i.e. [total] => 256 \n    [free] => 247\n    [waiting] => 7\n    [active] => 2
-     * @return boolean
+     * url of server status
+     * @var string
+     */
+    protected string $_sServerStatusUrl = 'http://localhost/server-status';
+
+    /**
+     * Warning level in percent
+     * @var float
+     */
+    protected float $_iWarn = 50;
+
+    /**
+     * Critical level in percent
+     * @var float
+     */
+    protected float $_iError = 75;
+
+    /**
+     * Self documentation (as idea)
+     * @return array
      */
-    protected function _getApacheProcesses() {
+    public function explain(): array
+    {
+        return [
+            'name' => 'Plugin ApacheProcesses',
+            'descriptionm' => 'Check count running Apache processes',
+            'parameters' => [
+                'url' => [
+                    'type' => 'string',
+                    'required' => false,
+                    'decsription' => 'Override https server-status page; default is http://localhost/server-status; Use it if the protocol to localhost is not http, but https or if it requires an authentication',
+                    'default' => $this->_sServerStatusUrl,
+                    'example' => '',
+                ],
+                'warning' => [
+                    'type' => 'float',
+                    'required' => false,
+                    'decsription' => 'Limit to switch to warning (in percent)',
+                    'default' => $this->_iWarn,
+                    'example' => 30,
+                ],
+                'error' => [
+                    'type' => 'float',
+                    'required' => false,
+                    'decsription' => 'Limit to switch to critical (in percent)',
+                    'default' => $this->_iError,
+                    'example' => 50,
+                ],
+            ],
+        ];
+    }
 
+    /**
+     * Fetch http server status and return slots, active and waiting processes
+     * as array i.e. [total] => 256 \n    [free] => 247\n    [waiting] => 7\n    [active] => 2
+     * It returns false if the url is not reachable
+     * It returns an empty array if the server status could not be parsed from http response
+     * @return array
+     */
+    protected function _getApacheProcesses(): bool|array
+    {
         $sBody = file_get_contents($this->_sServerStatusUrl);
-        if(!$sBody){
+        if (!$sBody) {
             return false;
         }
         $sRegexScoreboard = '/<pre>(.*)\<\/pre\>/U';
-        $aScore=[];
+        $aScore = [];
         $sStatusNobr = str_replace("\n", "", $sBody);
 
         if (preg_match_all($sRegexScoreboard, $sStatusNobr, $aTmpTable)) {
-            $sScoreString=$aTmpTable[1][0];
+            $sScoreString = $aTmpTable[1][0];
             // $aScore['scoreboard']=$sScoreString;
-            $aScore['total']=strlen($sScoreString);
-            $aScore['free']=substr_count($sScoreString, '.');
-            $aScore['waiting']=substr_count($sScoreString, '_');
-            $aScore['active']=$aScore['total']-$aScore['free']-$aScore['waiting'];
+            $aScore['total'] = strlen($sScoreString);
+            $aScore['free'] = substr_count($sScoreString, '.');
+            $aScore['waiting'] = substr_count($sScoreString, '_');
+            $aScore['active'] = $aScore['total'] - $aScore['free'] - $aScore['waiting'];
         }
         return $aScore;
     }
 
-
     /**
-     * get default group of this check
-     * @param array   $aParams
-     * @return array
+     * Get default group of this check
+     * @return string
      */
-    public function getGroup(){
+    public function getGroup(): string
+    {
         return 'monitor';
     }
 
     /**
-     * 
-     * @param array   $aParams
+     * Implemented method: run the check
+     * @param array $aParams  parameters
      * @return array
      */
-    public function run($aParams){
-        
+    public function run(array $aParams): array
+    {
+
         // --- (1) verify if array key(s) exist:
         // $this->_checkArrayKeys($aParams, "...");
-        if(isset($aParams['url']) && $aParams['url']){
-            $this->_sServerStatusUrl=$aParams['url'];
+        if (isset($aParams['url']) && $aParams['url']) {
+            $this->_sServerStatusUrl = $aParams['url'];
         }
-        if(isset($aParams['warning']) && (int)$aParams['warning']){
-            $this->_iWarn=(int)$aParams['warning'];
+        if (isset($aParams['warning']) && (int) $aParams['warning']) {
+            $this->_iWarn = (int) $aParams['warning'];
         }
-        if(isset($aParams['error']) && (int)$aParams['error']){
-            $this->_iError=(int)$aParams['error'];
+        if (isset($aParams['error']) && (int) $aParams['error']) {
+            $this->_iError = (int) $aParams['error'];
         }
-    
 
         // --- (2) do something magic
-        $aProcesses=$this->_getApacheProcesses();
-        $iActive=$aProcesses ? $aProcesses['active'] : false;
-        
+        $aProcesses = $this->_getApacheProcesses();
+        $iActive = $aProcesses ? $aProcesses['active'] : false;
+
         // set result code
-        if($iActive===false){
-            $iResult=RESULT_UNKNOWN;
+        if ($iActive === false) {
+            $iResult = RESULT_UNKNOWN;
         } else {
-            $sComment='';
-            $iTotal=$aProcesses['total'];
-            $iResult=RESULT_OK;
-            if(($iActive/$iTotal*100)>$this->_iWarn){
-                $iResult=RESULT_WARNING;
-                $sComment='more than warning level '.$this->_iWarn.'%';
+            $sComment = '';
+            $iTotal = $aProcesses['total'];
+            $iResult = RESULT_OK;
+            if (($iActive / $iTotal * 100) > $this->_iWarn) {
+                $iResult = RESULT_WARNING;
+                $sComment = "more than warning level $this->_iWarn %";
             } else {
-                $sComment='less than warning level '.$this->_iWarn.'%';
+                $sComment = "less than warning level $this->_iWarn %";
             }
-            if(($iActive/$iTotal*100)>$this->_iError){
-                $iResult=RESULT_ERROR;
-                $sComment='more than error level '.$this->_iError.'%';
+            if (($iActive / $iTotal * 100) > $this->_iError) {
+                $iResult = RESULT_ERROR;
+                $sComment = "more than error level $this->_iError %";
             }
         }
 
-
         // --- (3) response
         // see method appmonitorcheck->_setReturn()
         // 
@@ -144,14 +195,14 @@ class checkApacheProcesses extends appmonitorcheck{
         //              visual => {string} one of bar|line|simple (+params)
         //           
         return [
-            $iResult, 
-            ($iActive===false ? 'Apache httpd server status is not available' : 'apache processes: '.print_r($aProcesses, 1)).' '.$sComment,
-            ($iActive===false 
+            $iResult,
+            ($iActive === false ? 'Apache httpd server status is not available' : 'apache processes: ' . print_r($aProcesses, 1)) . ' ' . $sComment,
+            ($iActive === false
                 ? []
                 : [
-                    'type'=>'counter',
-                    'count'=>$iActive,
-                    'visual'=>'line',
+                    'type' => 'counter',
+                    'count' => $iActive,
+                    'visual' => 'line',
                 ]
             )
         ];
diff --git a/public_html/appmonitor/plugins/checks/cert.php b/public_html/appmonitor/plugins/checks/cert.php
index e83fa9e3..a6705b23 100755
--- a/public_html/appmonitor/plugins/checks/cert.php
+++ b/public_html/appmonitor/plugins/checks/cert.php
@@ -39,20 +39,22 @@
  * 2021-10-26  <axel.hahn@iml.unibe.ch>
  * 2022-05-02  <axel.hahn@iml.unibe.ch>  set warning to 21 days (old value was 30); add "critical" param
  * 2022-05-03  <axel.hahn@iml.unibe.ch>  critical limit is a warning only (because app is still functional)
+ * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 
  */
-class checkCert extends appmonitorcheck{
+class checkCert extends appmonitorcheck
+{
     /**
-     * get default group of this check
-     * @param array   $aParams
-     * @return array
+     * Get default group of this check
+     * @return string
      */
-    public function getGroup(){
+    public function getGroup(): string
+    {
         return 'security';
     }
 
     /**
-     * check SSL certificate 
+     * Check SSL certificate 
      * @param array $aParams
      * [
      *     "url"       optional: url to connect check; default: own protocol + server
@@ -60,62 +62,60 @@ class checkCert extends appmonitorcheck{
      *     "warning"   optional: count of days to warn; default=21 (=3 weeks)
      *     "critical"  optional: count of days to raise critical; default=5
      * ]
-     * @return boolean
+     * @return array
      */
-    public function run($aParams) {
-        $sUrl = isset($aParams["url"]) 
-                ? $aParams["url"] 
-                : 'http'. ($_SERVER['HTTPS'] ? 's' : '') . '://' . $_SERVER['SERVER_NAME'] .':' . $_SERVER['SERVER_PORT']
-                ;
-        $bVerify =   isset($aParams["verify"])   ? !!$aParams["verify"]        : true;
-        $iWarn   =   isset($aParams["warning"])  ? (int)($aParams["warning"])  : 21;
-        $iCrtitcal = isset($aParams["critical"]) ? (int)($aParams["critical"]) : 5;
+    public function run(array $aParams): array
+    {
+        $sUrl = $aParams["url"] ?? 'http' . ($_SERVER['HTTPS'] ? 's' : '') . '://' . $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'];
+        $bVerify = isset($aParams["verify"]) ? !!$aParams["verify"] : true;
+        $iWarn = isset($aParams["warning"]) ? (int) ($aParams["warning"]) : 21;
+        $iCrtitcal = isset($aParams["critical"]) ? (int) ($aParams["critical"]) : 5;
 
-        $sMessage="Checked url: $sUrl ... ";
-        $certinfo=$this->_certGetInfos($sUrl, $bVerify);
-        if(isset($certinfo['_error'])){
+        $sMessage = "Checked url: $sUrl ... ";
+        $certinfo = $this->_certGetInfos($sUrl, $bVerify);
+        if (isset($certinfo['_error'])) {
             return [
-                RESULT_ERROR, 
+                RESULT_ERROR,
                 $certinfo['_error'] . $sMessage
             ];
         }
-        
-        $sDNS=isset($certinfo['extensions']['subjectAltName']) ? $certinfo['extensions']['subjectAltName'] : false;
-        $sHost=parse_url($sUrl,PHP_URL_HOST);
-        if(strstr($sDNS, 'DNS:'.$sHost)===false){
+
+        $sDNS = $certinfo['extensions']['subjectAltName'] ?? false;
+        $sHost = parse_url($sUrl, PHP_URL_HOST);
+        if (strstr($sDNS, "DNS:$sHost") === false) {
             return [
-                RESULT_ERROR, 
-                'Wrong certificate: '.$sHost.' is not listed as DNS alias in ['.$sDNS.']  ' . $sMessage
+                RESULT_ERROR,
+                "Wrong certificate: $sHost is not listed as DNS alias in [$sDNS]. $sMessage"
             ];
         }
-        
+
         $iDaysleft = round(($certinfo['validTo_time_t'] - date('U')) / 60 / 60 / 24);
-        $sMessage.= 'Issuer: '. $sIssuer=$certinfo['issuer']['O'] 
-                . '; valid from: '. date("Y-m-d H:i", $certinfo['validFrom_time_t'])
-                . ' to '.date("Y-m-d H:i", $certinfo['validTo_time_t']).' '
-                . ( $iDaysleft ? "($iDaysleft days left)" : "expired since ".(-$iDaysleft)." days.")
-                ;
-        if ($iDaysleft<=0) {
+        $sMessage .= 'Issuer: ' . $certinfo['issuer']['O']
+            . '; valid from: ' . date("Y-m-d H:i", $certinfo['validFrom_time_t'])
+            . ' to ' . date("Y-m-d H:i", $certinfo['validTo_time_t']) . ' '
+            . ($iDaysleft ? "($iDaysleft days left)" : "expired since " . (-$iDaysleft) . " days.")
+        ;
+        if ($iDaysleft <= 0) {
             return [
-                RESULT_ERROR, 
+                RESULT_ERROR,
                 'Expired! ' . $sMessage
             ];
         }
-        if ($iDaysleft<=$iWarn) {
+        if ($iDaysleft <= $iWarn) {
             return [
-                RESULT_WARNING, 
-                ($iDaysleft<=$iCrtitcal
-                 ? 'Expires very soon! '
-                 : 'Expires soon. ' 
-                ). $sMessage
+                RESULT_WARNING,
+                ($iDaysleft <= $iCrtitcal
+                    ? 'Expires very soon! '
+                    : 'Expires soon. '
+                ) . $sMessage
             ];
         }
         // echo '<pre>';
         return [
-            RESULT_OK, 
-            'OK. ' 
-                .($bVerify ? 'Certificate is valid. ' : '(Verification is disabled; Check for expiration only.) ' )
-                . $sMessage
+            RESULT_OK,
+            'OK. '
+            . ($bVerify ? 'Certificate is valid. ' : '(Verification is disabled; Check for expiration only.) ')
+            . $sMessage
         ];
     }
 
diff --git a/public_html/appmonitor/plugins/checks/diskfree.php b/public_html/appmonitor/plugins/checks/diskfree.php
index 403c94e5..f1d94284 100755
--- a/public_html/appmonitor/plugins/checks/diskfree.php
+++ b/public_html/appmonitor/plugins/checks/diskfree.php
@@ -18,67 +18,69 @@
  * ____________________________________________________________________________
  * 
  * 2021-10-26  <axel.hahn@iml.unibe.ch>
+ * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 
  */
-class checkDiskfree extends appmonitorcheck{
+class checkDiskfree extends appmonitorcheck
+{
     /**
-     * get default group of this check
-     * @param array   $aParams
-     * @return array
+     * Get default group of this check
+     * @return string
      */
-    public function getGroup(){
+    public function getGroup(): string
+    {
         return 'disk';
     }
 
     /**
-     * check free disk space on a given directory
+     * Check free disk space on a given directory
      * @param array $aParams
      * [
      *     "directory"   directory that must exist
      *     "warning"     space for warning (optional)
      *     "critical"    minimal space
      * ]
-     * @return boolean
+     * @return array
      */
-    public function run($aParams) {
-        $this->_checkArrayKeys($aParams, "directory", "critical");
-        
+    public function run(array $aParams): array
+    {
+        $this->_checkArrayKeys($aParams, "directory,critical");
+
         $sDirectory = $aParams["directory"];
-        if(!is_dir($sDirectory)){
+        if (!is_dir($sDirectory)) {
             return [
-                RESULT_ERROR, 
-                'directory [' . $sDirectory . '] does not exist. Maybe it is wrong or is not mounted.'
+                RESULT_ERROR,
+                "directory [$sDirectory] does not exist. Maybe it is wrong or is not mounted."
             ];
         }
-        
+
         $iWarn = isset($aParams["warning"]) ? $this->_getSize($aParams["warning"]) : false;
         $iCritical = $this->_getSize($aParams["critical"]);
-        $iSpaceLeft=disk_free_space($sDirectory);
-        
-        
-        $sMessage='[' . $sDirectory . '] has '.$this->_getHrSize($iSpaceLeft).' left.';
-        
-        if($iWarn){
-            if($iWarn<=$iCritical){
+        $iSpaceLeft = disk_free_space($sDirectory);
+
+        $sMessage = '[' . $sDirectory . '] has ' . $this->_getHrSize($iSpaceLeft) . ' left.';
+
+        if ($iWarn) {
+            if ($iWarn <= $iCritical) {
                 header('HTTP/1.0 503 Service Unavailable');
                 die("ERROR in a Diskfree check - warning value must be larger than critical.<pre>" . print_r($aParams, true));
             }
-            if ($iWarn<$iSpaceLeft){
+            if ($iWarn < $iSpaceLeft) {
                 return [
-                    RESULT_OK, 
-                    $sMessage.' Warning level is not reached yet (still '.$this->_getHrSize($iSpaceLeft-$iWarn).' over warning limit).'
+                    RESULT_OK,
+                    "$sMessage Warning level is not reached yet (still " . $this->_getHrSize($iSpaceLeft - $iWarn) . "over warning limit)."
                 ];
             }
-            if ($iWarn>$iSpaceLeft && $iCritical<$iSpaceLeft){
+            if ($iWarn > $iSpaceLeft && $iCritical < $iSpaceLeft) {
                 return [
-                    RESULT_WARNING, 
-                    $sMessage.' Warning level '.$this->_getHrSize($iWarn).' was reached (space is '.$this->_getHrSize($iWarn-$iSpaceLeft).' below warning limit; still '.$this->_getHrSize($iSpaceLeft-$iCritical).' over critical limit).'
+                    RESULT_WARNING,
+                    $sMessage . ' Warning level ' . $this->_getHrSize($iWarn) . ' was reached (space is ' . $this->_getHrSize($iWarn - $iSpaceLeft) . ' below warning limit; still ' . $this->_getHrSize($iSpaceLeft - $iCritical) . ' over critical limit).'
                 ];
             }
         }
         // check space
-        if ($iCritical<$iSpaceLeft){
-            return [RESULT_OK, $sMessage .' Minimum is not reached yet (still '.$this->_getHrSize($iSpaceLeft-$iCritical).' over critical limit).'];
+        if ($iCritical < $iSpaceLeft) {
+            return [RESULT_OK, $sMessage . ' Minimum is not reached yet (still ' . $this->_getHrSize($iSpaceLeft - $iCritical) . ' over critical limit).'];
         } else {
             return [RESULT_ERROR, $sMessage];
         }
diff --git a/public_html/appmonitor/plugins/checks/exec.php b/public_html/appmonitor/plugins/checks/exec.php
index 05ff55da..6221ca6f 100644
--- a/public_html/appmonitor/plugins/checks/exec.php
+++ b/public_html/appmonitor/plugins/checks/exec.php
@@ -20,20 +20,22 @@
  * ____________________________________________________________________________
  * 
  * 2022-09-19  <axel.hahn@iml.unibe.ch>
+ * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 
  */
-class checkExec extends appmonitorcheck{
+class checkExec extends appmonitorcheck
+{
     /**
-     * get default group of this check
-     * @param array   $aParams - see run() method
-     * @return array
+     * Get default group of this check
+     * @return string
      */
-    public function getGroup($aParams){
+    public function getGroup()
+    {
         return 'service';
     }
 
     /**
-     * check execution of a command
+     * Check execution of a command
      * @param array $aParams
      * [
      *     "command"        {string} command to execute
@@ -48,74 +50,78 @@ class checkExec extends appmonitorcheck{
      *     "searchWarn"     {string} if search string is found check returns with warning
      *     "searchCritical" {string} if search string is found check returns with critical
      * ]
-     * @return boolean
+     * @return array
      */
-    public function run($aParams) {
+    public function run(array $aParams): array
+    {
         $this->_checkArrayKeys($aParams, "command");
-        $_sCmd=$aParams['command'];
-        $_bShowOutput=isset($aParams['output'])        ? !!$aParams['output']       : true;
+        $_sCmd = $aParams['command'];
+        $_bShowOutput = isset($aParams['output']) ? !!$aParams['output'] : true;
 
-        $_aRcOK=isset($aParams['exitOK'])              ? $aParams['exitOK']       : [];
-        $_aRcWarning=isset($aParams['exitWarn'])       ? $aParams['exitWarn']     : [];
-        $_aRcCritical=isset($aParams['exitCritical'])  ? $aParams['exitCritical'] : [];
+        $_aRcOK = isset($aParams['exitOK']) ? $aParams['exitOK'] : [];
+        $_aRcWarning = isset($aParams['exitWarn']) ? $aParams['exitWarn'] : [];
+        $_aRcCritical = isset($aParams['exitCritical']) ? $aParams['exitCritical'] : [];
 
-        $_sMode='default';
-        if(count($_aRcOK) + count($_aRcWarning) + count($_aRcCritical)){
-            $_sMode='exitcode';
+        $_sMode = 'default';
+        if (count($_aRcOK) + count($_aRcWarning) + count($_aRcCritical)) {
+            $_sMode = 'exitcode';
         }
 
-        exec($_sCmd,$aOutput, $iRc);
-        $_sOut=$_bShowOutput ? '<br>'.implode("<br>", $aOutput) : '';
+        exec($_sCmd, $aOutput, $iRc);
+        $_sOut = $_bShowOutput ? '<br>' . implode("<br>", $aOutput) : '';
 
-        switch($_sMode){
+        switch ($_sMode) {
             // non-zero exitcode is an error
             case "default":
                 if ($iRc) {
                     return [
-                        RESULT_ERROR, 
-                        'command failed with exitcode '.$iRc.': [' . $_sCmd . ']'.$_sOut
+                        RESULT_ERROR,
+                        'command failed with exitcode ' . $iRc . ': [' . $_sCmd . ']' . $_sOut
                     ];
                 } else {
-                    return[
-                        RESULT_OK, 
-                        'OK [' . $_sCmd . '] ' .$_sOut
+                    return [
+                        RESULT_OK,
+                        "OK [$_sCmd] $_sOut"
                     ];
-                };
-                break;;
+                }
+                ;
+                // break;
+                ;
 
             // handle given custom exitcodes
             case "exitcode":
-                if (in_array($iRc, $_aRcCritical)){
+                if (in_array($iRc, $_aRcCritical)) {
                     return [
-                        RESULT_ERROR, 
-                        'Critical exitcode '.$iRc.' detected: [' . $_sCmd . ']'.$_sOut
+                        RESULT_ERROR,
+                        "Critical exitcode $iRc detected: [$_sCmd] $_sOut"
                     ];
                 }
-                if (in_array($iRc, $_aRcWarning)){
+                if (in_array($iRc, $_aRcWarning)) {
                     return [
-                        RESULT_WARNING, 
-                        'Warning exitcode '.$iRc.' detected: [' . $_sCmd . ']'.$_sOut
+                        RESULT_WARNING,
+                        "Warning exitcode $iRc detected: [$_sCmd] $_sOut"
                     ];
                 }
-                if ($iRc == 0 || in_array($iRc, $_aRcOK)){
+                if ($iRc == 0 || in_array($iRc, $_aRcOK)) {
                     return [
-                        RESULT_OK, 
-                        'OK exitcode '.$iRc.' detected: [' . $_sCmd . ']'.$_sOut
+                        RESULT_OK,
+                        "OK exitcode $iRc detected: [$_sCmd] $_sOut"
                     ];
                 }
                 return [
-                    RESULT_UNKNOWN, 
-                    'UNKNOWN - unhandled exitcode '.$iRc.' detected: [' . $_sCmd . ']'.$_sOut
+                    RESULT_UNKNOWN,
+                    "UNKNOWN - unhandled exitcode $iRc detected: [$_sCmd] $_sOut"
                 ];
             case "search":
-                return[
-                    RESULT_UNKNOWN, 
-                    'UNKNOWN method [' . $_sMode . '] - is not implemented yet.'
+                return [
+                    RESULT_UNKNOWN,
+                    "UNKNOWN method [$_sMode] - is not implemented yet."
                 ];
-                break;;
+                // break;
+                ;
             default:
-                return[
-                    RESULT_UNKNOWN, 
+                return [
+                    RESULT_UNKNOWN,
                     'UNKNOWN mode [' . htmlentities($_sMode) . '].'
                 ];
         } // switch($_sMode)
diff --git a/public_html/appmonitor/plugins/checks/file.php b/public_html/appmonitor/plugins/checks/file.php
index 590ac778..f0aafde1 100755
--- a/public_html/appmonitor/plugins/checks/file.php
+++ b/public_html/appmonitor/plugins/checks/file.php
@@ -21,29 +21,31 @@
  * ____________________________________________________________________________
  * 
  * 2021-10-26  <axel.hahn@iml.unibe.ch>
- * 
+ * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  */
-class checkFile extends appmonitorcheck{
+class checkFile extends appmonitorcheck
+{
     /**
-     * get default group of this check
+     * Get default group of this check
      * @param array   $aParams - see run() method
-     * @return array
+     * @return string
      */
-    public function getGroup($aParams){
-        $sReturn='file';
-        if(isset($aParams['dir'])){
-            $sReturn='folder';
+    public function getGroup(array $aParams = []): string
+    {
+        $sReturn = 'file';
+        if (isset($aParams['dir'])) {
+            $sReturn = 'folder';
         }
-        foreach(['exists', 'executable', 'readable', 'writable'] as $sFlag){
-            if (isset($aParams[$sFlag]) && !$aParams[$sFlag]){
-                $sReturn='deny';
+        foreach (['exists', 'executable', 'readable', 'writable'] as $sFlag) {
+            if (isset($aParams[$sFlag]) && !$aParams[$sFlag]) {
+                $sReturn = 'deny';
             }
         }
         return $sReturn;
     }
 
     /**
-     * check a file
+     * Check a file
      * @param array $aParams
      * [
      *     "filename"    directory that must exist
@@ -55,9 +57,10 @@ class checkFile extends appmonitorcheck{
      *     "readable"    flag is readable
      *     "writable"    flag is writable
      * ]
-     * @return boolean
+     * @return array
      */
-    public function run($aParams) {
+    public function run(array $aParams): array
+    {
         $aOK = [];
         $aErrors = [];
         $this->_checkArrayKeys($aParams, "filename");
@@ -71,10 +74,10 @@ class checkFile extends appmonitorcheck{
                 $aErrors[] = $sMyflag;
             }
         }
-        foreach ([ 'dir', 'executable', 'file', 'link', 'readable', 'writable' ] as $sFiletest) {
+        foreach (['dir', 'executable', 'file', 'link', 'readable', 'writable'] as $sFiletest) {
             if (isset($aParams[$sFiletest])) {
                 $sTestCmd = 'return is_' . $sFiletest . '("' . $sFile . '");';
-                if (eval($sTestCmd) && $aParams[$sFiletest]) {
+                if (eval ($sTestCmd) && $aParams[$sFiletest]) {
                     $aOK[] = $sFiletest . '=' . ($aParams[$sFiletest] ? 'yes' : 'no');
                 } else {
                     $aErrors[] = $sFiletest . '=' . ($aParams[$sFiletest] ? 'yes' : 'no');
@@ -82,16 +85,17 @@ class checkFile extends appmonitorcheck{
             }
         }
         $sMessage = (count($aOK) ? ' flags OK: ' . implode('|', $aOK) : '')
-                . ' ' . (count($aErrors) ? ' flags FAILED: ' . implode('|', $aErrors) : '')
+            . ' ' . (count($aErrors) ? ' flags FAILED: ' . implode('|', $aErrors) : '')
         ;
         if (count($aErrors)) {
             return [
-                RESULT_ERROR, 
-                'file test [' . $sFile . '] ' . $sMessage
+                RESULT_ERROR,
+                "file test [$sFile] $sMessage"
             ];
         } else {
-            return[
-                RESULT_OK, 'file test [' . $sFile . '] ' . $sMessage
+            return [
+                RESULT_OK,
+                "file test [$sFile] $sMessage"
             ];
         }
     }
diff --git a/public_html/appmonitor/plugins/checks/hello.php b/public_html/appmonitor/plugins/checks/hello.php
index ebeb9818..e2ba5e60 100755
--- a/public_html/appmonitor/plugins/checks/hello.php
+++ b/public_html/appmonitor/plugins/checks/hello.php
@@ -40,17 +40,20 @@
  * ____________________________________________________________________________
  * 
  * 2019-06-05  <axel.hahn@iml.unibe.ch>
+ * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 
  */
-class checkHello extends appmonitorcheck{
-    
+class checkHello extends appmonitorcheck
+{
+
     /**
-     * 
+     * Run the check
      * @param array   $aParams
      * @return array
      */
-    public function run($aParams){
-        
+    public function run(array $aParams): array
+    {
+
         // --- (1) verify if array key(s) exist:
         $this->_checkArrayKeys($aParams, "message");
 
@@ -70,8 +73,8 @@ class checkHello extends appmonitorcheck{
         //              visual => {string} one of bar|line|simple (+params)
         //           
         return [
-            RESULT_OK, 
-            'Hello world! My message is: ' .$aParams['message']
+            RESULT_OK,
+            'Hello world! My message is: ' . $aParams['message']
         ];
     }
 }
diff --git a/public_html/appmonitor/plugins/checks/httpcontent.php b/public_html/appmonitor/plugins/checks/httpcontent.php
index 037adf6c..8655a067 100755
--- a/public_html/appmonitor/plugins/checks/httpcontent.php
+++ b/public_html/appmonitor/plugins/checks/httpcontent.php
@@ -20,24 +20,29 @@
  * 2021-10-26  <axel.hahn@iml.unibe.ch>
  * 2022-12-21  <axel.hahn@unibe.ch>      add flag sslverify
  * 2023-07-06  <axel.hahn@unibe.ch>      add flag userpwd
+ * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 
  */
-class checkHttpContent extends appmonitorcheck{
+class checkHttpContent extends appmonitorcheck
+{
     /**
-     * get default group of this check
-     * @param array   $aParams
-     * @return array
+     * Get default group of this check
+     * It is a "service" icon or "deny" for expected failures
+     * 
+     * @param array   $aParams with optional 'status' containing http response code
+     * @return string
      */
-    public function getGroup($aParams){
-        $sReturn='service';
-        if(isset($aParams['status']) && $aParams['status'] > 300 && $aParams['status'] < 500 ){
-            $sReturn='deny';
+    public function getGroup(array $aParams=[]): string
+    {
+        $sReturn = 'service';
+        if (isset($aParams['status']) && $aParams['status'] > 300 && $aParams['status'] < 500) {
+            $sReturn = 'deny';
         }
         return $sReturn;
     }
 
     /**
-     * make http request and test response body
+     * Make http request and test response header + body
      * @param array $aParams
      * [
      *     url                 string   url to fetch
@@ -46,16 +51,20 @@ class checkHttpContent extends appmonitorcheck{
      *     headeronly          boolean  optional flag to fetch http response herader only; default: false = returns header and body
      *     follow              boolean  optional flag to follow a location; default: false = do not follow
      *     sslverify           boolean  flag: enable/ disable verification of ssl certificate; default: true (verification is on)
+     *
      *     status              integer  test for an expected http status code; if none is given then test fails on status 400 and greater
+     *
      *     headercontains      string   test for a string in the http response header; it returns OK if the text was found
      *     headernotcontains   string   test for a string in the http response header; it returns OK if the text was not found
      *     headerregex         string   test for a regex in the http response header; it returns OK if the regex matches; example: "headerregex"=>"/lowercasematch/i"
+     *
      *     bodycontains        string   test for a string in the http response body; it returns OK if the text was found
      *     bodynotcontains     string   test for a string in the http response body; it returns OK if the text was not found
      *     bodyregex           string   test for a regex in the http response body; it returns OK if the regex matches; example: "headerregex"=>"/lowercasematch/i"
      * ]
      */
-    public function run($aParams) {
+    public function run(array $aParams)
+    {
         $this->_checkArrayKeys($aParams, "url");
         if (!function_exists("curl_init")) {
             header('HTTP/1.0 503 Service Unavailable');
@@ -63,30 +72,31 @@ class checkHttpContent extends appmonitorcheck{
         }
         $bShowContent = (isset($aParams["content"]) && $aParams["content"]) ? true : false;
         $ch = curl_init($aParams["url"]);
-        
+
         curl_setopt($ch, CURLOPT_HEADER, 1);
         curl_setopt($ch, CURLOPT_NOBODY, isset($aParams["headeronly"]) && $aParams["headeronly"]);
         curl_setopt($ch, CURLOPT_FOLLOWLOCATION, isset($aParams["follow"]) && $aParams["follow"]);
         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, isset($aParams["sslverify"]) ? !!$aParams["sslverify"] : 1);
-        curl_setopt($ch, CURLOPT_TIMEOUT, (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp);
-        if (isset($aParams["userpwd"])){
+        curl_setopt($ch, CURLOPT_TIMEOUT, (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp);
+        if (isset($aParams["userpwd"])) {
             curl_setopt($ch, CURLOPT_USERPWD, $aParams["userpwd"]);
         }
 
         $res = curl_exec($ch);
 
         if (!$res) {
-            $iErrorCode=curl_errno($ch);
-            $sErrorMsg=curl_error($ch);
+            $iErrorCode = curl_errno($ch);
+            $sErrorMsg = curl_error($ch);
             curl_close($ch);
             return [
-                RESULT_ERROR, 'ERROR: failed to fetch ' . $aParams["url"] . ' - curl error #'.$iErrorCode.': '.$sErrorMsg
+                RESULT_ERROR,
+                'ERROR: failed to fetch ' . $aParams["url"] . ' - curl error #' . $iErrorCode . ': ' . $sErrorMsg
             ];
-        } 
-        $sOut='';
-        $bError=false;
-        
+        }
+        $sOut = '';
+        $bError = false;
+
         $aInfos = curl_getinfo($ch);
         /*
             Array
@@ -122,112 +132,110 @@ class checkHttpContent extends appmonitorcheck{
                 [local_port] => 63597
             )
          */
-        
+
         curl_close($ch);
-        
-        $aTmp=explode("\r\n\r\n", $res, 2);
-        $sHttpHeader=$aTmp[0];
-        $sHttpBody=isset($aTmp[1]) ? $aTmp[1] : false;
-        
+
+        $aTmp = explode("\r\n\r\n", $res, 2);
+        $sHttpHeader = $aTmp[0];
+        $sHttpBody = $aTmp[1] ?? false;
+
         // ---------- check functions
-        
+
         // --- http status code
-        $sOut.="Http status: ".$aInfos['http_code']." - ";
-        if(isset($aParams["status"])){
-            if($aInfos['http_code'] === $aParams["status"]){
-                $sOut.="compare OK<br>";
+        $sOut .= "Http status: " . $aInfos['http_code'] . " - ";
+        if (isset($aParams["status"])) {
+            if ($aInfos['http_code'] === $aParams["status"]) {
+                $sOut .= "compare OK<br>";
             } else {
-                $sOut.="compare failed<br>";
-                $bError=true;
+                $sOut .= "compare failed<br>";
+                $bError = true;
             }
         } else {
-            if($aInfos['http_code'] >= 400){
-                $sOut.="Error page detected<br>";
-                $bError=true;
+            if ($aInfos['http_code'] >= 400) {
+                $sOut .= "Error page detected<br>";
+                $bError = true;
             } else {
-                $sOut.="request successful<br>";
+                $sOut .= "request successful<br>";
             }
         }
         // --- http header
-        if(isset($aParams["headercontains"]) && $aParams["headercontains"]){
-            $sOut.="Http header contains &quot;".$aParams["headercontains"]."&quot; - ";
-            if(!strstr($sHttpHeader, $aParams["headercontains"])===false){
-                $sOut.="compare OK<br>";
+        if (isset($aParams["headercontains"]) && $aParams["headercontains"]) {
+            $sOut .= "Http header contains &quot;" . $aParams["headercontains"] . "&quot; - ";
+            if (!strstr($sHttpHeader, $aParams["headercontains"]) === false) {
+                $sOut .= "compare OK<br>";
             } else {
-                $sOut.="compare failed<br>";
-                $bError=true;
+                $sOut .= "compare failed<br>";
+                $bError = true;
             }
         }
-        if(isset($aParams["headernotcontains"]) && $aParams["headernotcontains"]){
-            $sOut.="Http header does not contain &quot;".$aParams["headernotcontains"]."&quot; - ";
-            if(strstr($sHttpHeader, $aParams["headernotcontains"])===false){
-                $sOut.="compare OK<br>";
+        if (isset($aParams["headernotcontains"]) && $aParams["headernotcontains"]) {
+            $sOut .= "Http header does not contain &quot;" . $aParams["headernotcontains"] . "&quot; - ";
+            if (strstr($sHttpHeader, $aParams["headernotcontains"]) === false) {
+                $sOut .= "compare OK<br>";
             } else {
-                $sOut.="compare failed<br>";
-                $bError=true;
+                $sOut .= "compare failed<br>";
+                $bError = true;
             }
         }
-        if(isset($aParams["headerregex"]) && $aParams["headerregex"]){
-            $sOut.="Http header regex test &quot;".$aParams["headerregex"]."&quot; - ";
-            try{
-                $bRegex=preg_match($aParams["headerregex"], $sHttpHeader);
-                if($bRegex){
-                    $sOut.="compare OK<br>";
+        if (isset($aParams["headerregex"]) && $aParams["headerregex"]) {
+            $sOut .= "Http header regex test &quot;" . $aParams["headerregex"] . "&quot; - ";
+            try {
+                $bRegex = preg_match($aParams["headerregex"], $sHttpHeader);
+                if ($bRegex) {
+                    $sOut .= "compare OK<br>";
                 } else {
-                    $sOut.="compare failed<br>";
-                    $bError=true;
+                    $sOut .= "compare failed<br>";
+                    $bError = true;
                 }
-            } 
-            catch(Exception $e){
-                $sOut.="Wrong REGEX<br>" . print_r($e, 1).'<br>';
-                $bError=true;
+            } catch (Exception $e) {
+                $sOut .= "Wrong REGEX<br>" . print_r($e, 1) . '<br>';
+                $bError = true;
             }
         }
         // --- http body
-        if(isset($aParams["bodycontains"]) && $aParams["bodycontains"]){
-            $sOut.="Http body contains &quot;".$aParams["bodycontains"]."&quot; - ";
-            if(!strstr($sHttpBody, $aParams["bodycontains"])===false){
-                $sOut.="compare OK<br>";
+        if (isset($aParams["bodycontains"]) && $aParams["bodycontains"]) {
+            $sOut .= "Http body contains &quot;" . $aParams["bodycontains"] . "&quot; - ";
+            if (!strstr($sHttpBody, $aParams["bodycontains"]) === false) {
+                $sOut .= "compare OK<br>";
             } else {
-                $sOut.="compare failed<br>";
-                $bError=true;
+                $sOut .= "compare failed<br>";
+                $bError = true;
             }
         }
-        if(isset($aParams["bodynotcontains"]) && $aParams["bodynotcontains"]){
-            $sOut.="Http body does not contain &quot;".$aParams["bodynotcontains"]."&quot; - ";
-            if(strstr($sHttpBody, $aParams["bodynotcontains"])===false){
-                $sOut.="compare OK<br>";
+        if (isset($aParams["bodynotcontains"]) && $aParams["bodynotcontains"]) {
+            $sOut .= "Http body does not contain &quot;" . $aParams["bodynotcontains"] . "&quot; - ";
+            if (strstr($sHttpBody, $aParams["bodynotcontains"]) === false) {
+                $sOut .= "compare OK<br>";
             } else {
-                $sOut.="compare failed<br>";
-                $bError=true;
+                $sOut .= "compare failed<br>";
+                $bError = true;
             }
         }
-        if(isset($aParams["bodyregex"]) && $aParams["bodyregex"]){
-            $sOut.="Http body regex test &quot;".$aParams["bodyregex"]."&quot; - ";
-            try{
-                $bRegex=preg_match($aParams["bodyregex"], $sHttpBody);
-                if($bRegex){
-                    $sOut.="compare OK<br>";
+        if (isset($aParams["bodyregex"]) && $aParams["bodyregex"]) {
+            $sOut .= "Http body regex test &quot;" . $aParams["bodyregex"] . "&quot; - ";
+            try {
+                $bRegex = preg_match($aParams["bodyregex"], $sHttpBody);
+                if ($bRegex) {
+                    $sOut .= "compare OK<br>";
                 } else {
-                    $sOut.="compare failed<br>";
-                    $bError=true;
+                    $sOut .= "compare failed<br>";
+                    $bError = true;
                 }
-            } 
-            catch(Exception $e){
-                $sOut.="Wrong REGEX<br>" . print_r($e, 1).'<br>';
-                $bError=true;
+            } catch (Exception $e) {
+                $sOut .= "Wrong REGEX<br>" . print_r($e, 1) . '<br>';
+                $bError = true;
             }
         }
-        
+
         if (!$bError) {
             return [
-                RESULT_OK, 
-                'OK: http check "' . $aParams["url"] . '".<br>'.$sOut
+                RESULT_OK,
+                'OK: http check "' . $aParams["url"] . '".<br>' . $sOut
             ];
         } else {
             return [
-                RESULT_ERROR, 
-                'ERROR: http check "' . $aParams["url"] . '".<br>'.$sOut
+                RESULT_ERROR,
+                'ERROR: http check "' . $aParams["url"] . '".<br>' . $sOut
             ];
         }
 
diff --git a/public_html/appmonitor/plugins/checks/loadmeter.php b/public_html/appmonitor/plugins/checks/loadmeter.php
index 8bc5daf1..13911af1 100755
--- a/public_html/appmonitor/plugins/checks/loadmeter.php
+++ b/public_html/appmonitor/plugins/checks/loadmeter.php
@@ -43,40 +43,44 @@
  * ____________________________________________________________________________
  * 
  * 2019-06-06  <axel.hahn@iml.unibe.ch>
+ * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
+ * 2024-07-25  <axel.hahn@unibe.ch>      float return with 2 digits behind comma
  * 
  */
-class checkLoadmeter extends appmonitorcheck{
+class checkLoadmeter extends appmonitorcheck
+{
     /**
-     * get default group of this check
-     * @param array   $aParams
-     * @return array
+     * Get default group of this check
+     * @return string
      */
-    public function getGroup(){
+    public function getGroup(): string
+    {
         return 'monitor';
     }
 
     /**
-     * detect load of a machine and return a float value
+     * Detect load of a machine and return a float value
      * windows part was taken from https://stackoverflow.com/questions/5588616/how-do-you-calculate-server-load-in-php
      * @return float
      */
-    protected function _getLoad() {
-        if (function_exists('sys_getloadavg')){
+    protected function _getLoad(): float
+    {
+        if (function_exists('sys_getloadavg')) {
             $load = sys_getloadavg();
             return $load[0];
         } else {
             // Only MS Windows has not implemented sys_getloadavg
             // try something else
-            if(class_exists('COM')){
-                $wmi=new COM('WinMgmts:\\\\.');
-                $cpus=$wmi->InstancesOf('Win32_Processor');
-                $load=0;
-                if(version_compare('4.50.0', PHP_VERSION) == 1){
-                    while($cpu = $cpus->Next()){
+            if (class_exists('COM')) {
+                $wmi = new COM('WinMgmts:\\\\.');
+                $cpus = $wmi->InstancesOf('Win32_Processor');
+                $load = 0;
+                if (version_compare('4.50.0', PHP_VERSION) == 1) {
+                    while ($cpu = $cpus->Next()) {
                         $load += $cpu->LoadPercentage;
                     }
-                }else{
-                    foreach($cpus as $cpu){
+                } else {
+                    foreach ($cpus as $cpu) {
                         $load += $cpu->LoadPercentage;
                     }
                 }
@@ -87,31 +91,31 @@ class checkLoadmeter extends appmonitorcheck{
     }
 
     /**
-     * 
-     * @param array   $aParams
+     * Run the check and get load
+     * @param array   $aParams  optional array with keys warning,error
      * @return array
      */
-    public function run($aParams){
-        
+    public function run(array $aParams): array
+    {
+
         // --- (1) verify if array key(s) exist:
         // $this->_checkArrayKeys($aParams, "...");
 
-
         // --- (2) do something magic
         // $fLoad=rand(0, 1.3);
         // $fLoad=$this->_getServerLoad();
-        $fLoad=$this->_getLoad();
-        
+        $fLoad = $this->_getLoad();
+
         // set result code
-        if($fLoad===false){
-            $iResult=RESULT_UNKNOWN;
+        if ($fLoad === false) {
+            $iResult = RESULT_UNKNOWN;
         } else {
-            $iResult=RESULT_OK;
-            if(isset($aParams['warning']) && $aParams['warning'] && $fLoad>$aParams['warning']){
-                $iResult=RESULT_WARNING;
+            $iResult = RESULT_OK;
+            if (isset($aParams['warning']) && $aParams['warning'] && $fLoad > $aParams['warning']) {
+                $iResult = RESULT_WARNING;
             }
-            if(isset($aParams['error']) && $aParams['error'] && $fLoad>$aParams['error']){
-                $iResult=RESULT_ERROR;
+            if (isset($aParams['error']) && $aParams['error'] && $fLoad > $aParams['error']) {
+                $iResult = RESULT_ERROR;
             }
         }
 
@@ -128,14 +132,14 @@ class checkLoadmeter extends appmonitorcheck{
         //              visual => {string} one of bar|line|simple (+params)
         //           
         return [
-            $iResult, 
-            ($fLoad===false ? 'load value is not available' : 'current load is: '.$fLoad),
-            ($fLoad===false 
+            $iResult,
+            ($fLoad === false ? 'load value is not available' : 'current load is: ' . round($fLoad, 2)),
+            ($fLoad === false
                 ? []
                 : [
-                    'type'=>'counter',
-                    'count'=>$fLoad,
-                    'visual'=>'line',
+                    'type' => 'counter',
+                    'count' => round($fLoad, 2),
+                    'visual' => 'line',
                 ]
             )
         ]
diff --git a/public_html/appmonitor/plugins/checks/mysqlconnect.php b/public_html/appmonitor/plugins/checks/mysqlconnect.php
index 71342456..d259ec00 100755
--- a/public_html/appmonitor/plugins/checks/mysqlconnect.php
+++ b/public_html/appmonitor/plugins/checks/mysqlconnect.php
@@ -18,19 +18,22 @@
  * ____________________________________________________________________________
  * 
  * 2021-10-27  <axel.hahn@iml.unibe.ch>
+ * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 
  */
-class checkMysqlConnect extends appmonitorcheck{
+class checkMysqlConnect extends appmonitorcheck
+{
     /**
-     * get default group of this check
-     * @param array   $aParams
-     * @return array
+     * Get default group of this check
+     * @return string
      */
-    public function getGroup(){
+    public function getGroup(): string
+    {
         return 'database';
     }
+
     /**
-     * check mysql connection to a database using mysqli realconnect
+     * Check mysql connection to a database using mysqli realconnect
      * @param array $aParams
      * [
      *     server              string   database hostname / ip address
@@ -40,29 +43,30 @@ class checkMysqlConnect extends appmonitorcheck{
      *     port                integer  optional: port
      *     timeout             integer  optional timeout in sec; default: 5
      * ]
+     * @return array
      */
-    public function run($aParams) {
+    public function run(array $aParams): array
+    {
         $this->_checkArrayKeys($aParams, "server,user,password,db");
-        $mysqli=mysqli_init();
-        if(!$mysqli){
+        $mysqli = mysqli_init();
+        if (!$mysqli) {
             return [RESULT_ERROR, 'ERROR: mysqli_init failed.'];
         }
-        if (!$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp)) {
+        if (!$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp)) {
             return [RESULT_ERROR, 'ERROR: setting mysqli_options failed.'];
         }
 
-        $db = (isset($aParams["port"]) && $aParams["port"]) 
-                ? $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"], $aParams["port"])
-                : $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"])
-                ;
+        $db = (isset($aParams["port"]) && $aParams["port"])
+            ? $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"], $aParams["port"])
+            : $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"])
+        ;
         if ($db) {
             $mysqli->close();
             return [RESULT_OK, "OK: Mysql database " . $aParams["db"] . " was connected"];
-            return true;
         } else {
             return [
-                RESULT_ERROR, 
-                "ERROR: Mysql database " . $aParams["db"] . " was not connected. Error ".mysqli_connect_errno() .": ". mysqli_connect_error()
+                RESULT_ERROR,
+                "ERROR: Mysql database " . $aParams["db"] . " was not connected. Error " . mysqli_connect_errno() . ": " . mysqli_connect_error()
             ];
         }
     }
diff --git a/public_html/appmonitor/plugins/checks/pdoconnect.php b/public_html/appmonitor/plugins/checks/pdoconnect.php
index 7f8016b8..d95b3d55 100755
--- a/public_html/appmonitor/plugins/checks/pdoconnect.php
+++ b/public_html/appmonitor/plugins/checks/pdoconnect.php
@@ -18,19 +18,21 @@
  * ____________________________________________________________________________
  * 
  * 2021-10-27  <axel.hahn@iml.unibe.ch>
+ * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 
  */
-class checkPdoConnect extends appmonitorcheck{
+class checkPdoConnect extends appmonitorcheck
+{
     /**
-     * get default group of this check
-     * @param array   $aParams
-     * @return array
+     * Get default group of this check
+     * @return string
      */
-    public function getGroup(){
+    public function getGroup(): string
+    {
         return 'database';
     }
     /**
-     * check connection to a database using pdo
+     * Check connection to a database using pdo
      * see http://php.net/manual/en/pdo.drivers.php
      * 
      * @param array $aParams
@@ -40,30 +42,32 @@ class checkPdoConnect extends appmonitorcheck{
      *     password            string   password for db user
      *     timeout             integer  optional timeout in sec; default: 5
      * ]
+     * @return array
      */
-    public function run($aParams) {
+    public function run(array $aParams): array
+    {
         $this->_checkArrayKeys($aParams, "connect,user,password");
 
-        try{
+        try {
             $db = new PDO(
-                $aParams['connect'], 
-                $aParams['user'], 
-                $aParams['password'], 
+                $aParams['connect'],
+                $aParams['user'],
+                $aParams['password'],
                 [
                     PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
-                    
+
                     // timeout
                     // Not all drivers support this option; mysqli does
-                    PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp,                  
+                    PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp,
                     // mssql
                     // PDO::SQLSRV_ATTR_QUERY_TIMEOUT => $this->_iTimeoutTcp,  
                 ]
             );
-            $db=null;
+            $db = null;
             return [RESULT_OK, "OK: Database was connected with PDO " . $aParams['connect']];
-        } catch(PDOException $e) {
-            return [RESULT_ERROR, "ERROR: Database was not connected " . $aParams['connect'] . " was not connected. Error ".$e->getMessage()];
+        } catch (PDOException $e) {
+            return [RESULT_ERROR, "ERROR: Database was not connected " . $aParams['connect'] . " was not connected. Error " . $e->getMessage()];
         }
     }
-    
+
 }
diff --git a/public_html/appmonitor/plugins/checks/phpmodules.php b/public_html/appmonitor/plugins/checks/phpmodules.php
index 29e2874b..b4b8170a 100644
--- a/public_html/appmonitor/plugins/checks/phpmodules.php
+++ b/public_html/appmonitor/plugins/checks/phpmodules.php
@@ -18,71 +18,74 @@
  * ____________________________________________________________________________
  * 
  * 2022-05-06  <axel.hahn@iml.unibe.ch>  first lines
+ * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 
  */
-class checkPhpmodules extends appmonitorcheck{
+class checkPhpmodules extends appmonitorcheck
+{
     /**
-     * get default group of this check
-     * @param array   $aParams
-     * @return array
+     * Get default group of this check
+     * @return string
      */
-    public function getGroup(){
+    public function getGroup(): string
+    {
         return 'service';
     }
 
     /**
-     * check if system is listening to a given port
+     * Check if system is listening to a given port
      * @param array $aParams
      * [
      *     required     array  list of required php modules
      *     optional     array  optional: list of optional php modules
      * ]
-     * @return boolean
+     * @return array
      */
-    public function run($aParams) {
-        $sOut='';
-        $bHasError=false;
-        $bHasWarning=false;
+    public function run(array $aParams): array
+    {
+        $sOut = '';
+        $bHasError = false;
+        $bHasWarning = false;
         // $this->_checkArrayKeys($aParams, "required");
 
         // --- get all modules
-        $aAllMods=get_loaded_extensions(false);
-        
+        $aAllMods = get_loaded_extensions(false);
+
         // --- check required modules
-        if(isset($aParams['required']) && count($aParams['required'])){
-            $sOut.='Required: ';
-            foreach($aParams['required'] as $sMod){
-                $sOut.=$sMod.'=';
-                if(!array_search($sMod, $aAllMods)===false){
-                    $sOut.='OK;';
+        if (isset($aParams['required']) && count($aParams['required'])) {
+            $sOut .= 'Required: ';
+            foreach ($aParams['required'] as $sMod) {
+                $sOut .= $sMod . '=';
+                if (!array_search($sMod, $aAllMods) === false) {
+                    $sOut .= 'OK;';
                 } else {
-                    $bHasError=true;
-                    $sOut.='MISS;';
+                    $bHasError = true;
+                    $sOut .= 'MISS;';
                 }
             }
         }
         // --- check optional modules
-        if(isset($aParams['optional']) && count($aParams['optional'])){
-            $sOut.=($sOut ? '|' : '') . 'Optional: ';
-            foreach($aParams['optional'] as $sMod){
-                $sOut.=$sMod.'=';
-                if(!array_search($sMod, $aAllMods)===false){
-                    $sOut.='OK;';
+        if (isset($aParams['optional']) && count($aParams['optional'])) {
+            $sOut .= ($sOut ? '|' : '') . 'Optional: ';
+            foreach ($aParams['optional'] as $sMod) {
+                $sOut .= $sMod . '=';
+                if (!array_search($sMod, $aAllMods) === false) {
+                    $sOut .= 'OK;';
                 } else {
-                    $bHasWarning=true;
-                    $sOut.='MISS;';
+                    $bHasWarning = true;
+                    $sOut .= 'MISS;';
                 }
             }
         }
 
         // --- return result
-        if($bHasError){
+        if ($bHasError) {
             return [RESULT_ERROR, "ERROR: " . $sOut];
         }
-        if($bHasWarning){
+        if ($bHasWarning) {
             return [RESULT_WARNING, "WARNING: " . $sOut];
         }
         return [RESULT_OK, "OK: " . $sOut];
     }
-    
+
 }
diff --git a/public_html/appmonitor/plugins/checks/ping.php b/public_html/appmonitor/plugins/checks/ping.php
index fdb5910f..3056314c 100644
--- a/public_html/appmonitor/plugins/checks/ping.php
+++ b/public_html/appmonitor/plugins/checks/ping.php
@@ -20,40 +20,43 @@
  * 2022-07-05  <axel.hahn@iml.unibe.ch>
  * 2022-09-16  <axel.hahn@iml.unibe.ch>  read error before closing socket.
  * 2022-11-22  <axel.hahn@iml.unibe.ch>  Use exec with detecting MS Win for the ping parameter for count of pings
+ * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  */
-class checkPing extends appmonitorcheck{
+class checkPing extends appmonitorcheck
+{
     /**
-     * get default group of this check
-     * @param array   $aParams
-     * @return array
+     * Get default group of this check
+     * @return string
      */
-    public function getGroup(){
+    public function getGroup(): string
+    {
         return 'network';
     }
 
     /**
-     * check ping to a target
+     * Check ping to a target
      * @param array $aParams
      * [
      *     host                string   optional hostname to connect; default: 127.0.0.1
      *     timeout             integer  OBSOLET (because using exec): optional timeout in sec; default: 5
      * ]
-     * @return boolean
+     * @return array
      */
-    public function run($aParams) {
+    public function run(array $aParams): array
+    {
         $sHost = $aParams['host'] ?? '127.0.0.1';
 
-        $sParamCount=strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? "n" : "c";
-        $iRepeat=1;
-        
-        $sCommand="ping -$sParamCount $iRepeat $sHost 2>&1";
+        $sParamCount = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? "n" : "c";
+        $iRepeat = 1;
+
+        $sCommand = "ping -$sParamCount $iRepeat $sHost 2>&1";
         exec($sCommand, $aOut, $iRc);
-        $sOut=implode("\n", $aOut);
+        $sOut = implode("\n", $aOut);
 
-        if ($iRc>0){
-            return [RESULT_ERROR, "ERROR: ping to $sHost failed.\n".$sOut];
+        if ($iRc > 0) {
+            return [RESULT_ERROR, "ERROR: ping to $sHost failed.\n" . $sOut];
         }
-        return [RESULT_OK, "OK: ping to $sHost\n".$sOut];
+        return [RESULT_OK, "OK: ping to $sHost\n" . $sOut];
 
         /*
             Socket functions require root :-/
diff --git a/public_html/appmonitor/plugins/checks/porttcp.php b/public_html/appmonitor/plugins/checks/porttcp.php
index b9cee468..bd47ce5f 100755
--- a/public_html/appmonitor/plugins/checks/porttcp.php
+++ b/public_html/appmonitor/plugins/checks/porttcp.php
@@ -21,36 +21,39 @@
  * 2022-07-05  <axel.hahn@iml.unibe.ch>  send unknown if socket module is not activated.
  * 2022-09-16  <axel.hahn@iml.unibe.ch>  read error before closing socket.
  * 2022-12-05  <axel.hahn@unibe.ch>      add @ sign at socket functions to prevent warning
+ * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 
  */
-class checkPortTcp extends appmonitorcheck{
+class checkPortTcp extends appmonitorcheck
+{
     /**
-     * get default group of this check
-     * @param array   $aParams
-     * @return array
+     * Get default group of this check
+     * @return string
      */
-    public function getGroup(){
+    public function getGroup(): string
+    {
         return 'network';
     }
 
     /**
-     * check if system is listening to a given port
+     * Check if system is listening to a given port
      * @param array $aParams
      * [
      *     port                integer  port
      *     host                string   optional hostname to connect; default: 127.0.0.1
      *     timeout             integer  optional timeout in sec; default: 5
      * ]
-     * @return boolean
+     * @return array
      */
-    public function run($aParams) {
+    public function run(array $aParams): array
+    {
         $this->_checkArrayKeys($aParams, "port");
 
         $sHost = $aParams['host'] ?? '127.0.0.1';
         $iPort = (int) $aParams['port'];
 
-        if (!function_exists('socket_create')){
-            return [RESULT_UNKNOWN, "UNKNOWN: Unable to perform tcp test. The socket module is not enabled in the php installation."];
+        if (!function_exists('socket_create')) {
+            return [RESULT_UNKNOWN, "UNKNOWN: Unable to perform tcp test. The php-sockets module is not enabled in the php installation."];
         }
 
         // from http://php.net/manual/de/sockets.examples.php
@@ -65,14 +68,14 @@ class checkPortTcp extends appmonitorcheck{
             SOL_SOCKET,  // socket level
             SO_SNDTIMEO, // timeout option
             [
-              "sec"=>(isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp, // timeout in seconds
-              "usec"=>0
+                "sec" => (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp, // timeout in seconds
+                "usec" => 0
             ]
         );
 
         $result = @socket_connect($socket, $sHost, $iPort);
         if ($result === false) {
-            $aResult=[RESULT_ERROR, "ERROR: $sHost:$iPort failed. " . socket_strerror(socket_last_error($socket))];
+            $aResult = [RESULT_ERROR, "ERROR: $sHost:$iPort failed. " . socket_strerror(socket_last_error($socket))];
             socket_close($socket);
             return $aResult;
         } else {
@@ -80,5 +83,5 @@ class checkPortTcp extends appmonitorcheck{
             return [RESULT_OK, "OK: $sHost:$iPort was connected."];
         }
     }
-    
+
 }
diff --git a/public_html/appmonitor/plugins/checks/simple.php b/public_html/appmonitor/plugins/checks/simple.php
index daacf0a3..3af69b69 100755
--- a/public_html/appmonitor/plugins/checks/simple.php
+++ b/public_html/appmonitor/plugins/checks/simple.php
@@ -18,12 +18,14 @@
  * ____________________________________________________________________________
  * 
  * 2021-10-27  <axel.hahn@iml.unibe.ch>
+ * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 
  */
-class checkSimple extends appmonitorcheck{
-    
+class checkSimple extends appmonitorcheck
+{
+
     /**
-     * most simple check: set given values
+     * Most simple check: set given values
      * Use this function to add a counter
      * 
      * @param array $aParams
@@ -37,15 +39,16 @@ class checkSimple extends appmonitorcheck{
      *         - label         string   a label
      *         - value         float    a number
      *         - type          string   one of simple | bar | line
-     * 
+     * @return array
      */
-    public function run($aParams) {
+    public function run(array $aParams): array
+    {
         $this->_checkArrayKeys($aParams, "result,value");
         // $this->_setReturn((int) $aParams["result"], $aParams["value"]);
-        $aData=[];
-        foreach([ 'type', 'count', 'visual' ] as $sMyKey){
-            if(isset($aParams[$sMyKey])){
-                $aData[$sMyKey]=$aParams[$sMyKey];
+        $aData = [];
+        foreach (['type', 'count', 'visual'] as $sMyKey) {
+            if (isset($aParams[$sMyKey])) {
+                $aData[$sMyKey] = $aParams[$sMyKey];
             }
         }
         return [
diff --git a/public_html/appmonitor/plugins/checks/sqliteconnect.php b/public_html/appmonitor/plugins/checks/sqliteconnect.php
index 1fc264c5..703db60d 100755
--- a/public_html/appmonitor/plugins/checks/sqliteconnect.php
+++ b/public_html/appmonitor/plugins/checks/sqliteconnect.php
@@ -18,15 +18,17 @@
  * ____________________________________________________________________________
  * 
  * 2021-10-27  <axel.hahn@iml.unibe.ch>
+ * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 
  */
-class checkSqliteConnect extends appmonitorcheck{
+class checkSqliteConnect extends appmonitorcheck
+{
     /**
-     * get default group of this check
-     * @param array   $aParams
-     * @return array
+     * Get default group of this check
+     * @return string
      */
-    public function getGroup(){
+    public function getGroup()
+    {
         return 'database';
     }
 
@@ -37,33 +39,44 @@ class checkSqliteConnect extends appmonitorcheck{
      *     db                  string   full path of sqlite file 
      *     timeout             integer  optional timeout in sec; default: 5
      * ]
-     * @return boolean
+     * @return array
      */
-    public function run($aParams) {
+    public function run($aParams): array
+    {
         $this->_checkArrayKeys($aParams, "db");
         if (!file_exists($aParams["db"])) {
-            return [RESULT_ERROR, "ERROR: Sqlite database file " . $aParams["db"] . " does not exist."];
+            return [
+                RESULT_ERROR,
+                "ERROR: Sqlite database file " . $aParams["db"] . " does not exist."
+            ];
         }
-        if(!isset($aParams['user'])){
-            $aParams['user']='';
+        if (!isset($aParams['user'])) {
+            $aParams['user'] = '';
         }
-        if(!isset($aParams['password'])){
-            $aParams['password']='';
+        if (!isset($aParams['password'])) {
+            $aParams['password'] = '';
         }
         try {
             // $db = new SQLite3($sqliteDB);
             // $db = new PDO("sqlite:".$sqliteDB);
-            $o = new PDO("sqlite:" . $aParams["db"],
-                $aParams['user'], 
-                $aParams['password'], 
+            $o = new PDO(
+                "sqlite:" . $aParams["db"],
+                $aParams['user'],
+                $aParams['password'],
                 [
-                    PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp,                  
+                    PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp,
                 ]
             );
-            return [RESULT_OK, "OK: Sqlite database " . $aParams["db"] . " was connected"];
+            return [
+                RESULT_OK,
+                "OK: Sqlite database " . $aParams["db"] . " was connected"
+            ];
         } catch (Exception $e) {
-            return [RESULT_ERROR, "ERROR: Sqlite database " . $aParams["db"] . " was not connected. " . $e->getMessage()];
+            return [
+                RESULT_ERROR, 
+                "ERROR: Sqlite database " . $aParams["db"] . " was not connected. " . $e->getMessage()
+            ];
         }
     }
-    
+
 }
-- 
GitLab