<?php
/*
* SCROTT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to UNLICENSE
*/
require_once "class/database.class.php";
/*
* This class provides functionality for retrieving data from and updating
* data in a database table.
*/
abstract class table
{
/*
* This is an array acting as a dictionary, mapping a table name (key)
* into another array acting as a list (value) of the fields in that
* table. Classes that extend this class, should append a key=>val to
* this array that represents the table being modeled. This array is
* used by db table IO functions to construct SQL queries.
*/
protected $fields = array();
/*
* Instanciate an object representing an existing database entity
* named by the given GUID. If no such GUID exists, an exception
* is thrown. If NULL is passed, no database lookups are attempted.
* This is helpful for assembling new database objects.
*/
public function __construct(?string $guid = NULL)
{
if ($guid)
$this->loadObj($guid);
}
/*
* This function will iterate the $fields array and initialize this
* object's class properties based on the table=>fields mapping. If
* no such GUID exists, an exception is thrown.
*/
private function loadObj(string $guid) : void
{
$guid = database::esc($guid);
if (!self::isGUID($guid))
throw new Exception("GUID " . $guid . " does not exist");
foreach ($this->fields as $tbl => $flds)
{
$tbl = database::esc($tbl);
$query = "SELECT * FROM " . $tbl . " WHERE guid = '" . $guid . "'";
$res = database::query($query)[0];
foreach ($flds as $fld)
{
if (isset($res[$fld]))
$this->$fld = $res[$fld];
}
}
}
/*
* This function uses loadObj to re-initialize this object, if in
* the case it gets modified in another scope.
*/
public function refreshObj() : void
{
$this->loadObj($this->guid);
}
/*
* This function will update this object in the database, or insert new
* data if this object does not yet have a GUID. This function uses the
* $fields array to construct SQL queries.
*/
public function saveObj() : void
{
/* update existing object */
if (isset($this->guid))
{
$this->updated = self::getCurrentTimestamp();
foreach ($this->fields as $tbl => $flds)
{
$tbl = database::esc($tbl);
$udstr = "";
foreach ($flds as $fld)
{
if (!isset($this->$fld))
continue;
$fld = database::esc($fld);
$udstr .= $fld . " = '" . database::esc($this->$fld) . "', ";
}
if (strlen($udstr) > 0)
{
$udstr = substr($udstr, 0, -2); // remove ", " from the end
$query = "UPDATE " . $tbl . " SET " . $udstr . " WHERE guid = '" . database::esc($this->guid) . "'";
database::query($query);
}
}
}
/* create new object */
else
{
$this->guid = self::getNewGUID();
$this->created = self::getCurrentTimestamp();
$this->updated = $this->created;
foreach ($this->fields as $tbl => $flds)
{
$tbl = database::esc($tbl);
$fldstr = "";
$valstr = "";
foreach ($flds as $fld)
{
if (!isset($this->$fld))
continue;
$fld = database::esc($fld);
$fldstr .= $fld . ", ";
$valstr .= "'" . database::esc($this->$fld) . "', ";
}
if (strlen($fldstr) > 0)
{
$fldstr = substr($fldstr, 0, -2); // remove ", "
$valstr = substr($valstr, 0, -2);
$query = "INSERT INTO " . $tbl . " (" . $fldstr . ") VALUES (" . $valstr . ")";
database::query($query);
}
}
}
}
/*
* This function will remove this object from the database by deleting
* rows with this object's GUID from tables in $fields. If this object
* does not have a GUID, throw an exception.
*/
public function delObj() : void
{
if (!isset($this->guid))
throw new Exception("GUID (null) does not exist");
$guid = database::esc($this->guid);
foreach ($this->fields as $tbl => $flds)
{
$tbl = database::esc($tbl);
$query = "DELETE FROM " . $tbl . " WHERE guid = '" . $guid . "'";
database::query($query);
}
/* garbage collection */
$query = "DELETE FROM members WHERE guid = '" . $guid . "' OR member = '" . $guid . "'";
database::query($query);
$query = "DELETE FROM views WHERE guid = '" . $guid . "' OR viewer = '" . $guid . "'";
database::query($query);
}
/*
* Get a random sha256 blob, returned as a hexadecimal string
*/
public static function getBlob() : string
{
return hash("sha256", openssl_random_pseudo_bytes(64));
}
/*
* Get current timestamp as a string for object database purposes
*/
private static function getCurrentTimestamp() : string
{
$query = "SELECT now() AS stamp";
$res = database::query($query);
return $res[0]['stamp'];
}
/*
* Check whether the given GUID exists
*/
private static function isGUID(string $guid) : bool
{
$guid = database::esc($guid);
$query = "SELECT guid FROM objects WHERE guid = '" . $guid . "'";
$res = database::query($query);
return count($res) > 0;
}
/*
* Get a new, unique GUID for a new system object
*/
private static function getNewGUID() : string
{
do $guid = substr(self::getBlob(), 0, 8);
while (self::isGUID($guid));
return $guid;
}
/*
* Assert that this object is of the expected type in the database.
* Throw an exception if a type mismatch exists. Check will only
* be performed if guid and objtype are set on this object.
*/
protected function expectType(string $objtype) : void
{
if (isset($this->guid) && isset($this->objtype))
{
if ($this->objtype != $objtype)
throw new Exception("Invalid object allocation. " . $this->guid . " is a " .
$this->objtype . ", not a " . $objtype . ".");
}
}
}
?>