From ba3b60b07ba1307f8e89346783437472d60efe0e Mon Sep 17 00:00:00 2001 From: frytimo Date: Sat, 17 Sep 2022 17:44:21 -0400 Subject: [PATCH] Move database methods to static and document database class (#6474) * Add new static method to created newly connected database object * Document database class and clean up and document some of the methods. This removes the methods that should not be in each instance and places them in the single instance class as to occupy less resources and be able to create database objects more efficiently. * More docs & removed the ability to set any value within the object. Co-authored-by: Tim Fry --- app/contacts/resources/classes/contacts.php | 8 +- app/dialplans/dialplan_edit.php | 3 +- .../user_logs/resources/classes/user_logs.php | 4 +- resources/classes/database.php | 1329 +++++++++++------ 4 files changed, 842 insertions(+), 502 deletions(-) diff --git a/app/contacts/resources/classes/contacts.php b/app/contacts/resources/classes/contacts.php index 9677767420..46c435917b 100644 --- a/app/contacts/resources/classes/contacts.php +++ b/app/contacts/resources/classes/contacts.php @@ -119,7 +119,7 @@ if (!class_exists('contacts')) { $p = new permissions; $database = new database; foreach ($this->tables as $table) { - $p->add($database->singular($table).'_delete', 'temp'); + $p->add(database::singular($table).'_delete', 'temp'); } //execute delete @@ -132,7 +132,7 @@ if (!class_exists('contacts')) { //revoke temp permissions $database = new database; foreach ($this->tables as $table) { - $p->delete($database->singular($table).'_delete', 'temp'); + $p->delete(database::singular($table).'_delete', 'temp'); } //set message @@ -163,11 +163,11 @@ if (!class_exists('contacts')) { $x = 0; foreach ($records as $property_name => $properties) { $database = new database; - if (permission_exists($database->singular($property_name).'_delete')) { + if (permission_exists(database::singular($property_name).'_delete')) { if (is_array($properties) && @sizeof($properties) != 0) { foreach ($properties as $property) { if ($property['checked'] == 'true' && is_uuid($property['uuid'])) { - $array[$property_name][$x][$database->singular($property_name).'_uuid'] = $property['uuid']; + $array[$property_name][$x][database::singular($property_name).'_uuid'] = $property['uuid']; $array[$property_name][$x]['contact_uuid'] = $this->contact_uuid; $array[$property_name][$x]['domain_uuid'] = $_SESSION['domain_uuid']; $x++; diff --git a/app/dialplans/dialplan_edit.php b/app/dialplans/dialplan_edit.php index 8ace6b4186..4858a9efab 100644 --- a/app/dialplans/dialplan_edit.php +++ b/app/dialplans/dialplan_edit.php @@ -273,7 +273,8 @@ $database = new database; $database->app_name = 'dialplans'; $database->app_uuid = $app_uuid; - $database->uuid($dialplan_uuid); + if ( strlen($dialplan_uuid)>0 ) + $database->uuid($dialplan_uuid); $database->save($array); unset($array); diff --git a/core/user_logs/resources/classes/user_logs.php b/core/user_logs/resources/classes/user_logs.php index be19e55f3e..73326487dd 100644 --- a/core/user_logs/resources/classes/user_logs.php +++ b/core/user_logs/resources/classes/user_logs.php @@ -73,6 +73,7 @@ if (!class_exists('user_logs')) { * add user_logs */ public static function add($result) { + $array = []; //prepare the array $array['user_logs'][0]["timestamp"] = 'now()'; $array['user_logs'][0]["domain_uuid"] = $result['domain_uuid']; @@ -97,7 +98,8 @@ if (!class_exists('user_logs')) { $database = new database; $database->app_name = 'authentication'; $database->app_uuid = 'a8a12918-69a4-4ece-a1ae-3932be0e41f1'; - $database->uuid($user_log_uuid); + if (strlen($user_log_uuid)>0) + $database->uuid($user_log_uuid); $database->save($array, false); $message = $database->message; diff --git a/resources/classes/database.php b/resources/classes/database.php index 7ca3800b95..5cd5fef2eb 100644 --- a/resources/classes/database.php +++ b/resources/classes/database.php @@ -30,43 +30,340 @@ include "root.php"; if (!class_exists('database')) { class database { + const TABLE_PREFIX = "v_"; + /** - * Define the class variables + * Database connection + * @access private + * @var PDO object */ - public $db; - public $driver; - public $type; - public $host; - public $port; - public $db_name; - public $db_secure; - public $db_cert_authority; - public $username; - public $password; - public $path; - public $table; - public $where; //array - public $order_by; //array - public $order_type; - public $limit; - public $offset; - public $fields; - public $count; - public $sql; - public $result; - public $app_name; - public $app_uuid; - public $domain_uuid; + private $db; + /** + * Driver to use. + * @access private + * @var string Can be pgsql, mysql, sqlite, odbc + */ + private $driver; + /** + * Alias of driver. + * @access private + * @var string Can be pgsql, mysql, sqlite, odbc + * @see $driver + */ + private $type; + /** + * Host for database connection + * @access private + * @var string host name or IP address. + */ + private $host; + /** + * Port number + * @access private + * @var int 1025 - 65534 + */ + private $port; + /** + * Database name + * @access private + * @var string + */ + private $db_name; + /** + * Database security + * @access private + * @var boolean + */ + private $db_secure; + /** + * Specifies the file name of the client SSL certificate + * @access private + * @var string full path + */ + private $db_cert_authority; + /** + * Username used to connect + * @access private + * @var string + */ + private $username; + /** + * Password used to connect + * @access private + * @var string + */ + private $password; + /** + * Full path to file name. + * @access private + * @var string full path to file name + */ + private $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: + *

  1. 'name' - Any valid column name.
  2. + *
  3. 'operator' - Must be one of the following values: =, >, <, >=, <=, <>, !=
  4. + *
  5. '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 $order_by + */ + private $where; //array + /** + * Order By clause(s) of an SQL statement. + *

Array of arrays must be passed with each having the + * following keys: + *

  1. 'name' - Any valid column name.
  2. + *
  3. 'operator' - Must be one of the following values: =, >, <, >=, <=, <>, !=
  4. + *
  5. '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 + */ + private $order_by; //array + /** + * Ascending or Descending order. + * @var string + * @access private + */ + private $order_type; + /** + * Numerical value to limit returned results. + * @var int Used for 'LIMIT' in SQL statement. + * @access private + */ + private $limit; + /** + * Numerical value to offset returned results. + * @var int Used for 'OFFSET' in SQL statement. + * @access private + */ + private $offset; + /** + *

Array of fields.

+ *

Fields are specified in 'name'=>'value' format. + *

Used by {@link database::add() } and {@link database::update() }

+ * @access private + * @var array Array of columns + * @see database::add() + * @see database::update() + */ + private $fields; + /** + * Unknown property + * @var unknown + * @access private + */ + private $count; + /** + * Unknown property + * @var unknown + * @access private + */ + private $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 name making the request. + * @var string App name making database request. + * @see $app_uuid + * @access private + */ + private $app_name; + /** + * Stores the application UUID making the request. + * @var string + * @see $app_name + * @access private + */ + private $app_uuid; + /** + *

Stores the domain UUID making the request.

+ *

This is defaulted to the Session domain UUID.

+ * @access private + * @uses $_SESSION['domain_uuid']
Default value upon object creation + * @var string Domain UUID making request. + */ + private $domain_uuid; + + /** + *

Message for the query results.

+ * @var array Contains the message array after a query + * @access private + */ + private $message; /** * Called when the object is created */ public function __construct() { - if (!isset($this->domain_uuid) && isset($_SESSION['domain_uuid'])) { +// $this->domain_uuid is still null +// if (!isset($this->domain_uuid) && isset($_SESSION['domain_uuid'])) { + if (isset($_SESSION['domain_uuid'])) { $this->domain_uuid = $_SESSION['domain_uuid']; } } + /** + *

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; } + else { trigger_error('Domain or App UUID not valid', E_USER_ERROR); } + 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': + return $this->$name; + case 'count': + return $this->count(); + default: + trigger_error('Object property not available', E_USER_ERROR); + } + } + /** * Called when there are no references to a particular object * unset the variables used in the class @@ -78,7 +375,11 @@ include "root.php"; } /** - * Connect to the database + *

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() { @@ -123,12 +424,6 @@ include "root.php"; if (!isset($this->password) && isset($db_password)) { $this->password = $db_password; } if (!isset($this->path) && isset($db_path)) { $this->path = $db_path; } } - if (strlen($this->driver) == 0) { - $this->driver = $this->type; - } - - //sanitize the database name - $this->db_name = preg_replace('#[^a-zA-Z0-9_\-\.]#', '', $this->db_name); if ($this->driver == "sqlite") { if (strlen($this->db_name) == 0) { @@ -195,7 +490,7 @@ include "root.php"; try { if (strlen($this->host) > 0) { if (strlen($this->port) == 0) { $this->port = "5432"; } - if ($this->db_secure == true) { + 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=verify-ca sslrootcert=$this->db_cert_authority"); } else { @@ -223,7 +518,13 @@ include "root.php"; } } - public function tables() { + /** + * Returns the table names from the database. + * @return array tables + * @depends connect() + */ + public function tables() : array { + $result = []; //connect to the database if needed if (!$this->db) { $this->connect(); @@ -267,7 +568,12 @@ include "root.php"; return $result; } - public function table_info() { + /** + * Returns table information from the database. + * @return array table info + * @depends connect() + */ + public function table_info() : array { //public $db; //public $type; //public $table; @@ -277,9 +583,7 @@ include "root.php"; if (!$this->db) { $this->connect(); } - //sanitize the names - $this->table = preg_replace('#[^a-zA-Z0-9_\-]#', '', $this->table); - $this->db_name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $this->db_name); + //get the table info if (strlen($this->table) == 0) { return false; } if ($this->type == "sqlite") { @@ -310,7 +614,20 @@ include "root.php"; return $prep_statement->fetchAll(PDO::FETCH_ASSOC); } - public function table_exists ($db_type, $db_name, $table_name) { + /** + * 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) : bool { + 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(); @@ -318,13 +635,13 @@ include "root.php"; //query table store to see if the table exists $sql = ""; - if ($db_type == "sqlite") { + if ($this->type == "sqlite") { $sql .= "SELECT * FROM sqlite_master WHERE type='table' and name='$table_name' "; } - if ($db_type == "pgsql") { + if ($this->type == "pgsql") { $sql .= "select * from pg_tables where schemaname='public' and tablename = '$table_name' "; } - if ($db_type == "mysql") { + if ($this->type == "mysql") { $sql .= "SELECT TABLE_NAME FROM information_schema.tables WHERE table_schema = '$db_name' and TABLE_NAME = '$table_name' "; } $prep_statement = $this->db->prepare($sql); @@ -338,12 +655,18 @@ include "root.php"; } } - public function fields() { + /** + * Queries {@link database::table_info()} to return the fields. + * @access public + * @return array Two dimensional array + * @depends table_info() + */ + public function fields() : array { //public $db; //public $type; //public $table; //public $name; - + $result = []; //get the table info $table_info = $this->table_info(); @@ -381,10 +704,18 @@ include "root.php"; return $result; } - //public function disconnect() { - // return null; - //} - + /** + * Searches database using the following object properties: + *
    + *
  1. table - sanitized name of the table {@see database::table}
  2. + *
  3. where - where clause {@see database::where}
  4. + *
  5. order_by - order_by clause {@see database::order_by}
  6. + *
  7. limit - limit clause {@see database::limit}
  8. + *
  9. offset - offset clause {@see database::offset}
  10. + *
+ * @return boolean + * @depends connect() + */ public function find() { //connect; //table; @@ -397,8 +728,7 @@ include "root.php"; if (!$this->db) { $this->connect(); } - //sanitize the name - $this->table = preg_replace('#[^a-zA-Z0-9_\-]#', '', $this->table); + //get data from the database $sql = "select * from ".$this->table." "; if ($this->where) { @@ -406,7 +736,7 @@ include "root.php"; if (is_array($this->where)) { foreach($this->where as $row) { //sanitize the name - $array['name'] = preg_replace('#[^a-zA-Z0-9_\-]#', '', $array['name']); + $array['name'] = self::sanitize($array['name']); //validate the operator switch ($row['operator']) { @@ -415,7 +745,6 @@ include "root.php"; case "<=": break; case ">=": break; case "=": break; - case ">=": break; case "<>": break; case "!=": break; default: @@ -447,7 +776,7 @@ include "root.php"; if (is_array($this->order_by)) { foreach($this->order_by as $row) { //sanitize the name - $row['name'] = preg_replace('#[^a-zA-Z0-9_\-]#', '', $row['name']); + $row['name'] = self::sanitize($row['name']); //sanitize the order switch ($row['order']) { @@ -555,7 +884,7 @@ include "root.php"; $this->connect(); } //sanitize the table name - $this->table = preg_replace('#[^a-zA-Z0-9_\-]#', '', $this->table); + //$this->table = self::sanitize($this->table); // no longer needed //count the fields $field_count = count($this->fields); //add data to the database @@ -564,7 +893,7 @@ include "root.php"; $i = 1; if (is_array($this->fields)) { foreach($this->fields as $name => $value) { - $name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $name); + $name = self::sanitize($name); if (count($this->fields) == $i) { $sql .= $name." \n"; } @@ -580,7 +909,7 @@ include "root.php"; $i = 1; if (is_array($this->fields)) { foreach($this->fields as $name => $value) { - $name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $name); + $name = self::sanitize($name); if ($field_count == $i) { if (strlen($value) > 0) { //$sql .= "'".$value."' "; @@ -640,13 +969,13 @@ include "root.php"; $this->connect(); } //sanitize the table name - $this->table = preg_replace('#[^a-zA-Z0-9_\-]#', '', $this->table); + //$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 = preg_replace('#[^a-zA-Z0-9_\-]#', '', $name); + $name = self::sanitize($name); if (count($this->fields) == $i) { if (strlen($name) > 0 && $value == null) { $sql .= $name." = null "; @@ -675,7 +1004,7 @@ include "root.php"; foreach($this->where as $row) { //sanitize the name - $row['name'] = preg_replace('#[^a-zA-Z0-9_\-]#', '', $row['name']); + $row['name'] = self::sanitize($row['name']); //validate the operator switch ($row['operator']) { @@ -684,7 +1013,6 @@ include "root.php"; case "<=": break; case ">=": break; case "=": break; - case ">=": break; case "<>": break; case "!=": break; default: @@ -718,10 +1046,10 @@ include "root.php"; unset($sql); } - public function delete($array) { - + public function delete(array $array) { + $retval = true; //return the array - if (!is_array($array)) { echo "not an array"; return false; } + if (!is_array($array)) { return false; } //connect to the database if needed if (!$this->db) { @@ -731,88 +1059,71 @@ include "root.php"; //set the message id $m = 0; - //set the app name - if (!isset($this->app_name)) { - $this->app_name = $this->name; - } - - //set the table prefix - $table_prefix = 'v_'; - //debug sql $this->debug["sql"] = true; - //debug info - //echo "
\n";
-					//print_r($delete_array);
-					//echo "
\n"; - //exit; - //set the message id $m = 0; //loop through the array $checked = false; - if (is_array($array)) { + $x = 0; + foreach ($array as $parent_name => $tables) { + if (is_array($tables)) { + foreach ($tables as $id => $row) { - $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"; - //prepare the variables - $parent_name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $parent_name); - $parent_key_name = $this->singular($parent_name)."_uuid"; + //build the delete array + if ($row['checked'] == 'true') { + //set checked to true + $checked = true; - //build the delete array - if ($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]); + //delete the child data + if (isset($row[$parent_key_name])) { + $new_array[$parent_name][$x][$parent_key_name] = $row[$parent_key_name]; } - //loop through the fields - foreach($row as $field_name => $field_value) { + //remove the row from the main array + unset($array[$parent_name][$x]); + } - //find the child tables - $y = 0; - if (is_array($field_value)) { - //prepare the variables - $child_name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $field_name); - $child_key_name = $this->singular($child_name)."_uuid"; + //loop through the fields + foreach($row as $field_name => $field_value) { - //loop through the child rows - foreach ($field_value as $sub_row) { + //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"; - //build the delete array - if ($row['checked'] == 'true') { - //set checked to true - $checked = true; + //loop through the child rows + foreach ($field_value as $sub_row) { - //delete the child data - $new_array[$child_name][][$child_key_name] = $sub_row[$child_key_name]; + //build the delete array + if ($row['checked'] == 'true') { + //set checked to true + $checked = true; - //remove the row from the main array - unset($array[$parent_name][$x][$child_name][$y]); - } + //delete the child data + $new_array[$child_name][][$child_key_name] = $sub_row[$child_key_name]; - //increment the value - $y++; + //remove the row from the main array + unset($array[$parent_name][$x][$child_name][$y]); } + + //increment the value + $y++; } } + } - //increment the value - $x++; + //increment the value + $x++; - } } } } @@ -823,7 +1134,7 @@ include "root.php"; } //get the current data - if (is_array($new_array) && count($new_array) > 0) { + if (count($new_array) > 0) { //build an array of tables, fields, and values foreach($new_array as $table_name => $rows) { foreach($rows as $row) { @@ -836,15 +1147,15 @@ include "root.php"; //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 = preg_replace('#[^a-zA-Z0-9_\-]#', '', $table_name); - $sql = "select * from ".$table_prefix.$table_name." "; + $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 = preg_replace('#[^a-zA-Z0-9_\-]#', '', $field_name); + $field_name = self::sanitize($field_name); if ($i > 0) { $sql .= " ,"; } $sql .= " :".$field_name."_".$i." "; $i++; @@ -867,12 +1178,12 @@ include "root.php"; } //get relations array - $relations = $this->get_relations($parent_name); + $relations = self::get_relations($parent_name); //add child data to the old array foreach($old_array as $parent_name => $rows) { //get relations array - $relations = $this->get_relations($parent_name); + $relations = self::get_relations($parent_name); //loop through the rows $x = 0; @@ -884,12 +1195,12 @@ include "root.php"; $child_table = $relation['table']; //remove the v_ prefix - if (substr($child_table, 0, 2) == "v_") { - $child_table = substr($child_table, 2); + 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 ".$table_prefix.$child_table." "; + $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'); @@ -900,7 +1211,7 @@ include "root.php"; //delete the child data if (isset($row[$relation['field']]) && strlen($row[$relation['field']]) > 0) { - $sql = "delete from ".$table_prefix.$child_table." "; + $sql = "delete from ".self::TABLE_PREFIX.$child_table." "; $sql .= "where ".$relation['field']." = :".$relation['field']; $parameters[$relation['field']] = $row[$relation['field']]; // $this->execute($sql, $parameters); @@ -918,54 +1229,53 @@ include "root.php"; $this->db->beginTransaction(); //delete the current data - if (is_array($new_array)) { - foreach($new_array as $table_name => $rows) { - //echo "table: ".$table_name."\n"; - foreach($rows as $row) { - if (permission_exists($this->singular($table_name).'_delete')) { - $sql = "delete from ".$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++; + 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->name; + $message["details"][$m]["message"] = "OK"; + $message["details"][$m]["code"] = "200"; + //$message["details"][$m]["uuid"] = $parent_key_value; + if ($this->debug["sql"]) { + $message["details"][$m]["sql"] = $sql; } - try { - $this->execute($sql, $parameters); - $message["message"] = "OK"; - $message["code"] = "200"; - $message["uuid"] = $id; - $message["details"][$m]["name"] = $this->name; - $message["details"][$m]["message"] = "OK"; - $message["details"][$m]["code"] = "200"; - //$message["details"][$m]["uuid"] = $parent_key_value; - if ($this->debug["sql"]) { - $message["details"][$m]["sql"] = $sql; - } - $this->message = $message; - $m++; - unset($sql); - unset($statement); + $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->name; + $message["details"][$m]["message"] = $e->getMessage(); + $message["details"][$m]["code"] = "400"; + if ($this->debug["sql"]) { + $message["details"][$m]["sql"] = $sql; } - 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"; - if ($this->debug["sql"]) { - $message["details"][$m]["sql"] = $sql; - } - $this->message = $message; - $m++; - } - unset($parameters); - } //if permission - } //foreach rows - } //foreach $array - } + $this->message = $message; + $m++; + } + unset($parameters); + } //if permission + } //foreach rows + } //foreach $array //commit the atomic transaction $this->db->commit(); @@ -978,7 +1288,7 @@ include "root.php"; //log the transaction results if (file_exists($_SERVER["PROJECT_ROOT"]."/app/database_transactions/app_config.php")) { - $sql = "insert into v_database_transactions "; + $sql = "insert into ".self::TABLE_PREFIX."database_transactions "; $sql .= "("; $sql .= "database_transaction_uuid, "; if (isset($this->domain_uuid) && is_uuid($this->domain_uuid)) { @@ -1058,17 +1368,21 @@ include "root.php"; $statement->execute(); unset($sql); } - + return $retval; } //delete - public function count() { + /** + * Counts the number of rows. + * @return int Represents the number of counted rows or -1 if failed. + */ + public function count() : int { //connect to the database if needed if (!$this->db) { $this->connect(); } //sanitize the table name - $this->table = preg_replace('#[^a-zA-Z0-9_\-]#', '', $this->table); + //$this->table = self::sanitize($this->table); // no longer needed //get the number of rows $sql = "select count(*) as num_rows from ".$this->table." "; @@ -1077,7 +1391,7 @@ include "root.php"; if (is_array($this->where)) { foreach($this->where as $row) { //sanitize the name - $row['name'] = preg_replace('#[^a-zA-Z0-9_\-]#', '', $row['name']); + $row['name'] = self::sanitize($row['name']); //validate the operator switch ($row['operator']) { @@ -1086,21 +1400,18 @@ include "root.php"; case "<=": break; case ">=": break; case "=": break; - case ">=": break; case "<>": break; case "!=": break; default: //invalid operator - return false; + return -1; } //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']." "; } @@ -1112,7 +1423,7 @@ include "root.php"; } } } - unset($this->where); + //unset($this->where); //should not be objects resposibility $prep_statement = $this->db->prepare($sql); if ($prep_statement) { $prep_statement->execute($params); @@ -1128,6 +1439,13 @@ include "root.php"; } //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 @@ -1187,220 +1505,197 @@ include "root.php"; } } //select - public function find_new() { + /** + * 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): ?database { //connect to the database if needed - if (!$this->db) { - $this->connect(); - } + if (!$this->db) { + $this->connect(); + } //set the name - if (isset($array['name'])) { - $this->name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $array['name']); - } + if (isset($array['name'])) { + $this->name = $array['name']; + } //set the uuid - if (isset($array['uuid']) and $this->is_uuid($array['uuid'])) { - $this->uuid = $array['uuid']; - } + if (isset($array['uuid'])) { + $this->uuid = $array['uuid']; + } //build the query - $sql = "SELECT * FROM v_".$this->name." "; - if (isset($this->uuid)) { - //get the specific uuid - $sql .= "WHERE ".$this->singular($this->name)."_uuid = '".$this->uuid."' "; - } - else { - //where - $i = 0; - if (is_array($array)) { - foreach($array['where'] as $row) { - //sanitize the name - $array['name'] = preg_replace('#[^a-zA-Z0-9_\-]#', '', $array['name']); + $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; + } - //validate the operator - switch ($row['operator']) { - case "<": break; - 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['value']." "; - } - else { - //$sql .= "AND ".$row['name']." ".$row['operator']." '".$row['value']."' "; - $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++; + //build the sql + if ($i == 0) { + $sql .= "WHERE " . $row['name'] . " " . $row['operator'] . " :" . $row['value'] . " "; + } else { + $sql .= "AND " . $row['name'] . " " . $row['operator'] . " :" . $row['value'] . " "; } } - //order by - if (isset($array['order_by'])) { - $array['order_by'] = preg_replace('#[^a-zA-Z0-9_\-]#', '', $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']." "; - } + //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"; - if ($this->debug["sql"]) { - $message["details"][$m]["sql"] = $sql; - } - $this->message = $message; - $this->result = $prep_statement->fetchAll(PDO::FETCH_NAMED); - unset($prep_statement); - $m++; - return $this; + 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"; + if ($this->debug["sql"]) { + $message["details"][$m]["sql"] = $sql; } - 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"; - if ($this->debug["sql"]) { - $message["details"][$m]["sql"] = $sql; - } - $this->message = $message; - $this->result = ''; - $m++; - return $this; + $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"; + if ($this->debug["sql"]) { + $message["details"][$m]["sql"] = $sql; } + $this->message = $message; + $this->result = ''; + $m++; + } + return $this; } - private function normalize_array($array, $name) { - //get the depth of the array - $depth = $this->array_depth($array); - //before normalizing the array - //echo "before: ".$depth."
\n"; - //echo "
\n";
-					//print_r($array);
-					//echo "
\n"; - //normalize the array - if ($depth == 1) { - $return_array[$name][] = $array; - } else if ($depth == 2) { - $return_array[$name] = $array; - //} else if ($depth == 3) { - // $return_array[$name][] = $array; - } else { - $return_array = $array; - } - unset($array); - //after normalizing the array - $depth = $this->array_depth($new_array); - //echo "after: ".$depth."
\n"; - //echo "
\n";
-					//print_r($new_array);
-					//echo "
\n"; - //return the array - return $return_array; - } - - public function uuid($uuid) { + /** + * 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) : database { $this->uuid = $uuid; return $this; } - public function copy($array) { + /** + * 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)') : bool { + //set default return value + $retval = false; //return the array - if (!is_array($array)) { echo "not an array"; return false; } + if (!is_array($array)) { return $retval; } - //set the table prefix - $table_prefix = 'v_'; + //initialize array + $copy_array = []; //set the message id $m = 0; //loop through the array - if (is_array($array)) { - $x = 0; - foreach ($array as $parent_name => $tables) { - if (is_array($tables)) { - foreach ($tables as $id => $row) { + $x = 0; + foreach ($array as $parent_name => $tables) { + if (is_array($tables)) { + foreach ($tables as $id => $row) { - //prepare the variables - $parent_name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $parent_name); - $parent_key_name = $this->singular($parent_name)."_uuid"; + //prepare the variables + $parent_name = self::sanitize($parent_name); + $parent_key_name = self::singular($parent_name)."_uuid"; - //build the copy array - if ($row['checked'] == 'true') { - //set checked to true - $checked = true; + //build the copy array + if ($row['checked'] == 'true') { + //set checked to true + $checked = true; - //copy the child data - if (is_uuid($row[$parent_key_name])) { - $copy_array[$parent_name][$x][$parent_key_name] = $row[$parent_key_name]; - } + //copy the child data + if (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]); + //remove the row from the main array + unset($array[$parent_name][$x]); - //loop through the fields + //loop through the fields - foreach($row as $field_name => $field_value) { - //find the child tables - if (is_array($field_value)) { + foreach($row as $field_name => $field_value) { + //find the child tables + if (is_array($field_value)) { - //prepare the variables - $child_name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $field_name); - $child_key_name = $this->singular($child_name)."_uuid"; + //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) { + //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]; + //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]); + //remove the row from the main array + unset($array[$parent_name][$x][$child_name][$y]); - //increment the value - $y++; - } + //increment the value + $y++; } } } + } - //increment the value - $x++; + //increment the value + $x++; - } } } } //get the current data - if (is_array($copy_array) && count($copy_array) > 0) { + if (count($copy_array) > 0) { //build an array of tables, fields, and values foreach($copy_array as $table_name => $rows) { @@ -1417,15 +1712,15 @@ include "root.php"; //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 = preg_replace('#[^a-zA-Z0-9_\-]#', '', $table_name); - $sql = "select * from ".$table_prefix.$table_name." "; + $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 = preg_replace('#[^a-zA-Z0-9_\-]#', '', $field_name); + $field_name = self::sanitize($field_name); if ($i > 0) { $sql .= " ,"; } $sql .= " :".$field_name."_".$i." "; $i++; @@ -1449,7 +1744,7 @@ include "root.php"; //add child data to the old array foreach($copy_array as $parent_name => $rows) { //get relations array - $relations = $this->get_relations($parent_name); + $relations = self::get_relations($parent_name); //loop through the rows $x = 0; @@ -1460,12 +1755,12 @@ include "root.php"; $child_table = $relation['table']; //remove the v_ prefix - if (substr($child_table, 0, 2) == "v_") { - $child_table = substr($child_table, 2); + 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 ".$table_prefix.$child_table." "; + $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'); @@ -1482,70 +1777,74 @@ include "root.php"; //update the parent and child keys $checked = false; - if (is_array($array)) { - $x = 0; - foreach ($array as $parent_name => $tables) { - if (is_array($tables)) { - foreach ($tables as $id => $row) { + $x = 0; + foreach ($array as $parent_name => $tables) { + if (is_array($tables)) { + foreach ($tables as $id => $row) { - //prepare the variables - $parent_name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $parent_name); - $parent_key_name = $this->singular($parent_name)."_uuid"; - $parent_key_value = uuid(); + //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; + //update the parent key id + $array[$parent_name][$x][$parent_key_name] = $parent_key_value; - //add copy to the description - if (isset($array[$parent_name][$x][$this->singular($parent_name).'_description'])) { - $array[$parent_name][$x][$this->singular($parent_name).'_description'] = '(Copy) '.$array[$parent_name][$x][$this->singular($parent_name).'_description']; - } + //add copy to the description + if (isset($array[$parent_name][$x][self::singular($parent_name).'_description'])) { + $array[$parent_name][$x][self::singular($parent_name).'_description'] = $suffix.$array[$parent_name][$x][self::singular($parent_name).'_description']; + } - //loop through the fields - foreach($row as $field_name => $field_value) { + //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 = preg_replace('#[^a-zA-Z0-9_\-]#', '', $field_name); - $child_key_name = $this->singular($child_name)."_uuid"; + //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; + //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(); + //udpate the child key id + $array[$parent_name][$x][$child_name][$y][$child_key_name] = uuid(); - //increment the value - $y++; - } + //increment the value + $y++; } } + } - //increment the value - $x++; + //increment the value + $x++; - } } } } //save the copy of the data if (is_array($array) && count($array) > 0) { - $this->save($array); + $retval = $this->save($array); unset($array); } - + return $retval; } //end function copy - - public function toggle($array) { + /** + * 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) : bool { //return the array - if (!is_array($array)) { echo "not an array"; return false; } + if (!is_array($array)) { return false; } //set the message id $m = 0; @@ -1558,8 +1857,8 @@ include "root.php"; foreach ($tables as $id => $row) { //prepare the variables - $parent_name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $parent_name); - $parent_key_name = $this->singular($parent_name)."_uuid"; + $parent_name = self::sanitize($parent_name); + $parent_key_name = self::singular($parent_name)."_uuid"; //build the toggle array if ($row['checked'] == 'true') { @@ -1578,8 +1877,8 @@ include "root.php"; $y = 0; if (is_array($field_value)) { //prepare the variables - $child_name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $field_name); - $child_key_name = $this->singular($child_name)."_uuid"; + $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) { @@ -1612,7 +1911,7 @@ include "root.php"; //get the $apps array from the installed apps from the core and mod directories if (!is_array($_SESSION['apps'])) { - $this->get_apps(); + self::get_apps(); } //search through all fields to see if toggle field exists @@ -1626,7 +1925,7 @@ include "root.php"; else { $table_name = $row['table']['name']; } - if ($table_name === 'v_'.$parent_name) { + if ($table_name === self::TABLE_PREFIX.$parent_name) { if (is_array($row['fields'])) { foreach ($row['fields'] as $field) { if (isset($field['toggle'])) { @@ -1645,8 +1944,8 @@ include "root.php"; foreach ($toggle_array as $table_name => $table) { $x = 0; foreach($table as $row) { - $child_name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $table_name); - $child_key_name = $this->singular($child_name)."_uuid"; + $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]; @@ -1656,25 +1955,25 @@ include "root.php"; unset($toggle_array); //save the array - $this->save($array); - //view_array($this->message); + return $this->save($array); } //end function toggle - - public function save($array, $transaction_save = true) { - + /** + *

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 boolean Returns true on success and false on failure of one or more failed write attempts. + */ + public function save(array &$array, bool $transaction_save = true) : bool { + $retval = true; //return the array - if (!is_array($array)) { echo "not an array"; return false; } + if (!is_array($array)) { return false; } //set the message id $m = 0; - //set the app name - if (!isset($this->app_name)) { - $this->app_name = $this->name; - } - //debug sql $this->debug["sql"] = true; @@ -1683,30 +1982,19 @@ include "root.php"; $this->connect(); } - //normalize the array structure - //$new_array = $this->normalize_array($array, $this->name); - //unset($array); - $new_array = $array; - //start the atomic transaction $this->db->beginTransaction(); - //debug info - //echo "
\n";
-					//print_r($new_array);
-					//echo "
\n"; - //exit; - //loop through the array - if (is_array($new_array)) foreach ($new_array as $schema_name => $schema_array) { + if (is_array($array)) foreach ($array as $schema_name => $schema_array) { - $this->name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $schema_name); + $this->name = $schema_name; if (is_array($schema_array)) foreach ($schema_array as $schema_id => $array) { //set the variables - $table_name = "v_".$this->name; - $parent_key_name = $this->singular($this->name)."_uuid"; - $parent_key_name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $parent_key_name); + $table_name = self::TABLE_PREFIX.$this->name; + $parent_key_name = self::singular($this->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 @@ -1726,14 +2014,14 @@ include "root.php"; } //allow characters found in the uuid only. - $parent_key_value = preg_replace('#[^a-zA-Z0-9_\-]#', '', $parent_key_value); + $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[] = preg_replace('#[^a-zA-Z0-9_\-]#', '', $key); + $parent_field_names[] = self::sanitize($key); } } } @@ -1774,10 +2062,10 @@ include "root.php"; //add a record if ($action == "add") { - if (permission_exists($this->singular($this->name).'_add')) { + if (permission_exists(self::singular($this->name).'_add')) { $params = array(); - $sql = "INSERT INTO v_".$this->name." "; + $sql = "INSERT INTO ".self::TABLE_PREFIX.$this->name." "; $sql .= "("; if (!$parent_key_exists) { $sql .= $parent_key_name.", "; @@ -1785,7 +2073,7 @@ include "root.php"; if (is_array($array)) { foreach ($array as $array_key => $array_value) { if (!is_array($array_value)) { - $array_key = preg_replace('#[^a-zA-Z0-9_\-]#', '', $array_key); + $array_key = self::sanitize($array_key); $sql .= $array_key.", "; } } @@ -1855,6 +2143,7 @@ include "root.php"; $m++; } catch(PDOException $e) { + $retval = false; $message["message"] = "Bad Request"; $message["code"] = "400"; $message["details"][$m]["name"] = $this->name; @@ -1874,8 +2163,9 @@ include "root.php"; unset($sql); } else { + $retval = false; $message["name"] = $this->name; - $message["message"] = "Forbidden, does not have '".$this->singular($this->name)."_add'"; + $message["message"] = "Forbidden, does not have '".self::singular($this->name)."_add'"; $message["code"] = "403"; $message["line"] = __line__; $this->message[] = $message; @@ -1885,15 +2175,15 @@ include "root.php"; //edit a specific uuid if ($action == "update") { - if (permission_exists($this->singular($this->name).'_edit')) { + if (permission_exists(self::singular($this->name).'_edit')) { //parent data $params = array(); - $sql = "UPDATE v_".$this->name." SET "; + $sql = "UPDATE ".self::TABLE_PREFIX.$this->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 = preg_replace('#[^a-zA-Z0-9_\-]#', '', $array_key); + $array_key = self::sanitize($array_key); if (strlen($array_value) == 0) { $sql .= $array_key." = null, "; } @@ -1950,6 +2240,7 @@ include "root.php"; unset($sql); } catch(PDOException $e) { + $retval = false; $message["message"] = "Bad Request"; $message["code"] = "400"; $message["details"][$m]["name"] = $this->name; @@ -1967,8 +2258,8 @@ include "root.php"; } } else { - $message["name"] = $this->name; - $message["message"] = "Forbidden, does not have '".$this->singular($this->name)."_edit'"; + $retval = false; + $message["message"] = "Forbidden, does not have '".self::singular($this->name)."_edit'"; $message["code"] = "403"; $message["line"] = __line__; $this->message = $message; @@ -1984,12 +2275,12 @@ include "root.php"; foreach ($array as $key => $value) { if (is_array($value)) { - $table_name = "v_".$key; - $table_name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $table_name); + $table_name = self::TABLE_PREFIX.$key; + $table_name = self::sanitize($table_name); foreach ($value as $id => $row) { //prepare the variables - $child_name = $this->singular($key); - $child_name = preg_replace('#[^a-zA-Z0-9_\-]#', '', $child_name); + $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 @@ -2014,14 +2305,14 @@ include "root.php"; } //allow characters found in the uuid only. - $child_key_value = preg_replace('#[^a-zA-Z0-9_\-]#', '', $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[] = preg_replace('#[^a-zA-Z0-9_\-]#', '', $k); + $child_field_names[] = self::sanitize($k); } } } @@ -2062,7 +2353,7 @@ include "root.php"; if (is_array($row)) { foreach ($row as $k => $v) { if (!is_array($v) && ($k != $parent_key_name || $k != $child_key_name)) { - $k = preg_replace('#[^a-zA-Z0-9_\-]#', '', $k); + $k = self::sanitize($k); if (strlen($v) == 0) { $sql .= $k." = null, "; } @@ -2117,6 +2408,7 @@ include "root.php"; $m++; } catch(PDOException $e) { + $retval = false; if ($message["code"] = "200") { $message["message"] = "Bad Request"; $message["code"] = "400"; @@ -2136,6 +2428,7 @@ include "root.php"; } } else { + $retval = false; $message["name"] = $child_name; $message["message"] = "Forbidden, does not have '${child_name}_edit'"; $message["code"] = "403"; @@ -2170,15 +2463,15 @@ include "root.php"; $sql = "INSERT INTO ".$table_name." "; $sql .= "("; if (!$parent_key_exists) { - $sql .= $this->singular($parent_key_name).", "; + $sql .= self::singular($parent_key_name).", "; } if (!$child_key_exists) { - $sql .= $this->singular($child_key_name).", "; + $sql .= self::singular($child_key_name).", "; } if (is_array($row)) { foreach ($row as $k => $v) { if (!is_array($v)) { - $k = preg_replace('#[^a-zA-Z0-9_\-]#', '', $k); + $k = self::sanitize($k); $sql .= $k.", "; } } @@ -2210,7 +2503,7 @@ include "root.php"; $params[$k] = $_SERVER['REMOTE_ADDR']; } else { - $k = preg_replace('#[^a-zA-Z0-9_\-]#', '', $k); + $k = self::sanitize($k); $sql .= ':'.$k.", "; $params[$k] = trim($v); } @@ -2248,6 +2541,7 @@ include "root.php"; $m++; } catch(PDOException $e) { + $retval = false; if ($message["code"] = "200") { $message["message"] = "Bad Request"; $message["code"] = "400"; @@ -2267,6 +2561,7 @@ include "root.php"; } } else { + $retval = false; $message["name"] = $child_name; $message["message"] = "Forbidden, does not have '${child_name}_add'"; $message["code"] = "403"; @@ -2287,20 +2582,6 @@ include "root.php"; } // foreach schema_array } // foreach main array - //return the before and after data - //log this in the future - //if (is_array($old_array)) { - //normalize the array structure - //$old_array = $this->normalize_array($old_array, $this->name); - - //debug info - //echo "
\n";
-							//print_r($old_array);
-							//echo "
\n"; - //exit; - //} - //$message["new"] = $new_array; - //$message["new"]["md5"] = md5(json_encode($new_array)); $this->message = $message; //commit the atomic transaction @@ -2325,7 +2606,7 @@ include "root.php"; //log the transaction results if ($transaction_save && file_exists($_SERVER["PROJECT_ROOT"]."/app/database_transactions/app_config.php")) { try { - $sql = "insert into v_database_transactions "; + $sql = "insert into ".self::TABLE_PREFIX."database_transactions "; $sql .= "("; $sql .= "database_transaction_uuid, "; $sql .= "domain_uuid, "; @@ -2374,7 +2655,7 @@ include "root.php"; else { $sql .= "null, "; } - if (is_array($new_array)) { + if (is_array($array)) { $sql .= ":transaction_new, "; } else { @@ -2397,8 +2678,8 @@ include "root.php"; $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); + if (is_array($array)) { + $new_json = json_encode($array, JSON_PRETTY_PRINT); $statement->bindParam(':transaction_new', $new_json); } $message = json_encode($this->message, JSON_PRETTY_PRINT); @@ -2411,11 +2692,16 @@ include "root.php"; exit; } } - + return $retval; } //save method - //define singular function to convert a word in english to singular - public function singular($word) { + /** + * 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) : string { //"-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' @@ -2451,7 +2737,14 @@ include "root.php"; } } - public function get_apps() { + /** + * Gets the $apps array from the installed apps from the core and mod directories and writes it to $_SESSION['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; @@ -2464,25 +2757,36 @@ include "root.php"; $_SESSION['apps'] = $apps; } - public function array_depth($array) { + /** + * 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) : int { + $depth = 0; if (is_array($array)) { + $depth++; foreach ($array as $value) { - if (!isset($depth)) { $depth = 1; } if (is_array($value)) { - $depth = $this->array_depth($value) + 1; + $depth = self::array_depth($value) + 1; } } } - else { - $depth = 0; - } return $depth; } - public function domain_uuid_exists($name) { + /** + * Searches through all fields to see if domain_uuid exists + * @param string $name + * @uses $_SESSION['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 (!is_array($_SESSION['apps'])) { - $this->get_apps(); + self::get_apps(); } //search through all fields to see if domain_uuid exists @@ -2497,7 +2801,7 @@ include "root.php"; else { $table_name = $row['table']['name']; } - if ($table_name === 'v_'.$name) { + if ($table_name === self::TABLE_PREFIX.$name) { if (is_array($row['fields'])) { foreach ($row['fields'] as $field) { if ($field['name'] == "domain_uuid") { @@ -2515,18 +2819,24 @@ include "root.php"; return false; } - public function get_relations($schema) { + /** + * 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, 2) == "v_") { - $schema = substr($schema, 2); + if (substr($schema, 0, strlen(self::TABLE_PREFIX)) == self::TABLE_PREFIX) { + $schema = substr($schema, strlen(self::TABLE_PREFIX)); } //sanitize the values - $schema = preg_replace('#[^a-zA-Z0-9_\-]#', '', $schema); + $schema = self::sanitize($schema); //get the apps array - $config_list = glob($_SERVER["DOCUMENT_ROOT"] . PROJECT_PATH . "/{core,app}/{".$schema.",".$this->singular($schema)."}/app_config.php", GLOB_BRACE); + $config_list = glob($_SERVER["DOCUMENT_ROOT"] . PROJECT_PATH . "/{core,app}/{".$schema.",".self::singular($schema)."}/app_config.php", GLOB_BRACE); foreach ($config_list as &$config_path) { include($config_path); } @@ -2538,7 +2848,7 @@ include "root.php"; foreach ($row['fields'] as $z => $field) { if ($field['deprecated'] != "true") { if ($field['key']['type'] == "foreign") { - if ($row['table']['name'] == "v_".$schema || $field['key']['reference']['table'] == "v_".$schema) { + if ($row['table']['name'] == self::TABLE_PREFIX.$schema || $field['key']['reference']['table'] == self::TABLE_PREFIX.$schema) { //get the field name if (is_array($field['name'])) { $field_name = trim($field['name']['text']); @@ -2574,6 +2884,33 @@ include "root.php"; } } + /** + * 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) : string { + 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 reference to a new instance of database object + * @see database::__construct() + * @see database::connect() + */ + public static function &new() : database { + $db = new database(); + $db->connect(); + return $db; + } + } //class database } //!class_exists