Select Git revision
valuestore.class.php 19.11 KiB
<?php
/**
* value store for versions for the deployment tool
*
* EXAMPLES:
*
* get versions of a phase
* $oVersion = new valuestore();
* $oVersion->setProject("", $this->_aPrjConfig["fileprefix"], $sPhase, $sPlace);
* $aVersions=$oVersion->getVersion();
*
* set versions of a host
* $oVersion = new valuestore();
* $oVersion->setProject("", $$sPackage, $sPhase, $sPlace);
* $oVersion->updateVar($sVarname, $sValue)
*
* delete
* $oVersion = new valuestore();
*
* to delete values for a single place:
* $oVersion->setProject("", $this->_aPrjConfig["fileprefix"], $sPhase, $sPlace);
* $aVersions=$oVersion->deleteValues();
*
* to delete all version items for a whole whole project (packages) in all phases
* $oVersion->setProject("", $this->_aPrjConfig["fileprefix"]);
* $aVersions=$oVersion->deleteValues("version");
*
* @author hahn
*/
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;
/**
* filename of sqlite database file
* @var type
*/
private $_dbfile = false;
/**
* database connection
* @var object
*/
protected $_oDB = false;
/**
* TTL vor stored values - 1 day [sec]
* @var integer
*/
protected $_iTTL = 86400;
/**
* create statement for the database
* @var type
*/
private $_sCreate = '
CREATE TABLE "values" (
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE ,
`time` DATETIME,
`project` TEXT,
`package` TEXT,
`phase` TEXT,
`place` TEXT,
`host` TEXT,
`variable` TEXT,
`data` TEXT,
UNIQUE (project, package, phase, place, host, variable) ON CONFLICT REPLACE
);'
;
// hardcoded
protected $_allowedPhases=array('preview', 'stage', 'live');
protected $_allowedPlaces=array('onhold', 'ready2install', 'deployed');
// ----------------------------------------------------------------------
// CONSTRUCTOR
// ----------------------------------------------------------------------
/**
* constructor ... no params
*/
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
* @global object $oLog
* @param string $sMessage messeage text
* @param string $sLevel warnlevel of the given message
* @return bool
*/
private function log($sMessage, $sLevel = "info") {
global $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
*/
private function _createDb() {
if (file_exists($this->_dbfile)) {
echo $this->_bDebug ? "removing existing file $this->_dbfile ...<br>\n" : '';
unlink($this->_dbfile);
}
// after deleting the db file we nee to re instantiate
echo $this->_bDebug ? "create database as file $this->_dbfile ...<br>\n" : '';
$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);
}
return true;
}
/**
* execute a sql statement
* @param string $sSql sql statement
* @param array $aVars array with values (uses PDO::prepare(); $sSql must contain placeholders :key)
* @return database object
*/
private function _makeQuery($sSql, $aVars=false) {
// $this->_log(__FUNCTION__."($sSql)");
// echo "DEBUG: executing SQL<pre>$sSql</pre>";
$this->log("start query");
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');
}
$this->log("end query - ".$sSql);
return $result;
}
/**
* execute a sql statement
* @param string $sSql sql statement
* @return array of resultset
*/
private function _makeSelectQuery($sSql, $aKey=false) {
// $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();
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);
return $aReturn;
}
/**
* 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
*/
private function _quit($sFunction, $sMessage){
error_log(__CLASS__ . "::$sFunction - $sMessage " . "whereiam: " . print_r($this->whereiam(), 1));
if ($this->_bDebug){
echo __CLASS__ . "::$sFunction stopped.<br>\n"
. "whereiam: <pre>" . print_r($this->whereiam(), 1)."</pre>"
;
die($sMessage);
} else {
die("ERROR ... wrong usage of class ". __CLASS__);
}
return false;
}
// ----------------------------------------------------------------------
// PUBLIC GETTER
// ----------------------------------------------------------------------
/**
* get list of current projects
* @return type
*/
public function getProjects(){
$sSql="select distinct(project) from `values`";
return $this->_makeSelectQuery($sSql, 'project');
}
/**
* get list of current projects
* @return type
*/
public function getPackages(){
$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
*/
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.");
}
$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
public function getPlaces(){
if (!$this->sProject || !$this->sPhase){
$this->_quit(__FUNCTION__ , "ERROR: you need to set a project, and phase first. Use the setProject() method to do so.");
}
$sWhere='1=1 '
. ($this->sProject ? "AND project='" . $this->sProject . "' " : "")
. ($this->sPackage ? "AND phase='" . $this->sPackage . "' " : "")
;
$sSql="select distinct(phase) from `values` WHERE $sWhere";
$sSql="select distinct(place) from `values`
WHERE
project='" . $this->sProject . "'
AND phase='" . $this->sPhase . "'
";
return $this->_makeSelectQuery($sSql, 'place');
}
*/
/**
* get hosts that have installed a project
* @return type
*/
public function getHosts(){
$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";
return $this->_makeSelectQuery($sSql, 'host');
}
/**
* 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";
return $this->_makeSelectQuery($sSql);
}
/**
* 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');
// 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;
}
}
return $aData;
}
/**
* return currebntly 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,
);
}
/**
* return currebntly set project, phase, place and host
* @return type
*/
public function dumpdb(){
$sSql="select * from `values`";
return $this->_makeSelectQuery($sSql);
}
// ----------------------------------------------------------------------
// PUBLIC SETTER
// ----------------------------------------------------------------------
/**
* 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
*/
public function setProject($sProject, $sPackage, $sPhase=false, $sPlace=false, $sHost=false){
$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)
* @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
*/
public function setPackage($sPackage, $sPhase, $sPlace=false, $sHost=false){
$this->sPackage=preg_replace('/[^a-z\-\_0-9]/', '' ,$sPackage);
$this->setPhase($sPhase, $sPlace, $sHost);
return $this->sPackage;
}
/**
* 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)
* @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.");
return false;
}
if ($sPhase && array_search($sPhase, $this->_allowedPhases)===false){
$this->_quit(__FUNCTION__ , "ERROR: you set a wrong phase [$sPhase]");
}
$this->sPhase=$sPhase;
$this->setPlace($sPlace, $sHost);
return $this->sPhase;
}
/**
* set a place (and host)
* @see setProject()
*
* @param string $sPlace place (onhold|ready2install|deployed)
* @param string $sHost hostname (for place deployed)
* @return type string
*/
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.");
return false;
}
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]");
}
$this->sPlace=$sPlace;
$this->setHost($sHost);
return $this->sPlace;
}
/**
* set a host for place "deployed"
* @see setProject()
*
* @param string $sHost hostname
* @return type string
*/
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.");
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: on place [deployed] a host MUST be set.");
}
*/
$this->sHost=preg_replace('/[^a-z\.\-0-9]/', '', $sHost);
return $this->sHost;
}
// ----------------------------------------------------------------------
// DATABASE FUNCTIONS
// ----------------------------------------------------------------------
/**
* cleanup value store
* @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;
}
$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
* project or package and optional phase, place, host. Then call this delete
* method.
* @param string $sVariable optional: limit deletion to a given variable
* @return boolean
*/
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.");
}
$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
*/
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.");
}
$this->cleanup();
$sSql="
INSERT into `values` (`time`, `project`, `package`, `phase`, `place`, `host`, `variable`, `data` )
VALUES (
'" . date("Y-m-d H:i:s") . "',
'" . $this->sProject . "',
'" . $this->sPackage . "',
'" . $this->sPhase . "',
'" . $this->sPlace . "',
'" . $this->sHost . "',
:variable,
:value
);
";
return $this->_makeQuery(
$sSql,
array(
'variable'=>$sVarname,
'value'=>$sValue,
)
);
}
/**
* update a version value
* @param type $sVersioninfos
* @return boolean
*/
public function updateVersion($sVersioninfos){
return $this->updateVar('version', $sVersioninfos);
}
}