diff --git a/public_html/valuestore/classes/valuestore.class.php b/public_html/valuestore/classes/valuestore.class.php index 8110ba3824b292f61d758d71c6d59e530a441bdc..cb7b5b39a5b427bcd9c3729ac345c9f7f0b34eb4 100644 --- a/public_html/valuestore/classes/valuestore.class.php +++ b/public_html/valuestore/classes/valuestore.class.php @@ -26,40 +26,45 @@ * $aVersions=$oVersion->deleteValues("version"); * * @author hahn + * + * Axel: <axel.hahn@unibe.ch> + * (...) + * 2024-08-29 Axel php8 only; added variable types; short array syntax */ -class valuestore { - - public $sProject = false; - public $sPackage = false; - public $sPhase = false; - public $sPlace = false; - public $sHost = false; - public $sVariable = false; - public $sData = false; - - protected $_bDebug = true; +class valuestore +{ + + public string $sProject = ''; + public string $sPackage = ''; + public string $sPhase = ''; + public string $sPlace = ''; + public string $sHost = ''; + public string $sVariable = ''; + public string $sData = ''; + + protected bool $_bDebug = true; /** * filename of sqlite database file - * @var type + * @var string */ - private $_dbfile = false; + private string $_dbfile = ''; /** * database connection * @var object */ - protected $_oDB = false; + protected object $_oDB; /** * TTL vor stored values - 1 day [sec] * @var integer */ - protected $_iTTL = 86400; + protected int $_iTTL = 86400; /** * create statement for the database - * @var type + * @var string */ private $_sCreate = ' CREATE TABLE "values" ( @@ -75,51 +80,58 @@ class valuestore { UNIQUE (project, package, phase, place, host, variable) ON CONFLICT REPLACE );' ; - + // hardcoded - protected $_allowedPhases=array('preview', 'stage', 'live'); - protected $_allowedPlaces=array('onhold', 'ready2install', 'deployed'); - + protected array $_allowedPhases = ['preview', 'stage', 'live']; + protected array $_allowedPlaces = ['onhold', 'ready2install', 'deployed']; + // ---------------------------------------------------------------------- // CONSTRUCTOR // ---------------------------------------------------------------------- - + /** - * constructor ... no params + * Constructor ... no params */ - public function __construct(){ - + public function __construct() + { + // cache dir is hardcoded to versions directory :-/ $this->_dbfile = __DIR__ . '/../data/versioncache.db'; - + $this->_oDB = new PDO("sqlite:" . $this->_dbfile); if (!file_exists($this->_dbfile) || !filesize($this->_dbfile)) { $this->_createDb(); } } + /** - * add a log messsage + * Add a logging messsage with ahlogger + * * @global object $oLog + * * @param string $sMessage messeage text * @param string $sLevel warnlevel of the given message * @return bool */ - private function log($sMessage, $sLevel = "info") { + private function log($sMessage, $sLevel = "info"): bool + { global $oCLog; - if($oCLog){ + if ($oCLog) { return $oCLog->add(basename(__FILE__) . " class " . __CLASS__ . " - " . $sMessage, $sLevel); } return false; } - + // ---------------------------------------------------------------------- // PRIVATE // ---------------------------------------------------------------------- /** * create sqlite database - called in constructor if the file does not exist + * @return boolean */ - private function _createDb() { + private function _createDb(): bool + { if (file_exists($this->_dbfile)) { echo $this->_bDebug ? "removing existing file $this->_dbfile ...<br>\n" : ''; unlink($this->_dbfile); @@ -129,116 +141,127 @@ class valuestore { $this->_oDB = new PDO("sqlite:" . $this->_dbfile); $this->_makeQuery($this->_sCreate); if (!file_exists($this->_dbfile)) { - $this->_quit(__FUNCTION__ , "ERROR: unable to create sqlite database " . $this->_dbfile); + $this->_quit(__FUNCTION__, "ERROR: unable to create sqlite database " . $this->_dbfile); } return true; } /** - * execute a sql statement + * Execute a sql statement + * It returns false if the query failed. + * On success you get a PDO result object + * * @param string $sSql sql statement * @param array $aVars array with values (uses PDO::prepare(); $sSql must contain placeholders :key) - * @return database object + * @return bool|object result object of query */ - private function _makeQuery($sSql, $aVars=false) { + private function _makeQuery(string $sSql, array $aVars = []): bool|object + { // $this->_log(__FUNCTION__."($sSql)"); // echo "DEBUG: executing SQL<pre>$sSql</pre>"; $this->log("start query"); - if ($aVars && is_array($aVars)){ + if ($aVars && is_array($aVars)) { $oStatement = $this->_oDB->prepare($sSql); $result = $oStatement->execute($aVars); } else { $result = $this->_oDB->query($sSql); } - if(!$result){ - $this->log('<pre>'.print_r($this->_oDB->errorInfo()).'</pre>', 'error'); + if (!$result) { + $this->log('<pre>' . print_r($this->_oDB->errorInfo()) . '</pre>', 'error'); } - $this->log("end query - ".$sSql); + $this->log("end query - " . $sSql); return $result; } - + /** - * execute a sql statement - * @param string $sSql sql statement + * Execute a sql SELECT statement and get an array with results + * + * @param string $sSql sql statement + * @param string $sKey optional: return only values of this key * @return array of resultset */ - private function _makeSelectQuery($sSql, $aKey=false) { + private function _makeSelectQuery(string $sSql, string $sKey = ''): array + { // $this->_log(__FUNCTION__."($sSql)"); // echo "DEBUG: executing select SQL<pre>$sSql</pre>"; $this->log("start query"); $oStatement = $this->_oDB->prepare($sSql); $oStatement->execute(); - $aReturn=array(); + $aReturn = []; while ($row = $oStatement->fetch(PDO::FETCH_ASSOC)) { - if ($aKey && array_key_exists($aKey, $row)){ - $aReturn[] = $row[$aKey]; - } else { - $aReturn[] = $row; - } - } - $this->log("end query - ".$sSql); + if ($sKey && array_key_exists($sKey, $row)) { + $aReturn[] = $row[$sKey]; + } else { + $aReturn[] = $row; + } + } + $this->log("end query - " . $sSql); return $aReturn; } /** - * log error and quit. it echoes the error message on screen if debug + * Log error and quit. it echoes the error message on screen if debug * is enabled. + * * @param string $sFunction name of method that throws the error * @param string $sMessage error message - * @return boolean + * @return void */ - private function _quit($sFunction, $sMessage){ + private function _quit(string $sFunction, string $sMessage): void + { error_log(__CLASS__ . "::$sFunction - $sMessage " . "whereiam: " . print_r($this->whereiam(), 1)); - if ($this->_bDebug){ + if ($this->_bDebug) { echo __CLASS__ . "::$sFunction stopped.<br>\n" - . "whereiam: <pre>" . print_r($this->whereiam(), 1)."</pre>" - ; + . "whereiam: <pre>" . print_r($this->whereiam(), 1) . "</pre>" + ; die($sMessage); } else { - die("ERROR ... wrong usage of class ". __CLASS__); + die("ERROR ... wrong usage of class " . __CLASS__); } - return false; } // ---------------------------------------------------------------------- // PUBLIC GETTER // ---------------------------------------------------------------------- - - + + /** - * get list of current projects - * @return type + * Get list of current projects + * @return array */ - public function getProjects(){ - $sSql="select distinct(project) from `values`"; + public function getProjects(): array + { + $sSql = "select distinct(project) from `values`"; return $this->_makeSelectQuery($sSql, 'project'); } - + /** - * get list of current projects - * @return type + * Get list of current projects + * @return array */ - public function getPackages(){ - $sSql="select distinct(package) from `values`"; + public function getPackages(): array + { + $sSql = "select distinct(package) from `values`"; return $this->_makeSelectQuery($sSql, 'package'); } - + /** - * get phases of the current project; a project must be set be set before - * @return type + * Get phases of the current project; a project must be set be set before + * @return array */ - public function getPhases(){ - if (!$this->sProject && !$this->sPackage){ - $this->_quit(__FUNCTION__ , "you need to set a project first. Use the setProject() method to do so."); + public function getPhases(): array + { + if (!$this->sProject && !$this->sPackage) { + $this->_quit(__FUNCTION__, "you need to set a project first. Use the setProject() method to do so."); } - $sWhere='1=1 ' - . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") - . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") - ; - $sSql="select distinct(phase) from `values` WHERE $sWhere"; - return $this->_makeSelectQuery($sSql,'phase'); + $sWhere = '1=1 ' + . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") + . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") + ; + $sSql = "select distinct(phase) from `values` WHERE $sWhere"; + return $this->_makeSelectQuery($sSql, 'phase'); } - + /** * get places of the current project; a project and must be set be set before * @return type @@ -259,254 +282,286 @@ class valuestore { return $this->_makeSelectQuery($sSql, 'place'); } */ - + /** * get hosts that have installed a project - * @return type + * @return array */ - public function getHosts(){ - $sWhere='1=1 ' - . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") - . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") - . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") + public function getHosts(): array + { + $sWhere = '1=1 ' + . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") + . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") + . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") . "AND place='deployed' " //. "AND host>'' " - ; - $sSql="select distinct(host) from `values` WHERE $sWhere"; + ; + $sSql = "select distinct(host) from `values` WHERE $sWhere"; return $this->_makeSelectQuery($sSql, 'host'); } /** - * get entries of the current place (project, phase and place can be + * Get entries of the current place (project, phase and place can be * set before) + * * @see setProject() + * * @param string $sVariable variable for filtering column "variable" * @return array */ - public function getVar($sVariable=''){ - $sWhere='1=1 ' - . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") - . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") - . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") - . ($this->sPlace ? "AND place='" . $this->sPlace . "' " : "") - . ($this->sHost ? "AND host='" . $this->sHost . "' " : "") - . ($sVariable ? "AND variable='" . $sVariable . "' " : "") - ; - $sSql="select * from `values` WHERE $sWhere"; + public function getVar($sVariable = ''): array + { + $sWhere = '1=1 ' + . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") + . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") + . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") + . ($this->sPlace ? "AND place='" . $this->sPlace . "' " : "") + . ($this->sHost ? "AND host='" . $this->sHost . "' " : "") + . ($sVariable ? "AND variable='" . $sVariable . "' " : "") + ; + $sSql = "select * from `values` WHERE $sWhere"; return $this->_makeSelectQuery($sSql); } - + /** - * get versions of the current place (project, phase and place can be + * Get versions of the current place (project, phase and place can be * set before) + * * @see setProject() + * * @return array */ - public function getVersion(){ - $aData=$this->getVar('version'); - + public function getVersion(): array + { + $aData = $this->getVar('version'); + // get json in subkey host -> data and store keys in host -> _data - if($aData && is_array($aData) && count($aData)){ - foreach($aData as $iKey=>$aHostinfos){ - $aJson=json_decode($aHostinfos['data'], 1); - $aData[$iKey]['_data']=$aJson; + if ($aData && is_array($aData) && count($aData)) { + foreach ($aData as $iKey => $aHostinfos) { + $aJson = json_decode($aHostinfos['data'], 1); + $aData[$iKey]['_data'] = $aJson; } } return $aData; } - + /** - * return currebntly set project, phase, place and host + * Get currently set project, phase, place and host * @return array */ - public function whereiam(){ - return array( - 'project'=>$this->sProject, - 'package'=>$this->sPackage, - 'phase'=>$this->sPhase, - 'place'=>$this->sPlace, - 'host'=>$this->sHost, - ); + public function whereiam(): array + { + return [ + 'project' => $this->sProject, + 'package' => $this->sPackage, + 'phase' => $this->sPhase, + 'place' => $this->sPlace, + 'host' => $this->sHost, + ]; } + /** - * return currebntly set project, phase, place and host - * @return type + * Get all values in valuestore as array + * @return array */ - public function dumpdb(){ - $sSql="select * from `values`"; + public function dumpdb(): array + { + $sSql = "select * from `values`"; return $this->_makeSelectQuery($sSql); } - - + + // ---------------------------------------------------------------------- // PUBLIC SETTER // ---------------------------------------------------------------------- - + /** - * init a "position" before getting or updating deleting a value + * Init a "position" before getting or updating deleting a value * * @param string $sProject project id * @param string $sPackage package id - * @param string $sPhase phase (preview|stage|live) - * @param string $sPlace place (onhold|ready2install|deployed) - * @param string $sHost hostname (for place deployed) - * @return type string + * @param string $sPhase optional: phase (preview|stage|live) + * @param string $sPlace optional: place (onhold|ready2install|deployed) + * @param string $sHost optional: hostname (for place deployed) + * @return string */ - public function setProject($sProject, $sPackage, $sPhase=false, $sPlace=false, $sHost=false){ - $this->sProject=preg_replace('/[^a-z\-\_0-9]/', '' ,$sProject); + public function setProject(string $sProject, string $sPackage, string $sPhase = '', string $sPlace = '', $sHost = ''): string + { + $this->sProject = preg_replace('/[^a-z\-\_0-9]/', '', $sProject); $this->setPackage($sPackage, $sPhase, $sPlace, $sHost); return $this->sProject; } /** - * set a package (and more granular items) + * Set a package (and more granular items) * @see setProject() * * @param string $sPackage package id * @param string $sPhase phase (preview|stage|live) - * @param string $sPlace place (onhold|ready2install|deployed) - * @param string $sHost hostname (for place deployed) - * @return type string + * @param string $sPlace optional: place (onhold|ready2install|deployed) + * @param string $sHost optional: hostname (for place deployed) + * @return string */ - public function setPackage($sPackage, $sPhase, $sPlace=false, $sHost=false){ - $this->sPackage=preg_replace('/[^a-z\-\_0-9]/', '' ,$sPackage); + public function setPackage(string $sPackage, string $sPhase, string $sPlace = '', string $sHost = ''): string + { + $this->sPackage = preg_replace('/[^a-z\-\_0-9]/', '', $sPackage); $this->setPhase($sPhase, $sPlace, $sHost); return $this->sPackage; } - + /** - * set a phase (and more granular items) + * Set a phase (and more granular items) * @see setProject() * * @param string $sPhase phase (preview|stage|live) - * @param string $sPlace place (onhold|ready2install|deployed) - * @param string $sHost hostname (for place deployed) + * @param string $sPlace optional: place (onhold|ready2install|deployed) + * @param string $sHost optional: hostname (for place deployed) * @return type string */ - public function setPhase($sPhase, $sPlace=false, $sHost=false){ - if (!$this->sProject && !$this->sPackage){ - $this->_quit(__FUNCTION__ , "ERROR: you need to set a project. Use the setProject() method to do so."); + public function setPhase(string $sPhase, string $sPlace = '', string $sHost = ''): string + { + if (!$this->sProject && !$this->sPackage) { + $this->_quit(__FUNCTION__, "ERROR: you need to set a project. Use the setProject() method to do so."); return false; } - if ($sPhase && array_search($sPhase, $this->_allowedPhases)===false){ - $this->_quit(__FUNCTION__ , "ERROR: you set a wrong phase [$sPhase]"); + if ($sPhase && array_search($sPhase, $this->_allowedPhases) === false) { + $this->_quit(__FUNCTION__, "ERROR: you set a wrong phase [$sPhase]"); } - $this->sPhase=$sPhase; + $this->sPhase = $sPhase; $this->setPlace($sPlace, $sHost); return $this->sPhase; } /** - * set a place (and host) + * Set a place (and optional a host) + * It aborts if project and package are not set + * * @see setProject() * * @param string $sPlace place (onhold|ready2install|deployed) - * @param string $sHost hostname (for place deployed) - * @return type string + * @param string $sHost optional: hostname (for place deployed) + * @return bool */ - public function setPlace($sPlace, $sHost=false){ - if ((!$this->sProject && !$this->sPackage)){ - $this->_quit(__FUNCTION__ , "ERROR: you need to set a project and phase. Use the setProject() method to do so."); + public function setPlace($sPlace, string $sHost = ''): bool + { + if ((!$this->sProject && !$this->sPackage)) { + $this->_quit(__FUNCTION__, "ERROR: you need to set a project and phase. Use the setProject() method to do so."); return false; } - if ($sPlace && !$this->sPhase){ - $this->_quit(__FUNCTION__ , "ERROR: you cannot set place [$sPlace] with leaving phase empty."); + if ($sPlace && !$this->sPhase) { + $this->_quit(__FUNCTION__, "ERROR: you cannot set place [$sPlace] with leaving phase empty."); } - if ($sPlace && array_search($sPlace, $this->_allowedPlaces)===false){ - $this->_quit(__FUNCTION__ , "ERROR: you set a wrong place [$sPlace]"); + if ($sPlace && array_search($sPlace, $this->_allowedPlaces) === false) { + $this->_quit(__FUNCTION__, "ERROR: you set a wrong place [$sPlace]"); } - $this->sPlace=$sPlace; + $this->sPlace = $sPlace; $this->setHost($sHost); - return $this->sPlace; + return true; } - + /** - * set a host for place "deployed" + * Set a host for place "deployed". + * It aborts if project and package are not set + * * @see setProject() * * @param string $sHost hostname - * @return type string + * @return bool */ - public function setHost($sHost=false){ - if ((!$this->sProject && !$this->sPackage)){ - $this->_quit(__FUNCTION__ , "ERROR: you need to set a project, phase and place. Use the setProject() method to do so."); + public function setHost($sHost = ''): bool + { + if ((!$this->sProject && !$this->sPackage)) { + $this->_quit(__FUNCTION__, "ERROR: you need to set a project, phase and place. Use the setProject() method to do so."); return false; } - if($sHost && $this->sPlace!=='deployed'){ - $this->_quit(__FUNCTION__ , "ERROR: a host can be set on place [deployed] only."); + if ($sHost && $this->sPlace !== 'deployed') { + $this->_quit(__FUNCTION__, "ERROR: a host can be set on place [deployed] only."); } /* if(!$sHost && $this->sPlace==='deployed'){ $this->_quit(__FUNCTION__ , "ERROR: on place [deployed] a host MUST be set."); } */ - $this->sHost=preg_replace('/[^a-z\.\-0-9]/', '', $sHost); - return $this->sHost; + $this->sHost = preg_replace('/[^a-z\.\-0-9]/', '', $sHost); + return true; } - + // ---------------------------------------------------------------------- // DATABASE FUNCTIONS // ---------------------------------------------------------------------- - + /** - * cleanup value store + * Cleanup value store: delete entries older a given ttl value in seconds + * Afterwards a "vacuum" command will be started. + * * @param integer $iTtl optional: max age in seconds of items to keep; 0 to delete all; default is 1 day * @return boolean */ - public function cleanup($iTtl=false){ - if ($iTtl===false){ - $iTtl=$this->_iTTL; + public function cleanup(int $iTtl = -1): bool + { + if ($iTtl === -1) { + $iTtl = $this->_iTTL; } - $sTtlDate=date("Y-m-d H:i:s", date("U") - (int)$iTtl ); - $sSql="DELETE FROM `values` WHERE `time` < '$sTtlDate' " - . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") - . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") - . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") - . ($this->sPlace ? "AND place='" . $this->sPlace . "' " : "") - . ($this->sHost ? "AND host='" . $this->sHost . "' " : "") - ; + $sTtlDate = date("Y-m-d H:i:s", date("U") - (int) $iTtl); + $sSql = "DELETE FROM `values` WHERE `time` < '$sTtlDate' " + . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") + . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") + . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") + . ($this->sPlace ? "AND place='" . $this->sPlace . "' " : "") + . ($this->sHost ? "AND host='" . $this->sHost . "' " : "") + ; // die("$iTtl ... $sSql ... ABORT"); $this->_makeQuery($sSql); $this->_makeQuery("vacuum;"); return true; } - + /** - * delete values from value store. You need to call setProject() to set + * Delete values from value store. You need to call setProject() to set * project or package and optional phase, place, host. Then call this delete * method. + * It returns a PDO result object. + * It aborts if project and package are not set + * It returns false on database query error + * * @param string $sVariable optional: limit deletion to a given variable - * @return boolean + * @return boolean|object */ - public function deleteValues($sVariable){ - if ((!$this->sProject && !$this->sPackage) ){ - $this->_quit(__FUNCTION__ , "ERROR: you need to set a project, phase and place. use the setProject() method to do so."); + public function deleteValues($sVariable): bool|object + { + if ((!$this->sProject && !$this->sPackage)) { + $this->_quit(__FUNCTION__, "ERROR: you need to set a project, phase and place. use the setProject() method to do so."); } - $sWhere='1=1 ' - . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") - . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") - . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") - . ($this->sPlace ? "AND place='" . $this->sPlace . "' " : "") - . ($this->sHost ? "AND host='" . $this->sHost . "' " : "") - . ($sVariable ? "AND variable='" . $sVariable . "' " : "") - ; - $sSql="DELETE FROM `values` WHERE $sWhere"; + $sWhere = '1=1 ' + . ($this->sProject ? "AND project='" . $this->sProject . "' " : "") + . ($this->sPackage ? "AND package='" . $this->sPackage . "' " : "") + . ($this->sPhase ? "AND phase='" . $this->sPhase . "' " : "") + . ($this->sPlace ? "AND place='" . $this->sPlace . "' " : "") + . ($this->sHost ? "AND host='" . $this->sHost . "' " : "") + . ($sVariable ? "AND variable='" . $sVariable . "' " : "") + ; + $sSql = "DELETE FROM `values` WHERE $sWhere"; // echo $sSql; // return true; return $this->_makeQuery($sSql); } - + /** - * update a version - * @return boolean + * Update a version informatio in value store. + * It returns a PDO result object. + * It aborts if project and package are not set + * It returns false on database query error + * + * @return boolean|object */ - public function updateVar($sVarname,$sValue){ - if ((!$this->sProject && !$this->sPackage) || !$this->sPhase || !$this->sPlace ){ - $this->_quit(__FUNCTION__ , "ERROR: you need to set a project, phase and place. use the setProject() method to do so."); + public function updateVar($sVarname, $sValue): bool|object + { + if ((!$this->sProject && !$this->sPackage) || !$this->sPhase || !$this->sPlace) { + $this->_quit(__FUNCTION__, "ERROR: you need to set a project, phase and place. use the setProject() method to do so."); } $this->cleanup(); - $sSql=" + $sSql = " INSERT into `values` (`time`, `project`, `package`, `phase`, `place`, `host`, `variable`, `data` ) VALUES ( '" . date("Y-m-d H:i:s") . "', @@ -520,11 +575,11 @@ class valuestore { ); "; return $this->_makeQuery( - $sSql, - array( - 'variable'=>$sVarname, - 'value'=>$sValue, - ) + $sSql, + [ + 'variable' => $sVarname, + 'value' => $sValue, + ] ); } /** @@ -532,8 +587,9 @@ class valuestore { * @param type $sVersioninfos * @return boolean */ - public function updateVersion($sVersioninfos){ + public function updateVersion($sVersioninfos) + { return $this->updateVar('version', $sVersioninfos); } - + }