*/ // dblayer.php /* Documentation : The dbLayer provide an abstraction layer on PDO to be easier on all the CRUD (Create, Read, Update, Delete) operations, accross all the databases engines. To use it, extends in your code this class, and define the attributes : - protected $table : name of the table you want to use - protected $fields : description of all the fields in the database like : protected $fields = array ( "id"=>array ("integer", "not null", "autoincrement"), "zone"=>array ("varchar", "255", "not null"), "viewname"=>array ("varchar", "255"), "viewclients"=>array ("varchar", "255"), "comment"=>array ("varchar", "1024"), "opendate"=>array ("datetime", "not null"), "closedate"=>array ("datetime"), ); - protected $primary = "id" ; the primary key of the table (in text). Actually the dbLayer abstraction don't supports primary key on multiples columns - protected $unique = array ("column", array ("column1", "column2");) Optionnaly, you can add the - protected $debug = TRUE : enable the debug on screen (NOT FOR PROD !!) */ require_once ("domframework/verify.php"); /** Permit abstraction on the differents SQL databases available */ class dblayer extends PDO { /** The fields with the definition of type, and special parameters */ protected $fields = array (); /** The primary field */ protected $primary = null; /** An array to define the unique fields (or array of unique fields) */ protected $unique = null; /** The db connection */ protected $db = null; /** The verify stack */ protected function verifyOne ($field, $val) {} protected function verifyAll ($datas) {} /** Debug of the SQL */ public $debug = FALSE; /** Return all the tables available in the database */ function listTables () { $driver = $this->getAttribute (PDO::ATTR_DRIVER_NAME); $rc = @include_once ("dbLayer".ucfirst ($driver).".php"); if ($rc === FALSE) throw new Exception (sprintf (_("dbLayer driver %s not available"), $driver), 500); } // TODO !! /** Create Table creation from $this->fields with engine abstraction Example in sqlite3 id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, in MySQL id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, */ // TODO !! /** Create automatic creation of $fields from .schema of sqlite3/ show create table `NomTable`; for MySQL SQLite3 : PRAGMA TABLE_INFO('yourtable'); MYSQL : SHOW COLUMNS FROM yourtable;*/ /** Connection to the database engine See http://fr2.php.net/manual/en/pdo.construct.php for the $dsn format @param string $dsn PDO Data Source Name @param string|null $username Username to connect @param string|null $password Password to connect @param string|null $driver_options Driver options to the database */ function __construct ($dsn, $username=null, $password=null, $driver_options=null) { try { $this->db = new PDO ($dsn, $username, $password, $driver_options); $this->db->setAttribute (PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (Exception $e) { throw new Exception ("PDO error : ".$e->getMessage()); } } /** Create a new entry in the table. Datas must be an indexed array @param array $datas Datas to be recorded (column=>value)*/ function create ($datas) { if ($this->db === null) throw new Exception (_("Database not connected"), 500); if ($this->unique === null) throw new Exception (_("Unique fields of table are not defined"), 500); if (!is_array ($datas)) throw new Exception (_("The datas provided to create are not array"), 405); if (!in_array ($this->primary, $this->unique)) $this->unique[] = $this->primary; $datasOK = array (); // Check for missing parameters foreach ($this->fields as $key=>$params) { if (in_array ("autoincrement", $params)) $datas[$key] = null; if (in_array ("not null", $params) && !array_key_exists ($key, $datas)) throw new Exception ("Mandatory field '$key' not provided", 405); if (!array_key_exists ($key, $datas)) continue; // Verify the fields, if $verify is defined, before doing insertion $verify = $this->verifyOne ($key, $datas[$key]); if (is_array ($verify) && count ($verify)) throw new Exception ($verify[0]." ".$verify[1]." in ".$key); // TODO : Check for type inconsistancies before create $datasOK if (array_key_exists ($key, $datas)) $datasOK[$key] = $datas[$key]; } // Check for inconsistancy $verify = $this->verifyAll ($datas); if (count ($verify)) throw new Exception ("Errors in consistancy : ".print_r ($verify, TRUE), 405); // Check if the unique constrain is valid before doing the insertion foreach ($this->unique as $columns) { if (is_array ($columns)) { $select = array (); foreach ($columns as $col) { if (!array_key_exists ($col, $datasOK)) continue; $select[] = array ($col, $datasOK[$col]); } $rc = $this->read ($select, array ($this->primary)); if (count ($rc) > 0) throw new Exception (sprintf ( _("The provided values for columns '%s' already exists"), implode (",", $columns)), 405); } else { if (!array_key_exists ($columns, $datasOK)) continue; $rc = $this->read (array (array ($columns, $datasOK[$columns])), array ($this->primary)); if (count ($rc) > 0) throw new Exception (sprintf ( _("The column '%s' with this value already exists"), $columns), 405); } } // TODO : Check if the foreign keys constrains are valid before doing the // insertion $req = "INSERT INTO `".$this->table."` "; $req .= "(".implode (",", array_keys ($datasOK)).")"; $req .= " VALUES "; $req .= "(:".implode (",:", array_keys ($datasOK)).")"; if ($this->debug) echo "DEBUG : $req\n"; $st = $this->db->prepare ($req); foreach ($datasOK as $key=>$val) { if ($this->debug) echo "DEBUG BIND : $key->".var_export ($val, TRUE)."\n"; if ($val === null) $st->bindValue (":$key", $val, PDO::PARAM_NULL); elseif ($this->fields[$key][0] === "integer") $st->bindValue (":$key", $val, PDO::PARAM_INT); elseif ($this->fields[$key][0] === "varchar") $st->bindValue (":$key", $val, PDO::PARAM_STR); elseif ($this->fields[$key][0] === "datetime") $st->bindValue (":$key", $val, PDO::PARAM_STR); else throw new Exception ("TO BE DEVELOPPED : ".$this->fields[$key][0], 500); } $st->execute (); return $this->db->lastInsertId(); } /** Read the table content based on a select filter, ordered by order operator and the associated select value @param array|null $select Rows to select with $select = array (array ($key, $val, $operator), ...) $key=>column, $val=>value to found, $operator=>'LIKE', =... @param array|null $display Columns displayed $display = array ($col1, $col2...); @param array|null $order Sort the columns by orientation $order = array (array ($key, $orientation), ...) $key=>column, $orientation=ASC/DESC */ function read ($select=null, $display=null, $order=null) { if ($this->db === null) throw new Exception (_("Database not connected"), 500); if ($select !== null && !is_array ($select)) throw new Exception (_("Select information provided is not an array"), 405); if ($display !== null && !is_array ($display)) throw new Exception (_("Display information provided is not an array"), 405); if ($order !== null && !is_array ($order)) throw new Exception (_("Order information provided is not an array"), 405); if ($display !== null) { foreach ($display as $f) { if (!in_array ($f, array_keys ($this->fields))) throw new Exception (sprintf (_("Field %s not allowed"), $f), 506); } } else { $display = array_keys ($this->fields); } $req = "SELECT "; $req .= implode (",", $display); $req .= " FROM `".$this->table."`"; if ($select !== null) { $req .= " WHERE "; foreach ($select as $n=>$s) { if ($n > 0) $req .= " AND"; if (!isset ($s[2])) $s[2] = "="; $req .= " ".$s[0]." ".$s[2]." :".$s[0]; } } if ($order !== null) { $req .= " ORDER BY "; foreach ($order as $n=>$o) { if ($n > 0) $req .= ","; $req .= $o[0]; if (isset ($o[1]) && $o[1] === "DESC") $req .= " DESC"; else $req .= " ASC"; } } if ($this->debug) echo "DEBUG : $req\n"; $st = $this->db->prepare ($req); if ($select !== NULL) { foreach ($select as $s) { if ($this->debug) echo "DEBUG BIND : ".$s[0]."->". var_export ($s[1], TRUE)."\n"; $st->bindValue (":".$s[0], $s[1]); } } $st->execute (); $res = array (); while ($d = $st->fetch (PDO::FETCH_ASSOC)) $res[] = $d; return $res; } /** Update the key tuple with the provided datas Return the number of rows modified @param string|integer $updatekey The key applied on primary key to be updated @param array $datas The values to be updated */ function update ($updatekey, $datas) { if ($this->db === null) throw new Exception (_("Database not connected"), 500); $datasOK = array (); // Check for missing parameters foreach ($this->fields as $key=>$params) { // Verify the fields, if $verify is defined, before doing insertion if (array_key_exists ($key, $datas)) $datasOK[$key] = $datas[$key]; } if (count ($datasOK) === 0) throw new Exception (_("Don't receive any field to display"), 501); // Check for type inconsistancies before using $datasOK foreach ($datasOK as $key=>$params) { $verify = $this->verifyOne ($key, $datas[$key]); if (is_array ($verify) && count ($verify)) throw new Exception ($verify[0]." ".$verify[1]." in ".$key); } // Check if the unique constrain is valid before doing the insertion // 1. Read the actual state $before = $this->read (array (array ($this->primary, $updatekey))); if (count ($before) === 0) throw new Exception (_("Entry to modify unavailable"), 404); $before = reset ($before); // 2. Map the proposal entries into the before state $after = $before; foreach ($datasOK as $field=>$val) $after[$field] = $val; // 3. Check for constrain violation on unique columns foreach ($this->unique as $columns) { if (is_array ($columns)) { $select = array (); $select[] = array ($this->primary, $updatekey, "!="); foreach ($columns as $col) { if (!array_key_exists ($col, $after)) continue; $select[] = array ($col, $after[$col]); } // If there is only the primary key, there is no chance to have a // conflict if (count ($select) >= 2) { $rc = $this->read ($select, array ($this->primary)); if (count ($rc) > 0) throw new Exception (sprintf ( _("The provided values for columns '%s' already exists"), implode (",", $columns)), 405); } } else { if (array_key_exists ($columns, $datasOK)) continue; $select = array (); if ($columns !== $this->primary) $select[] = array ($this->primary, $updatekey, "!="); $select[] = array ($columns, $datasOK[$columns]); $rc = $this->read ($select, array ($this->primary)); if (count ($rc) > 0) throw new Exception (sprintf ( _("An entry already exists with this value in the column '%s'"), $columns), 405); } } // TODO : Check if the foreign keys constrains are valid before doing the $datasOK[$this->primary] = $updatekey; $req = "UPDATE `".$this->table."` SET "; $i = 0; foreach ($datasOK as $key=>$val) { if ($i>0) $req .= ","; $req .= "$key=:$key"; $i++; } $req .= " WHERE $this->primary=:$this->primary"; if ($this->debug) echo "DEBUG : $req\n"; $st = $this->db->prepare ($req); foreach ($datasOK as $key=>$val) { if ($this->debug) echo "DEBUG BIND : $key->".var_export ($val, TRUE)." "; if ($val === null) { if ($this->debug) echo "(null)\n"; $st->bindValue (":$key", $val, PDO::PARAM_NULL); } elseif ($this->fields[$key][0] === "integer") { if ($this->debug) echo "(integer)\n"; $st->bindValue (":$key", $val, PDO::PARAM_INT); } elseif ($this->fields[$key][0] === "varchar") { if ($this->debug) echo "(varchar)\n"; $st->bindValue (":$key", $val, PDO::PARAM_STR); } elseif ($this->fields[$key][0] === "datetime") { if ($this->debug) echo "(datetime)\n"; $st->bindValue (":$key", $val, PDO::PARAM_STR); } else { if ($this->debug) echo "(UNKNOWN)\n"; throw new Exception ("TO BE DEVELOPPED : ".$this->fields[$key][0], 500); } } $st->execute (); return $st->rowCount (); } /** Delete a tuple identified by its primary key Return the number of deleted rows (can be 0 !) @param strin|integer $deletekey The key of primary key to be deleted */ function delete ($deletekey) { if ($this->db === null) throw new Exception ("Database not connected"); $req = "DELETE FROM `".$this->table."` "; $req .= "WHERE $this->primary = :primary"; $st = $this->db->prepare ($req); if ($this->debug) echo "DEBUG : $req\n"; if ($this->debug) echo "DEBUG BIND : primary->". var_export ($deletekey, TRUE)."\n"; $st->bindValue (":primary", $deletekey); $st->execute (); return $st->rowCount(); } /** Translation of fields */ public function titles () { $arr = array (); foreach ($this->fields as $field=>$v) $arr[$field] = $field; return $arr; } } /** POC : error_reporting (E_ALL); require_once ("domframework/dbLayer.php"); class zone extends dbLayer { // The database must be initialized with // CREATE TABLE `dns_zones` (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, // zone VARCHAR(255) NOT NULL, // viewname VARCHAR(255), // viewclients VARCHAR(255), // comment VARCHAR(1024), // opendate DATETIME NOT NULL, // closedate DATETIME, // UNIQUE (zone,viewname)); protected $table = "dns_zones"; protected $fields = array ( "id"=>array ("integer", "not null", "autoincrement"), "zone"=>array ("varchar", "255", "not null"), "viewname"=>array ("varchar", "255"), "viewclients"=>array ("varchar", "255"), "comment"=>array ("varchar", "1024"), "opendate"=>array ("datetime", "not null"), "closedate"=>array ("datetime"), ); protected $primary = "id"; protected $unique = array ("id", array ("zone", "viewname")); } ini_set ("date.timezone", "Europe/Paris"); $zone = new zone ("sqlite:datas/database.db"); $last = $zone->create (array ("zone"=>"testZone", "opendate"=>date("Y-m-d H:i:s"))); //print_r ($zone->read ()); $zone->update (2040, array ("zone"=>"testZone2")); print_r ($zone->delete ($last)); */