Copyright (C) 2010 - 2022
All Rights Reserved.
Contributor(s):
Mark J Crane
Luis Daniel Lucio Quiroz
*/
//define the database class
if (!class_exists('database')) {
class database {
const TABLE_PREFIX = "v_";
/**
* Database connection
* @access private
* @var PDO object
*/
public $db;
/**
* Driver to use.
* @access public
* @var string Can be pgsql, mysql, sqlite, odbc
*/
public $driver;
/**
* Alias of driver.
* @access public
* @var string Can be pgsql, mysql, sqlite, odbc
* @see $driver
*/
public $type;
/**
* Host for database connection
* @access public
* @var string host name or IP address.
*/
public $host;
/**
* Port number
* @access public
* @var int 1025 - 65534
*/
public $port;
/**
* Database name
* @access public
* @var string
*/
public $db_name;
/**
* Database security
* @access public
* @var boolean
*/
public $db_secure;
/**
* Specifies the file name of the client SSL certificate
* @access public
* @var string full path
*/
public $db_cert_authority;
/**
* Username used to connect
* @access public
* @var string
*/
public $username;
/**
* Password used to connect
* @access public
* @var string
*/
public $password;
/**
* Full path to file name.
* @access public
* @var string full path to file name
*/
public $path;
/**
* Table name.
* @access private
* @var string sanitized
*/
private $table;
/**
* Where clause(s) of an SQL statement.
* Array of arrays must be passed with each having the
* following keys:
*
- 'name' - Any valid column name.
* - 'operator' - Must be one of the following values: =, >, <, >=, <=, <>, !=
* - 'value' - Value being matched
* Example Usage:
* $db->where['SearchTerm'] = ['name'=>'MyColumn','operator'=>'=','value'=>'MySearchTerm'
* $db->where['NextSearchTerm'] = ['name'=>'MyColumn','operator'=>'=','value'=>'MyOtherSearchTerm'
* Below is equivalent to the above.
* $db->where[0] = ['name'=>'MyColumn','operator'=>'=','value'=>'MyValue'
* $db->where[1] = ['name'=>'MyColumn','operator'=>'=>','value'=>'MyValue'
* @access public
* @var array Two dimensional array of key value pairs
* @see $order_by
*/
public $where; //array
/**
* Order By clause(s) of an SQL statement.
* Array of arrays must be passed with each having the
* following keys:
*
- 'name' - Any valid column name.
* - 'operator' - Must be one of the following values: =, >, <, >=, <=, <>, !=
* - 'value' - Value being matched
* Example Usage:
* $db->where['SearchTerm'] = ['name'=>'MyColumn','operator'=>'=','value'=>'MySearchTerm'
* $db->where['NextSearchTerm'] = ['name'=>'MyColumn','operator'=>'=','value'=>'MyOtherSearchTerm'
* Below is equivalent to the above.
* $db->where[0] = ['name'=>'MyColumn','operator'=>'=','value'=>'MyValue'
* $db->where[1] = ['name'=>'MyColumn','operator'=>'=>','value'=>'MyValue'
* @access private
* @var array Two dimensional array of key value pairs
* @see $where
*/
public $order_by; //array
/**
* Ascending or Descending order.
* @var string
* @access public
*/
public $order_type;
/**
* Numerical value to limit returned results.
* @var int Used for 'LIMIT' in SQL statement.
* @access public
*/
public $limit;
/**
* Numerical value to offset returned results.
* @var int Used for 'OFFSET' in SQL statement.
* @access public
*/
public $offset;
/**
* Array of fields.
* Fields are specified in 'name'=>'value' format.
*
Used by {@link database::add() } and {@link database::update() }
* @access public
* @var array Array of columns
* @see database::add()
* @see database::update()
*/
public $fields;
/**
* Unknown property
* @var unknown
* @access public
*/
public $count;
/**
* Unknown property
* @var unknown
* @access public
*/
public $sql;
/**
* Stores the result from the most recent query. The type will be based on what was requested.
* NOTE: If an error occurred on the last query the result is set to an empty string.
* @var mixed
*/
private $result;
/**
* Stores the application built from the app_config files.
* @var array
* @see $apps
* @access private
*/
private static $apps = [];
/**
* Stores the application name making the request.
* @var string App name making database request.
* @access public
*/
public $name;
/**
* Stores the application name making the request.
* @var string App name making database request.
* @see $app_uuid
* @access public
*/
public $app_name;
/**
* Stores the application UUID making the request.
* @var string
* @see $app_name
* @access public
*/
public $app_uuid;
/**
* Stores the domain UUID making the request.
* This is defaulted to the Session domain UUID.
* @access public
* @uses $this->domain_uuid
Default value upon object creation
* @var string Domain UUID making request.
*/
public $domain_uuid;
/**
* Stores the user UUID making the request.
* This is defaulted to the Session domain UUID.
* @access public
* @uses $this->user_uuid
Default value upon object creation
* @var string Domain UUID making request.
*/
public $user_uuid;
/**
* Message for the query results.
* @var array Contains the message array after a query
* @access private
*/
public $message;
/**
* Config object used to get the database connection params
* @var config
*/
private $config;
/**
* SSL Mode used to connect to the database
* @var string prefer or verify-ca. Default is 'prefer'
*/
public $ssl_mode;
/**
* Singleton type class
* @var database
*/
private static $database;
/**
* Called when the object is created
* @param array $params Optional
*/
public function __construct(array $params = []) {
//handle the config object
if (isset($params['config'])) {
$config = $params['config'];
}
else {
$config = new config();
}
//driver and type point to the same value
$this->driver = $config->get('database.0.type', 'pgsql');
$this->driver = $config->get('database.0.type', 'pgsql');
$this->type = $config->get('database.0.type', 'pgsql');
$this->host = $config->get('database.0.host', '127.0.0.1');
$this->port = $config->get('database.0.port', '5432');
$this->username = $config->get('database.0.username', 'fusionpbx');
$this->password = $config->get('database.0.password', 'fusionpbx');
$this->db_name = $config->get('database.0.name', 'fusionpbx');
$this->db_secure = $config->get('database.0.secure', '');
$this->db_cert_authority = $config->get('database.0.cert_authority', '');
$this->ssl_mode = $config->get('database.0.ssl_mode', '');
//save the reference to the single instance of the config to this object
$this->config = $config;
//connect to the database now
$this->connect();
//use the session domain_uuid
if (!isset($this->domain_uuid) && isset($_SESSION['domain_uuid'])) {
$this->domain_uuid = $_SESSION['domain_uuid'];
}
//allow passed domain_uuid in the constructor to override the session domain
if (isset($params['domain_uuid'])) {
$this->domain_uuid = $params['domain_uuid'];
}
//allow passed user_uuid in the constructor to override the session user_uuid
if (isset($params['user_uuid'])) {
$this->user_uuid = $params['user_uuid'];
} else {
//try to determine the current user_uuid using the session
$this->user_uuid = (!empty($_SESSION['user_uuid']) ? $_SESSION['user_uuid'] : null);
}
}
/**
* Magic function called whenever a property is attempted to be set.
* This is used to protect the values stored in the object properties.
* @param mixed $name Name of object property
* @param mixed $value Value of property
*/
public function __set($name,$value) {
switch($name) {
case 'name':
case 'app_name':
$this->app_name = self::sanitize($value);
break;
case 'message':
if (is_array($value)) {
$this->message = $value;
} else {
trigger_error('Message property must be set to array type', E_USER_ERROR);
}
break;
case 'table':
$this->table = self::sanitize($value);
break;
case 'db_name':
$this->db_name = self::sanitize($value);
break;
case 'db':
if ($name instanceof PDO) {
$this->db = $value;
} else {
trigger_error('db property must be a PDO object!', E_USER_ERROR);
}
break;
case 'count':
break;
case 'path':
$value = realpath($value);
if (file_exists($value)) {
$this->path = $value;
} else {
trigger_error('Unable to find database path file!', E_USER_ERROR);
}
break;
case 'db_cert_authority':
if (!file_exists($value)) {
trigger_error('db cert authority not found!', E_USER_WARNING);
}
$this->db_cert_authority = $value;
break;
case 'port':
$value = (int)$value; // force cast to int
if ($value > 1023 && $value < 65536) { $this->port = $value; } //valid values are 1024...65535
else { trigger_error('Port not a valid range', E_USER_ERROR); }
break;
case 'app_uuid':
case 'domain_uuid':
if (is_uuid($value)) { $this->domain_uuid = $value; }
break;
case 'type':
case 'driver':
switch($value) {
case 'pgsql':
case 'mysql':
case 'sqlite':
case 'odbc':
$this->type = $value;
$this->driver = $value;
break;
default:
trigger_error("Type/Driver must be set to pgsql,mysql,sqlite,odbc", E_USER_ERROR);
break;
}
case 'offset':
case 'limit':
if (is_int($value)) {
$this->$name = $value;
} else {
trigger_error('Offset or Limit not set to valid integer. Resetting to zero!', E_USER_WARNING);
}
break;
case '':
trigger_error('Database property must not be empty', E_USER_ERROR);
break;
case 'null':
case null:
trigger_error('Database property must not be null', E_USER_ERROR);
break;
case 'debug':
$this->debug = $value;
}
}
/**
* Magic function called whenever a property is requested.
* If any case statement is removed then access to the variable will be removed.
* @param mixed $name object property
* @return mixed
*/
public function __get($name) {
//remove any case statement below to remove access to the variable
switch($name) {
case 'name':
return $this->app_name;
case 'app_name':
case 'app_uuid':
case 'db':
case 'db_cert_authority':
case 'db_name':
case 'db_secure':
case 'domain_uuid':
case 'driver':
case 'fields':
case 'host':
case 'limit':
case 'message':
case 'offset':
case 'order_by':
case 'order_type':
case 'password':
case 'path':
case 'port':
case 'result':
case 'sql':
case 'table':
case 'type':
case 'username':
case 'where':
case 'debug':
case 'count':
return $this->count();
default:
trigger_error('Object property not available', E_USER_ERROR);
}
}
/**
* Returns the config object used to create this database object
* @return config Config object
*/
public function config(): config {
return $this->config;
}
/**
* Connect to the database.
* Database driver must be set before calling connect.
* For types other than sqlite. Execution will stop on failure.
* @depends database::driver Alias of database::type.
*
*/
public function connect() {
//get the database connection settings
//$db_type = $conf['database.0.type'];
//$db_host = $conf['database.0.host'];
//$db_port = $conf['database.0.port'];
//$db_name = $conf['database.0.name'];
//$db_username = $conf['database.0.username'];
//$db_password = $conf['database.0.password'];
//debug info
//echo "db type:".$db_type."\n";
//echo "db host:".$db_host."\n";
//echo "db port:".$db_port."\n";
//echo "db name:".$db_name."\n";
//echo "db username:".$db_username."\n";
//echo "db password:".$db_password."\n";
//echo "db path:".$db_path."\n";
//echo "\n";
//set defaults
if (!isset($this->driver) && isset($db_type)) { $this->driver = $db_type; }
if (!isset($this->type) && isset($db_type)) { $this->type = $db_type; }
if (!isset($this->host) && isset($db_host)) { $this->host = $db_host; }
if (!isset($this->port) && isset($db_port)) { $this->port = $db_port; }
if (!isset($this->db_name) && isset($db_name)) { $this->db_name = $db_name; }
if (!isset($this->db_secure) && isset($db_secure)) {
$this->db_secure = $db_secure;
}
else {
$this->db_secure = false;
}
if (!isset($this->username) && isset($db_username)) { $this->username = $db_username; }
if (!isset($this->password) && isset($db_password)) { $this->password = $db_password; }
if (!isset($this->path) && isset($db_path)) { $this->path = $db_path; }
if ($this->driver == "sqlite") {
if (empty($this->db_name)) {
$server_name = $_SERVER["SERVER_NAME"];
$server_name = str_replace ("www.", "", $server_name);
$db_name_short = $server_name;
$this->db_name = $server_name.'.db';
}
else {
$db_name_short = $this->db_name;
}
$this->path = realpath($this->path);
if (file_exists($this->path.'/'.$this->db_name)) {
//connect to the database
$this->db = new PDO('sqlite:'.$this->path.'/'.$this->db_name); //sqlite 3
//PRAGMA commands
$this->db->query('PRAGMA foreign_keys = ON;');
$this->db->query('PRAGMA journal_mode = wal;');
//add additional functions to SQLite so that they are accessible inside SQL
//bool PDO::sqliteCreateFunction ( string function_name, callback callback [, int num_args] )
$this->db->sqliteCreateFunction('md5', 'php_md5', 1);
$this->db->sqliteCreateFunction('unix_timestamp', 'php_unix_timestamp', 1);
$this->db->sqliteCreateFunction('now', 'php_now', 0);
$this->db->sqliteCreateFunction('sqlitedatatype', 'php_sqlite_data_type', 2);
$this->db->sqliteCreateFunction('strleft', 'php_left', 2);
$this->db->sqliteCreateFunction('strright', 'php_right', 2);
}
else {
echo "not found";
}
}
if ($this->driver == "mysql") {
try {
//mysql pdo connection
if (strlen($this->host) == 0 && empty($this->port)) {
//if both host and port are empty use the unix socket
$this->db = new PDO("mysql:host=$this->host;unix_socket=/var/run/mysqld/mysqld.sock;dbname=$this->db_name", $this->username, $this->password);
}
else {
if (empty($this->port)) {
//leave out port if it is empty
$this->db = new PDO("mysql:host=$this->host;dbname=$this->db_name;", $this->username, $this->password, array(
PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION
));
}
else {
$this->db = new PDO("mysql:host=$this->host;port=$this->port;dbname=$this->db_name;", $this->username, $this->password, array(
PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION
));
}
}
}
catch (PDOException $error) {
print "error: " . $error->getMessage() . "
";
die();
}
}
if ($this->driver == "pgsql") {
//database connection
try {
if (!empty($this->host)) {
if (empty($this->port)) { $this->port = "5432"; }
if ($this->db_secure === true) {
$this->db = new PDO("pgsql:host=$this->host port=$this->port dbname=$this->db_name user=$this->username password=$this->password sslmode=$this->ssl_mode sslrootcert=$this->db_cert_authority");
}
else {
$this->db = new PDO("pgsql:host=$this->host port=$this->port dbname=$this->db_name user=$this->username password=$this->password");
}
}
else {
$this->db = new PDO("pgsql:dbname=$this->db_name user=$this->username password=$this->password");
}
}
catch (PDOException $error) {
print "error: " . $error->getMessage() . "
";
die();
}
}
if ($this->driver == "odbc") {
//database connection
try {
$this->db = new PDO("odbc:".$this->db_name, $this->username, $this->password);
}
catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
}
}
/**
* Returns the table names from the database.
* @return array tables
* @depends connect()
*/
public function tables() {
$result = [];
//connect to the database if needed
if (!$this->db) {
$this->connect();
}
if ($this->type == "sqlite") {
$sql = "SELECT name FROM sqlite_master ";
$sql .= "WHERE type='table' ";
$sql .= "order by name;";
}
if ($this->type == "pgsql") {
$sql = "select table_name as name ";
$sql .= "from information_schema.tables ";
$sql .= "where table_schema='public' ";
$sql .= "and table_type='BASE TABLE' ";
$sql .= "order by table_name ";
}
if ($this->type == "mysql") {
$sql = "show tables";
}
if ($this->type == "mssql") {
$sql = "SELECT * FROM sys.Tables order by name asc";
}
$prep_statement = $this->db->prepare(check_sql($sql));
$prep_statement->execute();
$tmp = $prep_statement->fetchAll(PDO::FETCH_NAMED);
if ($this->type == "pgsql" || $this->type == "sqlite" || $this->type == "mssql") {
if (is_array($tmp)) {
foreach ($tmp as $row) {
$result[]['name'] = $row['name'];
}
}
}
if ($this->type == "mysql") {
if (is_array($tmp)) {
foreach ($tmp as $row) {
$table_array = array_values($row);
$result[]['name'] = $table_array[0];
}
}
}
return $result;
}
/**
* Returns table information from the database.
* @return array table info
* @depends connect()
*/
public function table_info() {
//public $db;
//public $type;
//public $table;
//public $name;
//connect to the database if needed
if (!$this->db) {
$this->connect();
}
//get the table info
if (empty($this->table)) { return false; }
if ($this->type == "sqlite") {
$sql = "PRAGMA table_info(".$this->table.");";
}
if ($this->type == "pgsql") {
$sql = "SELECT ordinal_position, ";
$sql .= "column_name, ";
$sql .= "data_type, ";
$sql .= "column_default, ";
$sql .= "is_nullable, ";
$sql .= "character_maximum_length, ";
$sql .= "numeric_precision ";
$sql .= "FROM information_schema.columns ";
$sql .= "WHERE table_name = '".$this->table."' ";
$sql .= "and table_catalog = '".$this->db_name."' ";
$sql .= "ORDER BY ordinal_position; ";
}
if ($this->type == "mysql") {
$sql = "DESCRIBE ".$this->table.";";
}
if ($this->type == "mssql") {
$sql = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '".$this->table."'";
}
$prep_statement = $this->db->prepare($sql);
$prep_statement->execute();
//set the result array
return $prep_statement->fetchAll(PDO::FETCH_ASSOC);
}
/**
* Checks if the table exists in the database.
* Note:
* Table name must be sanitized. Otherwise, a warning will be
* emitted and false will be returned.
* @param type $table_name Sanitized name of the table to search for.
* @return boolean Returns true if the table exists and false if it does not.
* @depends connect()
*/
public function table_exists ($table_name) {
if (self::sanitize($table_name) != $table_name) {
trigger_error('Table Name must be sanitized', E_USER_WARNING);
return false;
}
//connect to the database if needed
if (!$this->db) {
$this->connect();
}
//if unable to connect to the database
if (!$this->db) {
$backtrace = debug_backtrace();
echo "Connection Failed
\n";
echo "line number ".__line__."
\n";
echo "";
print_r($backtrace);
echo "
";
exit;
}
//query table store to see if the table exists
$sql = "";
if ($this->type == "sqlite") {
$sql .= "SELECT * FROM sqlite_master WHERE type='table' and name='$table_name' ";
}
if ($this->type == "pgsql") {
$sql .= "select * from pg_tables where schemaname='public' and tablename = '$table_name' ";
}
if ($this->type == "mysql") {
$sql .= "SELECT TABLE_NAME FROM information_schema.tables WHERE table_schema = '".$this->db_name."' and TABLE_NAME = '$table_name' ";
}
$prep_statement = $this->db->prepare($sql);
$prep_statement->execute();
$result = $prep_statement->fetchAll(PDO::FETCH_NAMED);
if (count($result) > 0) {
return true; //table exists
}
else {
return false; //table doesn't exist
}
}
/**
* Queries {@link database::table_info()} to return the fields.
* @access public
* @return array Two dimensional array
* @depends table_info()
*/
public function fields() {
//public $db;
//public $type;
//public $table;
//public $name;
//initialize the array
$result = [];
//get the table info
$table_info = $this->table_info();
//set the list of fields
if ($this->type == "sqlite") {
if (is_array($table_info)) {
foreach($table_info as $row) {
$result[]['name'] = $row['name'];
}
}
}
if ($this->type == "pgsql") {
if (is_array($table_info)) {
foreach($table_info as $row) {
$result[]['name'] = $row['column_name'];
}
}
}
if ($this->type == "mysql") {
if (is_array($table_info)) {
foreach($table_info as $row) {
$result[]['name'] = $row['Field'];
}
}
}
if ($this->type == "mssql") {
if (is_array($table_info)) {
foreach($table_info as $row) {
$result[]['name'] = $row['COLUMN_NAME'];
}
}
}
//return the result array
return $result;
}
/**
* Searches database using the following object properties:
*
* - table - sanitized name of the table {@see database::table}
* - where - where clause {@see database::where}
* - order_by - order_by clause {@see database::order_by}
* - limit - limit clause {@see database::limit}
* - offset - offset clause {@see database::offset}
*
* @return boolean
* @depends connect()
*/
public function find() {
//connect;
//table;
//where;
//order_by;
//limit;
//offset;
//connect to the database if needed
if (!$this->db) {
$this->connect();
}
//get data from the database
$sql = "select * from ".$this->table." ";
if ($this->where) {
$i = 0;
if (is_array($this->where)) {
foreach($this->where as $row) {
//sanitize the name
$array['name'] = self::sanitize($array['name']);
//validate the operator
switch ($row['operator']) {
case "<": break;
case ">": break;
case "<=": break;
case ">=": break;
case "=": break;
case "<>": break;
case "!=": break;
default:
//invalid operator
return false;
}
//build the sql
if ($i == 0) {
//$sql .= 'where '.$row['name']." ".$row['operator']." '".$row['value']."' ";
$sql .= 'where '.$row['name']." ".$row['operator']." :".$row['name']." ";
}
else {
//$sql .= "and ".$row['name']." ".$row['operator']." '".$row['value']."' ";
$sql .= "and ".$row['name']." ".$row['operator']." :".$row['name']." ";
}
//add the name and value to the params array
$params[$row['name']] = $row['value'];
//increment $i
$i++;
}
}
}
if (is_array($this->order_by)) {
$sql .= "order by ";
$i = 1;
if (is_array($this->order_by)) {
foreach($this->order_by as $row) {
//sanitize the name
$row['name'] = self::sanitize($row['name']);
//sanitize the order
switch ($row['order']) {
case "asc":
break;
case "desc":
break;
default:
$row['order'] = '';
}
//build the sql
if (count($this->order_by) == $i) {
$sql .= $row['name']." ".$row['order']." ";
}
else {
$sql .= $row['name']." ".$row['order'].", ";
}
//increment $i
$i++;
}
}
}
//limit
if (isset($this->limit) && is_numeric($this->limit)) {
$sql .= "limit ".$this->limit." ";
}
//offset
if (isset($this->offset) && is_numeric($this->offset)) {
$sql .= "offset ".$this->offset." ";
}
$prep_statement = $this->db->prepare($sql);
if ($prep_statement) {
$prep_statement->execute($params);
$array = $prep_statement->fetchAll(PDO::FETCH_ASSOC);
unset($prep_statement);
return $array;
}
else {
return false;
}
}
// Use this function to run complex queries
public function execute($sql, $parameters = null, $return_type = 'all') {
//connect to the database if needed
if (!$this->db) {
$this->connect();
}
//set the error mode
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//run the query, and return the results
try {
$prep_statement = $this->db->prepare($sql);
if (is_array($parameters)) {
$prep_statement->execute($parameters);
}
else {
$prep_statement->execute();
}
$message["message"] = "OK";
$message["code"] = "200";
$message["sql"] = $sql;
if (is_array($parameters)) {
$message["parameters"] = $parameters;
}
$this->message = $message;
//return the results
switch($return_type) {
case 'all':
return $prep_statement->fetchAll(PDO::FETCH_ASSOC);
case 'row':
return $prep_statement->fetch(PDO::FETCH_ASSOC);
case 'column';
return $prep_statement->fetchColumn();
default:
return $prep_statement->fetchAll(PDO::FETCH_ASSOC);
}
}
catch(PDOException $e) {
$message["type"] = 'error';
$message["code"] = $e->getCode();
$message["message"] = $e->getMessage();
$message["sql"] = $sql;
if (!empty($parameters)) {
$message["parameters"] = $parameters;
}
$this->message = $message;
return false;
}
}
public function add() {
//connect to the database if needed
if (!$this->db) {
$this->connect();
}
//sanitize the table name
//$this->table = self::sanitize($this->table); // no longer needed
//count the fields
$field_count = count($this->fields);
//add data to the database
$sql = "insert into ".$this->table;
$sql .= " (";
$i = 1;
if (is_array($this->fields)) {
foreach($this->fields as $name => $value) {
$name = self::sanitize($name);
if (count($this->fields) == $i) {
$sql .= $name." \n";
}
else {
$sql .= $name.", \n";
}
$i++;
}
}
$sql .= ") \n";
$sql .= "values \n";
$sql .= "(\n";
$i = 1;
if (is_array($this->fields)) {
foreach($this->fields as $name => $value) {
$name = self::sanitize($name);
if ($field_count == $i) {
if (isset($value) && $value != '') {
//$sql .= "'".$value."' ";
$sql .= ":".$name." \n";
$params[$name] = trim($value);
}
else {
$sql .= "null \n";
}
}
else {
if (isset($value) && $value != '') {
//$sql .= "'".$value."', ";
$sql .= ":".$name.", \n";
$params[$name] = trim($value);
}
else {
$sql .= "null, \n";
}
}
$i++;
}
}
$sql .= ")\n";
//run the query, show exceptions
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//reduce prepared statement latency
if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
$this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
}
//prepare the sql and parameters and then run the query
try {
//$this->sql = $sql;
//$this->db->exec($sql);
$prep_statement = $this->db->prepare($sql);
$prep_statement->execute($params);
}
catch(PDOException $e) {
echo "Error:
\n";
echo "\n";
echo "\n";
echo "\n";
echo $e->getMessage();
echo " | \n";
echo "
\n";
echo "
\n";
}
unset($sql, $prep_statement, $this->fields);
}
public function update() {
//connect to the database if needed
if (!$this->db) {
$this->connect();
}
//sanitize the table name
//$this->table = self::sanitize($this->table); // no longer needed
//udate the database
$sql = "update ".$this->table." set ";
$i = 1;
if (is_array($this->fields)) {
foreach($this->fields as $name => $value) {
$name = self::sanitize($name);
if (count($this->fields) == $i) {
if (!empty($name) && $value == null) {
$sql .= $name." = null ";
}
else {
//$sql .= $name." = '".$value."' ";
$sql .= $name." = :".$name." ";
$params[$name] = trim($value);
}
}
else {
if (!empty($name) && $value == null) {
$sql .= $name." = null, ";
}
else {
//$sql .= $name." = '".$value."', ";
$sql .= $name." = :".$name.", ";
$params[$name] = trim($value);
}
}
$i++;
}
}
$i = 0;
if (is_array($this->where)) {
foreach($this->where as $row) {
//sanitize the name
$row['name'] = self::sanitize($row['name']);
//validate the operator
switch ($row['operator']) {
case "<": break;
case ">": break;
case "<=": break;
case ">=": break;
case "=": break;
case "<>": break;
case "!=": break;
default:
//invalid operator
return false;
}
//build the sql
if ($i == 0) {
//$sql .= $row['name']." ".$row['operator']." '".$row['value']."' ";
$sql .= "where ".$row['name']." ".$row['operator']." :".$row['name']." ";
}
else {
//$sql .= $row['name']." ".$row['operator']." '".$row['value']."' ";
$sql .= "and ".$row['name']." ".$row['operator']." :".$row['name']." ";
}
//add the name and value to the params array
$params[$row['name']] = $row['value'];
//increment $i
$i++;
}
}
//$this->db->exec(check_sql($sql));
$prep_statement = $this->db->prepare($sql);
$prep_statement->execute($params);
unset($prep_statement);
unset($this->fields);
unset($this->where);
unset($sql);
}
public function delete(array $array) {
//set the default value
$retval = true;
//return the array
if (!is_array($array)) { return false; }
//connect to the database if needed
if (!$this->db) {
$this->connect();
}
//set the message id
$m = 0;
//debug sql
//$this->debug["sql"] = true;
//set the message id
$m = 0;
//loop through the array
$checked = false;
$x = 0;
foreach ($array as $parent_name => $tables) {
if (is_array($tables)) {
foreach ($tables as $id => $row) {
//prepare the variables
$parent_name = self::sanitize($parent_name);
$parent_key_name = self::singular($parent_name)."_uuid";
//build the delete array
if (!empty($row['checked']) && $row['checked'] == 'true') {
//set checked to true
$checked = true;
//delete the child data
if (isset($row[$parent_key_name])) {
$new_array[$parent_name][$x][$parent_key_name] = $row[$parent_key_name];
}
//remove the row from the main array
unset($array[$parent_name][$x]);
}
//loop through the fields
foreach($row as $field_name => $field_value) {
//find the child tables
$y = 0;
if (is_array($field_value)) {
//prepare the variables
$child_name = self::sanitize($field_name);
$child_key_name = self::singular($child_name)."_uuid";
//loop through the child rows
foreach ($field_value as $sub_row) {
//build the delete array
if ($row['checked'] == 'true') {
//set checked to true
$checked = true;
//delete the child data
$new_array[$child_name][][$child_key_name] = $sub_row[$child_key_name];
//remove the row from the main array
unset($array[$parent_name][$x][$child_name][$y]);
}
//increment the value
$y++;
}
}
}
//increment the value
$x++;
}
}
}
//if not checked then copy the array to delete array
if (!$checked) {
$new_array = $array;
}
//get the current data
if (count($new_array) > 0) {
//build an array of tables, fields, and values
foreach($new_array as $table_name => $rows) {
foreach($rows as $row) {
foreach($row as $field_name => $field_value) {
$keys[$table_name][$field_name][] = $field_value;
}
}
}
//use the array to get a copy of the parent data before deleting it
foreach($new_array as $table_name => $rows) {
foreach($rows as $row) {
$table_name = self::sanitize($table_name);
$sql = "select * from ".self::TABLE_PREFIX.$table_name." ";
$i = 0;
foreach($row as $field_name => $field_value) {
if ($i == 0) { $sql .= "where "; } else { $sql .= "and "; }
$sql .= $field_name." in ( ";
$i = 0;
foreach($keys[$table_name][$field_name] as $field_value) {
$field_name = self::sanitize($field_name);
if ($i > 0) { $sql .= " ,"; }
$sql .= " :".$field_name."_".$i." ";
$i++;
}
$sql .= ") ";
$i = 0;
foreach($keys[$table_name][$field_name] as $field_value) {
$parameters[$field_name.'_'.$i] = $field_value;
$i++;
}
}
}
if (isset($field_value) && $field_value != '') {
$results = $this->execute($sql, $parameters, 'all');
unset($parameters);
if (is_array($results)) {
$old_array[$table_name] = $results;
}
}
}
//get relations array
$relations = self::get_relations($parent_name);
//add child data to the old array
foreach($old_array as $parent_name => $rows) {
//get relations array
$relations = self::get_relations($parent_name);
//loop through the rows
$x = 0;
foreach($rows as $row) {
if (is_array($relations)) {
foreach ($relations as $relation) {
if ($relation['key']['action']['delete'] == 'cascade') {
//set the child table
$child_table = $relation['table'];
//remove the v_ prefix
if (substr($child_table, 0, strlen(self::TABLE_PREFIX)) == self::TABLE_PREFIX) {
$child_table = substr($child_table, strlen(self::TABLE_PREFIX));
}
//get the child data
$sql = "select * from ".self::TABLE_PREFIX.$child_table." ";
$sql .= "where ".$relation['field']." = :".$relation['field'];
$parameters[$relation['field']] = $row[$relation['field']];
$results = $this->execute($sql, $parameters, 'all');
unset($parameters);
if (is_array($results) && $parent_name !== $child_table) {
$old_array[$parent_name][$x][$child_table] = $results;
}
//delete the child data
if (isset($row[$relation['field']]) && !empty($row[$relation['field']])) {
$sql = "delete from ".self::TABLE_PREFIX.$child_table." ";
$sql .= "where ".$relation['field']." = :".$relation['field'];
$parameters[$relation['field']] = $row[$relation['field']];
// $this->execute($sql, $parameters);
}
unset($parameters);
}
}
}
$x++;
}
}
}
//start the atomic transaction
$this->db->beginTransaction();
//delete the current data
foreach($new_array as $table_name => $rows) {
//echo "table: ".$table_name."\n";
foreach($rows as $row) {
if (permission_exists(self::singular($table_name).'_delete')) {
$sql = "delete from ".self::TABLE_PREFIX.$table_name." ";
$i = 0;
foreach($row as $field_name => $field_value) {
//echo "field: ".$field_name." = ".$field_value."\n";
if ($i == 0) { $sql .= "where "; } else { $sql .= "and "; }
$sql .= $field_name." = :".$field_name." ";
$parameters[$field_name] = $field_value;
$i++;
}
try {
$this->execute($sql, $parameters);
$message["message"] = "OK";
$message["code"] = "200";
$message["uuid"] = $id;
$message["details"][$m]["name"] = $this->app_name;
$message["details"][$m]["message"] = "OK";
$message["details"][$m]["code"] = "200";
//$message["details"][$m]["uuid"] = $parent_key_value;
$message["details"][$m]["sql"] = $sql;
$this->message = $message;
$m++;
unset($sql);
unset($statement);
}
catch(PDOException $e) {
$retval = false;
$message["message"] = "Bad Request";
$message["code"] = "400";
$message["details"][$m]["name"] = $this->app_name;
$message["details"][$m]["message"] = $e->getMessage();
$message["details"][$m]["code"] = "400";
$message["details"][$m]["sql"] = $sql;
$this->message = $message;
$m++;
}
unset($parameters);
} //if permission
} //foreach rows
} //foreach $array
//commit the atomic transaction
$this->db->commit();
//set the action if not set
$transaction_type = 'delete';
//log the transaction results
if (file_exists($_SERVER["PROJECT_ROOT"]."/app/database_transactions/app_config.php")) {
$sql = "insert into ".self::TABLE_PREFIX."database_transactions ";
$sql .= "(";
$sql .= "database_transaction_uuid, ";
if (isset($this->domain_uuid) && is_uuid($this->domain_uuid)) {
$sql .= "domain_uuid, ";
}
if (isset($this->user_uuid) && is_uuid($this->user_uuid)) {
$sql .= "user_uuid, ";
}
if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
$sql .= "app_uuid, ";
}
if (isset($this->app_name) && !empty($this->app_name)) {
$sql .= "app_name, ";
}
$sql .= "transaction_code, ";
$sql .= "transaction_address, ";
$sql .= "transaction_type, ";
$sql .= "transaction_date, ";
$sql .= "transaction_old, ";
$sql .= "transaction_new, ";
$sql .= "transaction_result ";
$sql .= ")";
$sql .= "values ";
$sql .= "(";
$sql .= "'".uuid()."', ";
if (isset($this->domain_uuid) && is_uuid($this->domain_uuid)) {
$sql .= "'".$this->domain_uuid."', ";
}
if (isset($this->user_uuid) && is_uuid($this->user_uuid)) {
$sql .= ":user_uuid, ";
}
if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
$sql .= ":app_uuid, ";
}
if (isset($this->app_name) && !empty($this->app_name)) {
$sql .= ":app_name, ";
}
$sql .= "'".$message["code"]."', ";
$sql .= ":remote_address, ";
$sql .= "'".$transaction_type."', ";
$sql .= "now(), ";
if (is_array($old_array)) {
$sql .= ":transaction_old, ";
}
else {
$sql .= "null, ";
}
if (is_array($new_array)) {
$sql .= ":transaction_new, ";
}
else {
$sql .= "null, ";
}
$sql .= ":transaction_result ";
$sql .= ")";
$statement = $this->db->prepare($sql);
if (isset($this->user_uuid) && is_uuid($this->user_uuid)) {
$statement->bindParam(':user_uuid', $this->user_uuid);
}
if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
$statement->bindParam(':app_uuid', $this->app_uuid);
}
if (isset($this->app_name) && !empty($this->app_name)) {
$statement->bindParam(':app_name', $this->app_name);
}
$statement->bindParam(':remote_address', $_SERVER['REMOTE_ADDR']);
if (is_array($old_array)) {
$old_json = json_encode($old_array, JSON_PRETTY_PRINT);
$statement->bindParam(':transaction_old', $old_json);
}
if (is_array($new_array)) {
$new_json = json_encode($new_array, JSON_PRETTY_PRINT);
$statement->bindParam(':transaction_new', $new_json);
}
$result = json_encode($this->message, JSON_PRETTY_PRINT);
$statement->bindParam(':transaction_result', $result);
$statement->execute();
unset($sql);
}
return $retval;
} //delete
/**
* Counts the number of rows.
* @return int Represents the number of counted rows or -1 if failed.
*/
public function count() {
//connect to the database if needed
if (!$this->db) {
$this->connect();
}
//return if the table name is not set
if (empty($this->table)) {
return;
}
//sanitize the table name
//$this->table = self::sanitize($this->table); // no longer needed
//get the number of rows
$sql = "select count(*) as num_rows from ".$this->table." ";
$i = 0;
if (is_array($this->where)) {
foreach($this->where as $row) {
//sanitize the name
$row['name'] = self::sanitize($row['name']);
//validate the operator
switch ($row['operator']) {
case "<": break;
case ">": break;
case "<=": break;
case ">=": break;
case "=": break;
case "<>": break;
case "!=": break;
default:
//invalid operator
return -1;
}
//build the sql
if ($i == 0) {
$sql .= "where ".$row['name']." ".$row['operator']." :".$row['name']." ";
}
else {
$sql .= "and ".$row['name']." ".$row['operator']." :".$row['name']." ";
}
//add the name and value to the params array
$params[$row['name']] = $row['value'];
//increment $i
$i++;
}
}
//unset($this->where); //should not be objects resposibility
$prep_statement = $this->db->prepare($sql);
if ($prep_statement) {
if (!isset($params)) { $params = null; }
$prep_statement->execute($params);
$row = $prep_statement->fetch(PDO::FETCH_ASSOC);
if ($row['num_rows'] > 0) {
return $row['num_rows'];
}
else {
return 0;
}
}
unset($prep_statement);
} //count
/**
* Performs a select query on database using the $sql statement supplied.
* @param type $sql Valid SQL statement.
* @param type $parameters Value can be array, empty string, or null.
* @param type $return_type Values can be set to all, row, or column.
* @return mixed Returned values can be array, string, boolean, int, or false. This is dependent on $return_type.
*/
public function select($sql, $parameters = '', $return_type = 'all') {
//connect to the database if needed
if (!$this->db) {
$this->connect();
}
//unable to connect to the database
if (!$this->db) {
echo "Connection Failed
\n";
echo "line number ".__line__."
\n";
exit;
}
//set the error mode
if ($this->db) {
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
//reduce prepared statement latency
if ($this->db && defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
$this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
}
//execute the query and return the results
try {
$prep_statement = $this->db->prepare($sql);
if (is_array($parameters)) {
$prep_statement->execute($parameters);
}
else {
$prep_statement->execute();
}
$message["message"] = "OK";
$message["code"] = "200";
$message["sql"] = $sql;
if (is_array($parameters)) {
$message["parameters"] = $parameters;
}
$this->message = $message;
//return the results
switch($return_type) {
case 'all':
return $prep_statement->fetchAll(PDO::FETCH_ASSOC);
case 'row':
return $prep_statement->fetch(PDO::FETCH_ASSOC);
case 'column':
return $prep_statement->fetchColumn();
default:
return $prep_statement->fetchAll(PDO::FETCH_ASSOC);
}
}
catch(PDOException $e) {
$message["message"] = "Bad Request";
$message["code"] = "400";
$message["error"]["message"] = $e->getMessage();
$message["sql"] = $sql;
if (is_array($parameters)) {
$message["parameters"] = $parameters;
}
$this->message = $message;
return false;
}
} //select
/**
* Sets the object $result to sql array
* @param array $array Array containing the table name, uuid, SQL and where clause.
* @return database Returns the database object or null.
*/
public function find_new(array $array) {
//connect to the database if needed
if (!$this->db) {
$this->connect();
}
//set the name
if (isset($array['name'])) {
$this->name = $array['name'];
}
//set the uuid
if (isset($array['uuid'])) {
$this->uuid = $array['uuid'];
}
//build the query
$sql = "SELECT * FROM ".self::TABLE_PREFIX . $this->name . " ";
if (isset($this->uuid)) {
//get the specific uuid
$sql .= "WHERE " . self::singular($this->name) . "_uuid = '" . $this->uuid . "' ";
} else {
//where
$i = 0;
if (isset($array['where'])) {
foreach ($array['where'] as $row) {
if (isset($row['operator'])) {
//validate the operator
switch ($row['operator']) {
case "<": break;
case ">": break;
case "<=": break;
case ">=": break;
case "=": break;
case "<>": break;
case "!=": break;
default:
//invalid operator
return null;
}
//build the sql
if ($i == 0) {
$sql .= "WHERE " . $row['name'] . " " . $row['operator'] . " :" . $row['value'] . " ";
} else {
$sql .= "AND " . $row['name'] . " " . $row['operator'] . " :" . $row['value'] . " ";
}
}
//add the name and value to the params array
$params[$row['name']] = $row['value'];
//increment $i
$i++;
}
}
//order by
if (isset($array['order_by'])) {
$array['order_by'] = self::sanitize($array['order_by']);
$sql .= "ORDER BY " . $array['order_by'] . " ";
}
//limit
if (isset($array['limit']) && is_numeric($array['limit'])) {
$sql .= "LIMIT " . $array['limit'] . " ";
}
//offset
if (isset($array['offset']) && is_numeric($array['offset'])) {
$sql .= "OFFSET " . $array['offset'] . " ";
}
}
//execute the query, and return the results
try {
$prep_statement = $this->db->prepare($sql);
$prep_statement->execute($params);
$message["message"] = "OK";
$message["code"] = "200";
$message["details"][$m]["name"] = $this->name;
$message["details"][$m]["message"] = "OK";
$message["details"][$m]["code"] = "200";
$message["details"][$m]["sql"] = $sql;
$this->message = $message;
$this->result = $prep_statement->fetchAll(PDO::FETCH_NAMED);
unset($prep_statement);
$m++;
} catch (PDOException $e) {
$message["message"] = "Bad Request";
$message["code"] = "400";
$message["details"][$m]["name"] = $this->name;
$message["details"][$m]["message"] = $e->getMessage();
$message["details"][$m]["code"] = "400";
$message["details"][$m]["sql"] = $sql;
$this->message = $message;
$this->result = '';
$m++;
}
return $this;
}
/**
* Stores the passed UUID in the object
* @param string $uuid A valid UUID must be passed
* @return database Returns this object
*/
public function uuid(string $uuid) {
$this->uuid = $uuid;
return $this;
}
/**
* Copies records and appends suffix to the column description data
* @param array $array Three dimensional Array. The first dimension is the table name without the prefix 'v_'. Second dimension in the row value as int. Third dimension is the column name.
* @return bool Returns true on success and false on failure.
*/
public function copy(array $array, $suffix = '(Copy)') {
//set default return value
$retval = false;
//return the array
if (!is_array($array)) { return $retval; }
//initialize array
$copy_array = [];
//set the message id
$m = 0;
//loop through the array
$x = 0;
foreach ($array as $parent_name => $tables) {
if (is_array($tables)) {
foreach ($tables as $id => $row) {
//prepare the variables
$parent_name = self::sanitize($parent_name);
$parent_key_name = self::singular($parent_name)."_uuid";
//build the copy array
if (!empty($row['checked']) && $row['checked'] == 'true') {
//set checked to true
$checked = true;
//copy the child data
if (!empty($row[$parent_key_name]) && is_uuid($row[$parent_key_name])) {
$copy_array[$parent_name][$x][$parent_key_name] = $row[$parent_key_name];
}
//remove the row from the main array
unset($array[$parent_name][$x]);
//loop through the fields
foreach($row as $field_name => $field_value) {
//find the child tables
if (is_array($field_value)) {
//prepare the variables
$child_name = self::sanitize($field_name);
$child_key_name = self::singular($child_name)."_uuid";
//loop through the child rows
$y = 0;
foreach ($field_value as $sub_row) {
//delete the child data
$copy_array[$child_name][][$child_key_name] = $sub_row[$child_key_name];
//remove the row from the main array
unset($array[$parent_name][$x][$child_name][$y]);
//increment the value
$y++;
}
}
}
}
//increment the value
$x++;
}
}
}
//get the current data
if (count($copy_array) > 0) {
//build an array of tables, fields, and values
foreach($copy_array as $table_name => $rows) {
foreach($rows as $row) {
foreach($row as $field_name => $field_value) {
$keys[$table_name][$field_name][] = $field_value;
}
}
}
//unset the array
unset($array);
//use the array to get a copy of the paent data before deleting it
foreach($copy_array as $table_name => $rows) {
foreach($rows as $row) {
$table_name = self::sanitize($table_name);
$sql = "select * from ".self::TABLE_PREFIX.$table_name." ";
$i = 0;
foreach($row as $field_name => $field_value) {
if ($i == 0) { $sql .= "where "; } else { $sql .= "and "; }
$sql .= $field_name." in ( ";
$i = 0;
foreach($keys[$table_name][$field_name] as $field_value) {
$field_name = self::sanitize($field_name);
if ($i > 0) { $sql .= " ,"; }
$sql .= " :".$field_name."_".$i." ";
$i++;
}
$sql .= ") ";
$i = 0;
foreach($keys[$table_name][$field_name] as $field_value) {
$parameters[$field_name.'_'.$i] = $field_value;
$i++;
}
}
}
$results = $this->execute($sql, $parameters, 'all');
unset($parameters);
if (is_array($results)) {
$array[$table_name] = $results;
}
}
//add child data to the old array
foreach($copy_array as $parent_name => $rows) {
//get relations array
$relations = self::get_relations($parent_name);
//loop through the rows
$x = 0;
foreach($rows as $row) {
if (is_array($relations)) {
foreach ($relations as $relation) {
//set the child table
$child_table = $relation['table'];
//remove the v_ prefix
if (substr($child_table, 0, strlen(self::TABLE_PREFIX)) == self::TABLE_PREFIX) {
$child_table = substr($child_table, strlen(self::TABLE_PREFIX));
}
//get the child data
$sql = "select * from ".self::TABLE_PREFIX.$child_table." ";
$sql .= "where ".$relation['field']." = :".$relation['field'];
$parameters[$relation['field']] = $row[$relation['field']];
$results = $this->execute($sql, $parameters, 'all');
unset($parameters);
if (is_array($results)) {
$array[$parent_name][$x][$child_table] = $results;
}
}
}
$x++;
}
}
}
//update the parent and child keys
$checked = false;
$x = 0;
foreach ($array as $parent_name => $tables) {
if (is_array($tables)) {
foreach ($tables as $id => $row) {
//prepare the variables
$parent_name = self::sanitize($parent_name);
$parent_key_name = self::singular($parent_name)."_uuid";
$parent_key_value = uuid();
//update the parent key id
$array[$parent_name][$x][$parent_key_name] = $parent_key_value;
//set enabled
if (array_key_exists(self::singular($parent_name).'_enabled', $array[$parent_name][$x])) {
$array[$parent_name][$x][self::singular($parent_name).'_enabled'] = $row[self::singular($parent_name).'_enabled'] === true || $row[self::singular($parent_name).'_enabled'] == 'true' ? 'true' : 'false';
}
else if (array_key_exists('enabled', $array[$parent_name][$x])) {
$array[$parent_name][$x]['enabled'] = $row['enabled'] === true || $row['enabled'] == 'true' ? 'true' : 'false';
}
//add copy to the description
if (array_key_exists(self::singular($parent_name).'_description', $array[$parent_name][$x])) {
$array[$parent_name][$x][self::singular($parent_name).'_description'] = trim($array[$parent_name][$x][self::singular($parent_name).'_description'].' '.$suffix);
}
else if (array_key_exists('description', $array[$parent_name][$x])) {
$array[$parent_name][$x]['description'] = trim($array[$parent_name][$x]['description'].' '.$suffix);
}
//loop through the fields
foreach($row as $field_name => $field_value) {
//find the child tables
$y = 0;
if (is_array($field_value)) {
//prepare the variables
$child_name = self::sanitize($field_name);
$child_key_name = self::singular($child_name)."_uuid";
//loop through the child rows
foreach ($field_value as $sub_row) {
//update the parent key id
$array[$parent_name][$x][$child_name][$y][$parent_key_name] = $parent_key_value;
//udpate the child key id
$array[$parent_name][$x][$child_name][$y][$child_key_name] = uuid();
//increment the value
$y++;
}
}
}
//increment the value
$x++;
}
}
}
//save the copy of the data
if (is_array($array) && count($array) > 0) {
$retval = $this->save($array);
unset($array);
}
return $retval;
} //end function copy
/**
* Toggles fields on a table using the toggle_field array values within the app object.
* @param array $array Three dimensional Array. The first dimension is the table name without the prefix 'v_'. Second dimension in the row value as int. Third dimension is the column name.
* @return bool Returns true on success and false on failure.
* @depends database::save()
* @depends database::get_apps()
*/
public function toggle(array $array) {
//return the array
if (!is_array($array)) { return false; }
//set the message id
$m = 0;
//loop through the array
if (!empty($array) && is_array($array)) {
$x = 0;
foreach ($array as $parent_name => $tables) {
if (!empty($tables) && is_array($tables)) {
foreach ($tables as $id => $row) {
//prepare the variables
$parent_name = self::sanitize($parent_name);
$parent_key_name = self::singular($parent_name)."_uuid";
//build the toggle array
if (!empty($row['checked']) && $row['checked'] == 'true') {
//toggle the field value
//$toggle_array[$parent_name][$x][$parent_key_name] = $row[$parent_key_name];
$toggle_array[$parent_name][$x] = $row;
//remove the row from the main array
unset($array[$parent_name][$x]);
}
//loop through the fields
foreach($row as $field_name => $field_value) {
//find the child tables
$y = 0;
if (!empty($field_value) && is_array($field_value)) {
//prepare the variables
$child_name = self::sanitize($field_name);
$child_key_name = self::singular($child_name)."_uuid";
//loop through the child rows
foreach ($field_value as $sub_row) {
//build the delete array
if ($action == 'delete' && $sub_row['checked'] == 'true') {
//delete the child data
$delete_array[$child_name][$y][$child_key_name] = $sub_row[$child_key_name];
//remove the row from the main array
unset($array[$parent_name][$x][$child_name][$y]);
}
//increment the value
$y++;
}
}
}
//increment the value
$x++;
}
}
}
}
//unset the original array
unset($array);
//get the $apps array from the installed apps from the core and mod directories
if (count(self::$apps) == 0) {
self::get_apps();
}
//search through all fields to see if toggle field exists
foreach (self::$apps as $x => $app) {
if (!empty($app['db']) && is_array($app['db'])) {
foreach ($app['db'] as $y => $row) {
if (is_array($row['table']['name'])) {
$table_name = $row['table']['name']['text'];
}
else {
$table_name = $row['table']['name'];
}
if ($table_name === self::TABLE_PREFIX.$parent_name) {
if (is_array($row['fields'])) {
foreach ($row['fields'] as $field) {
if (isset($field['toggle'])) {
$toggle_field = $field['name'];
$toggle_values = $field['toggle'];
}
}
}
}
}
}
}
//if the toggle field and values are empty then set defaults
if (empty($toggle_field)) {
$toggle_field = self::singular($parent_name)."_enabled";
}
if (empty($toggle_values)) {
$toggle_values[] = 'true';
$toggle_values[] = 'false';
}
//get the current values from the database
foreach ($toggle_array as $table_name => $table) {
$x = 0;
foreach($table as $row) {
$child_name = self::sanitize($table_name);
$child_key_name = self::singular($child_name)."_uuid";
$array[$table_name][$x][$child_key_name] = $row[$child_key_name];
$array[$table_name][$x][$toggle_field] = ($row[$toggle_field] === $toggle_values[0]) ? $toggle_values[1] : $toggle_values[0];
$x++;
}
}
unset($toggle_array);
//save the array
return $this->save($array);
} //end function toggle
/**
* Save an array to the database.
* Usage Example:
$database = new database();
$database->app_name = "MyApp";
$database->app_uuid = "12345678-1234-1234-1234-123456789abc";
$row = 0;
$array['mytable'][$row]['mycolumn'] = "myvalue";
if ($database->save($array)) {
echo "Saved Successfully.";
} else {
echo "Save Failed.";
}
* @param array $array Three dimensional Array. The first dimension is the table name without the prefix 'v_'. Second dimension in the row value as int. Third dimension is the column name.
* @param bool $transaction_save
* @return returns and array wih result details
*/
public function save(array &$array, bool $transaction_save = true) {
//prepare the values
$parent_field_names = [];
$child_field_names = [];
$this->message = [];
$parent_key_exists = false;
$parent_key_name = null;
$parent_key_value = null;
$child_key_exists = false;
$child_key_name = null;
$child_key_value = null;
$table_name = null;
$child_table_name = null;
//set default return value
$retval = true;
//return the array
if (!is_array($array)) { return false; }
//set the message id
$m = 0;
//build the json string from the array
$new_json = json_encode($array, JSON_PRETTY_PRINT);
//debug sql
//$this->debug["sql"] = true;
//connect to the database if needed
if (!$this->db) {
$this->connect();
}
//start the atomic transaction
$this->db->beginTransaction();
//loop through the array
if (is_array($array)) foreach ($array as $parent_name => $schema_array) {
if (is_array($schema_array)) foreach ($schema_array as $schema_id => $array) {
//set the variables
$table_name = self::TABLE_PREFIX.$parent_name;
$parent_key_name = self::singular($parent_name)."_uuid";
$parent_key_name = self::sanitize($parent_key_name);
//if the uuid is set then set parent key exists and value
//determine if the parent_key_exists
$parent_key_exists = false;
if (isset($array[$parent_key_name])) {
$parent_key_value = $array[$parent_key_name];
$parent_key_exists = true;
}
else {
if (isset($this->uuid)) {
$parent_key_exists = true;
$parent_key_value = $this->uuid;
}
else {
$parent_key_value = uuid();
}
}
//allow characters found in the uuid only.
$parent_key_value = self::sanitize($parent_key_value);
//get the parent field names
$parent_field_names = array();
if (is_array($array)) {
foreach ($array as $key => $value) {
if (!is_array($value)) {
$parent_field_names[] = self::sanitize($key);
}
}
}
//determine action update or delete and get the original data
if ($parent_key_exists) {
$sql = "SELECT ".implode(", ", $parent_field_names)." FROM ".$table_name." ";
$sql .= "WHERE ".$parent_key_name." = '".$parent_key_value."'; ";
$prep_statement = $this->db->prepare($sql);
if ($prep_statement) {
//get the data
try {
$prep_statement->execute();
$result = $prep_statement->fetchAll(PDO::FETCH_ASSOC);
}
catch(PDOException $e) {
echo $sql."
\n";
echo 'Caught exception: '. $e->getMessage()."
\n";
echo $sql. "
\n";
exit;
}
//set the action
if (count($result) > 0) {
$action = "update";
$old_array[$parent_name] = $result;
}
else {
$action = "add";
}
}
unset($prep_statement, $result);
}
else {
$action = "add";
}
//add a record
if ($action == "add") {
if (permission_exists(self::singular($parent_name).'_add')) {
$params = array();
$sql = "INSERT INTO ".$table_name." ";
$sql .= "(";
if (!$parent_key_exists) {
$sql .= $parent_key_name.", ";
}
if (is_array($array)) {
foreach ($array as $array_key => $array_value) {
if (!is_array($array_value)) {
$array_key = self::sanitize($array_key);
if ($array_key != 'insert_user' &&
$array_key != 'insert_date' &&
$array_key != 'update_user' &&
$array_key != 'update_date') {
$sql .= $array_key.", ";
}
}
}
}
$sql .= "insert_date, ";
$sql .= "insert_user ";
$sql .= ") ";
$sql .= "VALUES ";
$sql .= "(";
if (!$parent_key_exists) {
$sql .= "'".$parent_key_value."', ";
}
if (is_array($array)) {
foreach ($array as $array_key => $array_value) {
if (!is_array($array_value)) {
if ($array_key != 'insert_user' &&
$array_key != 'insert_date' &&
$array_key != 'update_user' &&
$array_key != 'update_date') {
if (!isset($array_value) || $array_value == '') {
$sql .= "null, ";
}
elseif ($array_value === "now()") {
$sql .= "now(), ";
}
elseif ($array_value === "user_uuid()") {
$sql .= ':'.$array_key.", ";
$params[$array_key] = $this->user_uuid ?? null;
}
elseif ($array_value === "remote_address()") {
$sql .= ':'.$array_key.", ";
$params[$array_key] = $_SERVER['REMOTE_ADDR'];
}
else {
$array_value = $array_value ?? '';
$sql .= ':'.$array_key.", ";
$params[$array_key] = trim($array_value);
}
}
}
}
}
$sql .= "now(), ";
$sql .= ":insert_user ";
$sql .= ");";
//add insert user parameter
$params['insert_user'] = $this->user_uuid ?? null;
//set the error mode
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//reduce prepared statement latency
if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
$this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
}
//run the query and return the results
try {
//$this->db->query(check_sql($sql));
$prep_statement = $this->db->prepare($sql);
$prep_statement->execute($params);
unset($prep_statement);
$message["message"] = "OK";
$message["code"] = "200";
$message["uuid"] = $parent_key_value;
$message["details"][$m]["name"] = $this->app_name;
$message["details"][$m]["message"] = "OK";
$message["details"][$m]["code"] = "200";
$message["details"][$m]["uuid"] = $parent_key_value;
$message["details"][$m]["sql"] = $sql;
if (is_array($params)) {
$message["details"][$m]["params"] = $params;
}
unset($params);
$this->message = $message;
$m++;
}
catch(PDOException $e) {
$retval = false;
$message["message"] = "Bad Request";
$message["code"] = "400";
$message["details"][$m]["name"] = $this->app_name;
$message["details"][$m]["message"] = $e->getMessage();
$message["details"][$m]["code"] = "400";
$message["details"][$m]["array"] = $array;
$message["details"][$m]["sql"] = $sql;
if (is_array($params)) {
$message["details"][$m]["params"] = $params;
}
unset($params);
$this->message = $message;
$m++;
}
unset($sql);
}
else {
$retval = false;
$message["name"] = $this->app_name;
$message["message"] = "Forbidden, does not have '".self::singular($parent_name)."_add'";
$message["code"] = "403";
$message["line"] = __line__;
$this->message[] = $message;
$m++;
}
}
//edit a specific uuid
if ($action == "update") {
if (permission_exists(self::singular($parent_name).'_edit')) {
//parent data
$params = array();
$sql = "UPDATE ".$table_name." SET ";
if (is_array($array)) {
foreach ($array as $array_key => $array_value) {
if (!is_array($array_value) && $array_key != $parent_key_name) {
$array_key = self::sanitize($array_key);
if (!isset($array_value) || (isset($array_value) && $array_value === '')) {
$sql .= $array_key." = null, ";
}
elseif ($array_value === "now()") {
$sql .= $array_key." = now(), ";
}
elseif ($array_value === "user_uuid()") {
$sql .= $array_key." = :".$array_key.", ";
$params[$array_key] = $this->user_uuid ?? null;
}
elseif ($array_value === "remote_address()") {
$sql .= $array_key." = :".$array_key.", ";
$params[$array_key] = $_SERVER['REMOTE_ADDR'];
}
else {
$sql .= $array_key." = :".$array_key.", ";
$params[$array_key] = trim($array_value);
}
}
}
}
//add the modified date and user
$sql .= "update_date = now(), ";
$sql .= "update_user = :update_user ";
$params['update_user'] = $this->user_uuid ?? null;
//add the where with the parent name and value
$sql .= "WHERE ".$parent_key_name." = '".$parent_key_value."'; ";
$sql = str_replace(", WHERE", " WHERE", $sql);
//add update user parameter
$params['update_user'] = $this->user_uuid ?? null;
//set the error mode
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//reduce prepared statement latency
if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
$this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
}
//run the query and return the results
try {
$prep_statement = $this->db->prepare($sql);
$prep_statement->execute($params);
//$this->db->query(check_sql($sql));
$message["message"] = "OK";
$message["code"] = "200";
$message["uuid"] = $parent_key_value;
$message["details"][$m]["name"] = $this->app_name;
$message["details"][$m]["message"] = "OK";
$message["details"][$m]["code"] = "200";
$message["details"][$m]["uuid"] = $parent_key_value;
$message["details"][$m]["sql"] = $sql;
if (is_array($params)) {
$message["details"][$m]["params"] = $params;
}
unset($params);
$this->message = $message;
$m++;
unset($sql);
}
catch(PDOException $e) {
$retval = false;
$message["message"] = "Bad Request";
$message["code"] = "400";
$message["details"][$m]["name"] = $this->app_name;
$message["details"][$m]["message"] = $e->getMessage();
$message["details"][$m]["code"] = "400";
$message["details"][$m]["sql"] = $sql;
if (is_array($params)) {
$message["details"][$m]["params"] = $params;
}
unset($params);
$this->message = $message;
$m++;
}
}
else {
$retval = false;
$message["message"] = "Forbidden, does not have '".self::singular($parent_name)."_edit'";
$message["code"] = "403";
$message["line"] = __line__;
$this->message = $message;
$m++;
}
}
//unset the variables
unset($sql, $action);
//child data
if (is_array($array)) {
foreach ($array as $key => $value) {
if (is_array($value)) {
$child_table_name = self::TABLE_PREFIX.$key;
$child_table_name = self::sanitize($child_table_name);
foreach ($value as $id => $row) {
//prepare the variables
$child_name = self::singular($key);
$child_name = self::sanitize($child_name);
$child_key_name = $child_name."_uuid";
//determine if the parent key exists in the child array
$parent_key_exists = false;
if (!isset($array[$parent_key_name])) {
$parent_key_exists = true;
}
//determine if the uuid exists
$uuid_exists = false;
if (is_array($row)) foreach ($row as $k => $v) {
if ($child_key_name == $k) {
if (strlen($v) > 0) {
$child_key_value = trim($v);
$uuid_exists = true;
break;
}
}
else {
$uuid_exists = false;
}
}
//allow characters found in the uuid only
if (isset($child_key_value)) {
$child_key_value = self::sanitize($child_key_value);
}
//get the child field names
$child_field_names = array();
if (is_array($row)) {
foreach ($row as $k => $v) {
if (!is_array($v) && $k !== 'checked') {
$child_field_names[] = self::sanitize($k);
}
}
}
//determine sql update or delete and get the original data
if ($uuid_exists) {
$sql = "SELECT ". implode(", ", $child_field_names)." FROM ".$child_table_name." ";
$sql .= "WHERE ".$child_key_name." = '".$child_key_value."'; ";
try {
$prep_statement = $this->db->prepare($sql);
if ($prep_statement) {
//get the data
$prep_statement->execute();
$child_array = $prep_statement->fetch(PDO::FETCH_ASSOC);
//set the action
if (is_array($child_array)) {
$action = "update";
}
else {
$action = "add";
}
//add to the parent array
if (is_array($child_array)) {
$old_array[$parent_name][$schema_id][$key][] = $child_array;
}
}
unset($prep_statement);
}
catch(PDOException $e) {
echo $sql."
\n";
echo 'Caught exception: '. $e->getMessage()."
\n";
echo $sql. "
\n";
exit;
}
}
else {
$action = "add";
}
//update the child data
if ($action == "update") {
if (permission_exists($child_name.'_edit')) {
$sql = "UPDATE ".$child_table_name." SET ";
if (is_array($row)) {
foreach ($row as $k => $v) {
if (!is_array($v) && ($k != $parent_key_name || $k != $child_key_name)) {
$k = self::sanitize($k);
if (!isset($v) || (isset($v) && $v == '')) {
$sql .= $k." = null, ";
}
elseif ($v === "now()") {
$sql .= $k." = now(), ";
}
elseif ($v === "user_uuid()") {
$sql .= $k." = :".$k.", ";
$params[$k] = $this->user_uuid ?? null;
}
elseif ($v === "remote_address()") {
$sql .= $k." = :".$k.", ";
$params[$k] = $_SERVER['REMOTE_ADDR'];
}
else {
$sql .= $k." = :".$k.", ";
$params[$k] = isset($v) ? trim($v) : null;
}
}
}
}
//add the modified date and user
$sql .= "update_date = now(), ";
$sql .= "update_user = :update_user ";
$params['update_user'] = $this->user_uuid ?? null;
//add the where with the parent name and value
$sql .= "WHERE ".$parent_key_name." = '".$parent_key_value."' ";
$sql .= "AND ".$child_key_name." = '".$child_key_value."'; ";
$sql = str_replace(", WHERE", " WHERE", $sql);
//set the error mode
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//reduce prepared statement latency
if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
$this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
}
//$prep_statement->bindParam(':domain_uuid', $this->domain_uuid );
try {
//$this->db->query(check_sql($sql));
$prep_statement = $this->db->prepare($sql);
$prep_statement->execute($params);
unset($prep_statement);
$message["details"][$m]["name"] = $key;
$message["details"][$m]["message"] = "OK";
$message["details"][$m]["code"] = "200";
$message["details"][$m]["uuid"] = $child_key_value;
$message["details"][$m]["sql"] = $sql;
if (is_array($params)) {
$message["details"][$m]["params"] = $params;
}
unset($params);
$this->message = $message;
$m++;
}
catch(PDOException $e) {
$retval = false;
if ($message["code"] == "200") {
$message["message"] = "Bad Request";
$message["code"] = "400";
}
$message["details"][$m]["name"] = $key;
$message["details"][$m]["message"] = $e->getMessage();
$message["details"][$m]["code"] = "400";
$message["details"][$m]["sql"] = $sql;
if (is_array($params)) {
$message["details"][$m]["params"] = $params;
}
unset($params);
$this->message = $message;
$m++;
}
}
else {
$retval = false;
$message["name"] = $child_name;
$message["message"] = "Forbidden, does not have '".$child_name."_edit'";
$message["code"] = "403";
$message["line"] = __line__;
$this->message = $message;
$m++;
}
} //action update
//add the child data
if ($action == "add") {
if (permission_exists($child_name.'_add')) {
//determine if child or parent key exists
$child_key_name = $child_name.'_uuid';
$parent_key_exists = false;
$child_key_exists = false;
if (is_array($row)) {
foreach ($row as $k => $v) {
if ($k == $parent_key_name) {
$parent_key_exists = true;
}
if ($k == $child_key_name) {
$child_key_exists = true;
$child_key_value = trim($v);
}
}
}
if (!isset($child_key_value) || $child_key_value == '') {
$child_key_value = uuid();
}
//build the insert
$sql = "INSERT INTO ".$child_table_name." ";
$sql .= "(";
if (!$parent_key_exists) {
$sql .= self::singular($parent_key_name).", ";
}
if (!$child_key_exists) {
$sql .= self::singular($child_key_name).", ";
}
if (is_array($row)) {
foreach ($row as $k => $v) {
if (!is_array($v)) {
$k = self::sanitize($k);
if ($k != 'insert_user' &&
$k != 'insert_date' &&
$k != 'update_user' &&
$k != 'update_date') {
$sql .= $k.", ";
}
}
}
}
$sql .= "insert_date, ";
$sql .= "insert_user ";
$sql .= ") ";
$sql .= "VALUES ";
$sql .= "(";
if (!$parent_key_exists) {
$sql .= "'".$parent_key_value."', ";
}
if (!$child_key_exists) {
$sql .= "'".$child_key_value."', ";
}
if (is_array($row)) {
foreach ($row as $k => $v) {
if (!is_array($v)) {
if ($k != 'insert_user' &&
$k != 'insert_date' &&
$k != 'update_user' &&
$k != 'update_date') {
if (!isset($v) || strlen($v) == 0) {
$sql .= "null, ";
}
elseif ($v === "now()") {
$sql .= "now(), ";
}
elseif ($v === "user_uuid()") {
$sql .= ':'.$k.", ";
$params[$k] = $this->user_uuid ?? null;
}
elseif ($v === "remote_address()") {
$sql .= ':'.$k.", ";
$params[$k] = $_SERVER['REMOTE_ADDR'];
}
else {
$k = self::sanitize($k);
if ($k != 'insert_user' &&
$k != 'insert_date' &&
$k != 'update_user' &&
$k != 'update_date') {
$sql .= ':'.$k.", ";
$params[$k] = trim($v);
}
}
}
}
}
}
$sql .= "now(), ";
$sql .= ":insert_user ";
$sql .= ");";
//add insert user parameter
$params['insert_user'] = $this->user_uuid ?? null;
//set the error mode
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//reduce prepared statement latency
if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
$this->db->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
}
//run the query and return the results
try {
$prep_statement = $this->db->prepare($sql);
$prep_statement->execute($params);
unset($prep_statement);
$message["details"][$m]["name"] = $key;
$message["details"][$m]["message"] = "OK";
$message["details"][$m]["code"] = "200";
$message["details"][$m]["uuid"] = $child_key_value;
$message["details"][$m]["sql"] = $sql;
if (is_array($params)) {
$message["details"][$m]["params"] = $params;
}
unset($params);
$this->message = $message;
$m++;
}
catch(PDOException $e) {
$retval = false;
if ($message["code"] == "200") {
$message["message"] = "Bad Request";
$message["code"] = "400";
}
$message["details"][$m]["name"] = $key;
$message["details"][$m]["message"] = $e->getMessage();
$message["details"][$m]["code"] = "400";
$message["details"][$m]["sql"] = $sql;
if (is_array($params)) {
$message["details"][$m]["params"] = $params;
}
unset($params);
$this->message = $message;
$m++;
}
}
else {
$retval = false;
$message["name"] = $child_name;
$message["message"] = "Forbidden, does not have '".$child_name."_add'";
$message["code"] = "403";
$message["line"] = __line__;
$this->message = $message;
$m++;
}
} //action add
//unset the variables
unset($sql, $action, $child_key_name, $child_key_value);
} // foreach value
} //is array
} //foreach array
}
} // foreach schema_array
} // foreach main array
//save the message
$this->message = $message;
//commit the atomic transaction
$this->db->commit();
//set the action if not set
if (empty($action)) {
if (!empty($old_array)) {
$transaction_type = 'update';
}
else {
$transaction_type = 'add';
}
}
else {
$transaction_type = $action;
}
//log the transaction results
if ($transaction_save && file_exists($_SERVER["PROJECT_ROOT"]."/app/database_transactions/app_config.php")) {
try {
//get the domain_uuid
$domain_uuid = '';
foreach ($old_array as $data_array) {
foreach ($data_array as $row) {
if (!empty($row['domain_uuid'])) {
$domain_uuid = $row['domain_uuid'];
}
}
}
//insert the transaction into the database
$sql = "insert into ".self::TABLE_PREFIX."database_transactions ";
$sql .= "(";
$sql .= "database_transaction_uuid, ";
if (isset($domain_uuid) && is_uuid($domain_uuid)) {
$sql .= "domain_uuid, ";
}
if (isset($this->user_uuid) && is_uuid($this->user_uuid)) {
$sql .= "user_uuid, ";
}
if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
$sql .= "app_uuid, ";
}
if (isset($this->app_name) && !empty($this->app_name)) {
$sql .= "app_name, ";
}
$sql .= "transaction_code, ";
$sql .= "transaction_address, ";
$sql .= "transaction_type, ";
$sql .= "transaction_date, ";
$sql .= "transaction_old, ";
$sql .= "transaction_new, ";
$sql .= "transaction_result ";
$sql .= ")";
$sql .= "values ";
$sql .= "(";
$sql .= "'".uuid()."', ";
if (isset($domain_uuid) && is_uuid($domain_uuid)) {
$sql .= ":domain_uuid, ";
}
if (isset($this->user_uuid) && is_uuid($this->user_uuid)) {
$sql .= ":user_uuid, ";
}
if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
$sql .= ":app_uuid, ";
}
if (isset($this->app_name) && !empty($this->app_name)) {
$sql .= ":app_name, ";
}
$sql .= "'".$message["code"]."', ";
$sql .= ":remote_address, ";
$sql .= "'".$transaction_type."', ";
$sql .= "now(), ";
if (!empty($old_array)) {
$sql .= ":transaction_old, ";
}
else {
$sql .= "null, ";
}
if (!empty($array)) {
$sql .= ":transaction_new, ";
}
else {
$sql .= "null, ";
}
$sql .= ":transaction_result ";
$sql .= ")";
$statement = $this->db->prepare($sql);
if (isset($domain_uuid) && is_uuid($domain_uuid)) {
$statement->bindParam(':domain_uuid', $domain_uuid);
}
if (isset($this->user_uuid) && is_uuid($this->user_uuid)) {
$statement->bindParam(':user_uuid', $this->user_uuid);
}
if (isset($this->app_uuid) && is_uuid($this->app_uuid)) {
$statement->bindParam(':app_uuid', $this->app_uuid);
}
if (isset($this->app_name) && !empty($this->app_name)) {
$statement->bindParam(':app_name', $this->app_name);
}
$statement->bindParam(':remote_address', $_SERVER['REMOTE_ADDR']);
if (!empty($old_array)) {
$old_json = json_encode($old_array, JSON_PRETTY_PRINT);
$statement->bindParam(':transaction_old', $old_json);
}
if (!empty($new_json)) {
$statement->bindParam(':transaction_new', $new_json);
}
$message = json_encode($this->message, JSON_PRETTY_PRINT);
$statement->bindParam(':transaction_result', $message);
$statement->execute();
unset($sql);
}
catch(PDOException $e) {
echo $e->getMessage();
exit;
}
}
return $this->message;
} //save method
/**
* Converts a plural English word to singular.
* @param string $word English word
* @return string Singular version of English word
* @internal Moved to class to conserve resources.
*/
public static function singular(string $word) {
//"-es" is used for words that end in "-x", "-s", "-z", "-sh", "-ch" in which case you add
if (substr($word, -2) == "es") {
if (substr($word, -4) == "sses") { // eg. 'addresses' to 'address'
return substr($word,0,-2);
}
elseif (substr($word, -3) == "ses") { // eg. 'databases' to 'database' (necessary!)
return substr($word,0,-1);
}
elseif (substr($word, -3) == "ies") { // eg. 'countries' to 'country'
return substr($word,0,-3)."y";
}
elseif (substr($word, -3, 1) == "x") {
return substr($word,0,-2);
}
elseif (substr($word, -3, 1) == "s") {
return substr($word,0,-2);
}
elseif (substr($word, -3, 1) == "z") {
return substr($word,0,-2);
}
elseif (substr($word, -4, 2) == "sh") {
return substr($word,0,-2);
}
elseif (substr($word, -4, 2) == "ch") {
return substr($word,0,-2);
}
else {
return rtrim($word, "s");
}
}
else {
return rtrim($word, "s");
}
}
/**
* Gets the $apps array from the installed apps from the core and mod directories and writes it to self::$apps overwriting previous values.
* @uses $_SERVER['DOCUMENT_ROOT'] Global variable
* @uses PROJECT_PATH Global variable
* @return null Does not return any values
* @internal Moved to class to conserve resources.
*/
public static function get_apps() {
//get the $apps array from the installed apps from the core and mod directories
$config_list = glob($_SERVER["DOCUMENT_ROOT"] . PROJECT_PATH . "/*/*/app_config.php");
$x = 0;
if (is_array($config_list)) {
foreach ($config_list as $config_path) {
include($config_path);
$x++;
}
}
self::$apps = $apps;
}
/**
* Returns the depth of an array
* @param array $array Reference to array
* @return int Depth of array
* @internal Moved to class to conserve resources.
*/
public static function array_depth(array &$array) {
$depth = 0;
if (is_array($array)) {
$depth++;
foreach ($array as $value) {
if (is_array($value)) {
$depth = self::array_depth($value) + 1;
}
}
}
return $depth;
}
/**
* Searches through all fields to see if domain_uuid exists
* @param string $name
* @uses self::$apps directly
* @return boolean true on success and false on failure
* @see database::get_apps()
*/
public static function domain_uuid_exists($name) {
//get the $apps array from the installed apps from the core and mod directories
if (count(self::$apps) == 0) {
self::get_apps();
}
//search through all fields to see if domain_uuid exists
foreach (self::$apps as $x => &$app) {
if (is_array($app['db'])) {
foreach ($app['db'] as $y => $row) {
if (is_array($row['table']['name'])) {
$table_name = $row['table']['name']['text'];
}
else {
$table_name = $row['table']['name'];
}
if ($table_name === self::TABLE_PREFIX.$name) {
if (is_array($row['fields'])) {
foreach ($row['fields'] as $field) {
if ($field['name'] == "domain_uuid") {
return true;
}
} //foreach
} //is array
}
} //foreach
} //is array
} //foreach
//not found
return false;
}
/**
* Get Relations searches through all fields to find relations
* @param string $schema Table name
* @return array Returns array or false
* @internal Moved to class to conserve resources.
*/
public static function get_relations($schema) {
//remove the v_ prefix
if (substr($schema, 0, strlen(self::TABLE_PREFIX)) == self::TABLE_PREFIX) {
$schema = substr($schema, strlen(self::TABLE_PREFIX));
}
//sanitize the values
$schema = self::sanitize($schema);
//get the apps array
$config_list = [];
$directories = ["core", "app"];
$applications = [$schema, self::singular($schema)];
foreach ($directories as $directory) {
foreach ($applications as $application) {
$path = $_SERVER["DOCUMENT_ROOT"] . PROJECT_PATH . "/$directory/$application/app_config.php";
$app_config_files = glob($path);
if ($app_config_files !== false) {
$config_list = array_merge($config_list, $app_config_files);
}
}
}
$x = 0;
foreach ($config_list as $config_path) {
include($config_path);
$x++;
}
//search through all fields to find relations
if (!empty($apps) && is_array($apps)) {
foreach ($apps as $x => $app) {
foreach ($app['db'] as $y => $row) {
foreach ($row['fields'] as $z => $field) {
if (!empty($field['deprecated']) && $field['deprecated'] != "true") {
if (!empty($field['key']['type']) && $field['key']['type'] == "foreign") {
if ($row['table']['name'] == self::TABLE_PREFIX.$schema || $field['key']['reference']['table'] == self::TABLE_PREFIX.$schema) {
//get the field name
if (!empty($field['name']) && is_array($field['name'])) {
$field_name = trim($field['name']['text']);
}
else {
$field_name = trim($field['name']);
}
//build the array
$relations[$i]['table'] = $row['table']['name'];
$relations[$i]['field'] = $field_name;
$relations[$i]['key']['type'] = $field['key']['type'];
$relations[$i]['key']['table'] = $field['key']['reference']['table'];
$relations[$i]['key']['field'] = $field['key']['reference']['field'];
if (isset($field['key']['reference']['action'])) {
$relations[$i]['key']['action'] = $field['key']['reference']['action'];
}
//increment the value
$i++;
}
}
}
unset($field_name);
}
}
}
}
//return the array
if (!empty($relations) && is_array($relations)) {
return $relations;
} else {
return false;
}
}
/**
* Returns a sanitized string value safe for database or table name.
* @param string $value To be sanitized
* @return string Sanitized using preg_replace('#[^a-zA-Z0-9_\-]#', '')
* @see preg_replace()
*/
public static function sanitize(string $value) {
return preg_replace('#[^a-zA-Z0-9_\-]#', '', $value);
}
/**
* Returns a new connected database object.
* This allows a shortcut for a common syntax. For more information
* on how the connection happens see {@link database::__construct()} and
* {@link database::connect()}
* Usage:
* $database_object = database::new();
* @return database new instance of database object already connected
* @see database::__construct()
* @see database::connect()
*/
public static function new(array $params = []) {
if (self::$database === null) {
self::$database = new database($params);
self::$database->connect();
}
return self::$database;
}
} //class database
} //!class_exists
//addtitional functions for sqlite
if (!function_exists('php_md5')) {
function php_md5($string) {
return md5($string);
}
}
if (!function_exists('php_unix_time_stamp')) {
function php_unix_time_stamp($string) {
return strtotime($string);
}
}
if (!function_exists('php_now')) {
function php_now() {
return date("Y-m-d H:i:s");
}
}
if (!function_exists('php_left')) {
function php_left($string, $num) {
return substr($string, 0, $num);
}
}
if (!function_exists('php_right')) {
function php_right($string, $num) {
return substr($string, (strlen($string)-$num), strlen($string));
}
}
/*
//example usage
//find
require_once "resources/classes/database.php";
$database = new database;
$database->domain_uuid = $_SESSION["domain_uuid"];
$database->type = $db_type;
$database->table = "v_extensions";
$where[0]['name'] = 'domain_uuid';
$where[0]['value'] = $_SESSION["domain_uuid"];
$where[0]['operator'] = '=';
$database->where = $where;
$order_by[0]['name'] = 'extension';
$database->order_by = $order_by;
$database->order_type = 'desc';
$database->limit = '2';
$database->offset = '0';
$database->find();
print_r($database->result);
//insert
require_once "resources/classes/database.php";
$database = new database;
$database->domain_uuid = $_SESSION["domain_uuid"];
$database->table = "v_ivr_menus";
$fields[0]['name'] = 'domain_uuid';
$fields[0]['value'] = $_SESSION["domain_uuid"];
echo $database->count();
*/
?>