BAOBAB
a_co_db.class.php
Go to the documentation of this file.
1 <?php
2 /***************************************************************************************************************************/
26 defined( 'LGV_ADB_CATCHER' ) or die ( 'Cannot Execute Directly' ); // Makes sure that this file is in the correct context.
27 
28 if ( !defined( 'LGV_DBF_CATCHER' ) ) {
29  define( 'LGV_DBF_CATCHER', 1 );
30 }
31 
32 if ( !defined('LGV_ERROR_CATCHER') ) {
33  define('LGV_ERROR_CATCHER', 1);
34 }
35 
36 require_once(CO_Config::db_class_dir().'/co_main_db_record.class.php');
37 
38 require_once(CO_Config::badger_shared_class_dir().'/error.class.php');
39 
40 /***************************************************************************************************************************/
50 abstract class A_CO_DB {
51  protected $_pdo_object;
55  var $error;
57 
58  /***********************************************************************************************************************/
59  /***********************/
67  private function _execute_query( $in_sql,
68  $in_parameters = NULL,
69  $exec_only = false
70  ) {
71  $ret = NULL;
72  $this->error = NULL;
73  try {
74  if ($exec_only) {
75  $ret = $this->_pdo_object->preparedExec($in_sql, $in_parameters);
76  } else {
77  $ret = $this->_pdo_object->preparedQuery($in_sql, $in_parameters, false);
78  }
79  } catch (Exception $exception) {
83  $exception->getFile(),
84  $exception->getLine(),
85  $exception->getMessage());
86  $this->_security_db_object = NULL;
87  }
88 
89 // Commented out, but useful for debug.
90 // echo('SQL:<pre>'.htmlspecialchars(print_r($in_sql, true)).'</pre>');
91 // echo('Params:<pre>'.htmlspecialchars(print_r($in_parameters, true)).'</pre>');
92 // echo('RESULT:<pre>'.htmlspecialchars(print_r($ret, true)).'</pre>');
93 // echo('ERROR:<pre>'.htmlspecialchars(print_r($this->error, true)).'</pre>');
94  return $ret;
95  }
96 
97  /***********************************************************************************************************************/
98  /***********************/
104  protected function _create_read_security_predicate() {
105  if ($this->access_object->god_mode()) { // God can do everything...
106  return '';
107  } else {
108  $access_ids = $this->access_object->get_security_ids();
109  $ret = '((read_security_id=0) OR (read_security_id IS NULL)';
110 
111  if (isset($access_ids) && is_array($access_ids) && count($access_ids)) {
112  foreach ($access_ids as $access_id) {
113  $ret .= ' OR (read_security_id='.intval($access_id).')';
114  $ret .= ' OR (write_security_id='.intval($access_id).')'; // Write security also means that we can read.
115  }
116  }
117 
118  $ret .= ')';
119 
120  // Only God can access God...
121  if (($this instanceof CO_Security_DB) && !$this->access_object->god_mode()) {
122  $ret .= ' AND (id<>'.intval(CO_Config::god_mode_id()).')';
123  }
124 
125  return $ret;
126  }
127  }
128 
129  /***********************/
135  protected function _create_write_security_predicate() {
136  if ($this->access_object->god_mode()) { // God can do everything...
137  return '';
138  } else {
139  $access_ids = $this->access_object->get_security_ids();
140  $ret = '0';
141 
142  if (isset($access_ids) && is_array($access_ids) && count($access_ids)) {
143  $ret = '((write_security_id=0) OR (write_security_id IS NULL)';
144 
145  foreach ($access_ids as $access_id) {
146  $ret .= ' OR (write_security_id='.intval($access_id).')';
147  }
148 
149  $ret .= ')';
150 
151  // Only God can access God...
152  if (($this instanceof CO_Security_DB) && !$this->access_object->god_mode()) {
153  $ret .= ' AND (id<>'.intval(CO_Config::god_mode_id()).')';
154  }
155  }
156 
157  return $ret;
158  }
159  }
160 
161  /***********************/
167  protected function _create_security_predicate( $write = false
168  ) {
169 
170  $ret = $write ? $this->_create_write_security_predicate() : $this->_create_read_security_predicate();
171 
172  return $ret;
173  }
174 
175  /***********************/
181  public function _instantiate_record( $in_db_result
182  ) {
183  $ret = NULL;
184 
185  $classname = trim($in_db_result['access_class']);
186  $id = intval($in_db_result['id']);
187 
188  if ($classname && $id) {
189  $filename = CO_Config::db_classes_class_dir().'/'.strtolower($classname).'.class.php';
190 
191  if (!class_exists($classname)) {
192  if (!file_exists($filename)) {
193  $filename = NULL;
194  $dir_array = CO_Config::db_classes_extension_class_dir();
195 
196  if (!is_array($dir_array)) {
197  $dir_array = Array($dir_array);
198  }
199 
200  foreach ($dir_array as $dir) {
201  $filename = $dir.'/'.strtolower($classname).'.class.php';
202  if (file_exists($filename)) {
203  require_once($filename);
204  break;
205  } else {
206  $filename = NULL;
207  }
208  }
209  } else {
210  require_once($filename);
211  }
212  }
213 
214  if (!$filename) {
218  $classname,
219  __LINE__,
220  __METHOD__
221  );
222  return NULL;
223  }
224 
225  if (class_exists($classname)) { // We make sure that we send references to the same object, if we instantiate it multiple times.
226  if (isset($this->_existing_record_objects[$id])) {
227  $ret = $this->_existing_record_objects[$id];
228  if (isset($ret)) { // We make sure the object jives with what was given us from the DB.
229  // This forces the collection-based classes to reload their children.
230  if (method_exists($ret, 'reload_collection')) {
231  $ret->reload_collection();
232  }
233  $ret->load_from_db($in_db_result);
234  }
235  } else {
236  $ret = new $classname($this, $in_db_result);
237  }
238 
239  if (!$ret) {
243  $classname,
244  __LINE__,
245  __METHOD__
246  );
247  return NULL;
248  }
249 
250  // Make sure we cache this for later.
251  $this->_existing_record_objects[$id] = $ret;
252  } else {
256  $classname,
257  __LINE__,
258  __METHOD__
259  );
260  }
261  }
262 
263  return $ret;
264  }
265 
266  /***********************************************************************************************************************/
267  /***********************/
271  public function __construct( $in_pdo_object,
272  $in_access_object
273  ) {
274  $this->class_description = 'Abstract Base Class for Database -Should never be instantiated.';
275 
276  $this->access_object = $in_access_object;
277  $this->error = NULL;
278  $this->table_name = NULL;
279  $this->_pdo_object = $in_pdo_object;
280  if (isset($this->_pdo_object)) {
281  $this->_pdo_object->owner_instance = $this;
282  }
283  $this->_existing_record_objects = Array();
284  }
285 
286  /***********************/
292  public function execute_query( $in_sql,
293  $in_parameters = NULL,
294  $exec_only = false
295  ) {
296  return $this->_execute_query($in_sql, $in_parameters, $exec_only);
297  }
298 
299  /***********************/
307  public function get_db_backup() {
308  $ret = NULL;
309 
310  if ($this->access_object->god_mode()) {
311  $sql = 'SELECT * FROM '.$this->table_name.' WHERE id>1 ORDER BY id';
312 
313  $ret = $this->execute_query($sql, Array());
314  }
315 
316  return $ret;
317  }
318 
319  /***********************/
327  public function get_access_class_by_id( $in_id
328  ) {
329  $ret = NULL;
330 
331  $predicate = $this->_create_security_predicate();
332 
333  if ($predicate) {
334  $predicate .= ' AND ';
335  } else {
336  $predicate = 'true AND ';
337  }
338 
339  $sql = 'SELECT access_class FROM '.$this->table_name.' WHERE '.$predicate.'id='.intval($in_id);
340 
341  $ret = $this->execute_query($sql, Array());
342 
343  if (isset($ret) && is_array($ret) && count($ret)) {
344  $ret = strval($ret[0]['access_class']);
345  }
346 
347  return $ret;
348  }
349 
350  /***********************/
358  public function can_i_see_this_record( $in_id
359  ) {
360  $ret = false;
361 
362  if (intval($in_id)) {
363  $predicate = $this->_create_security_predicate();
364 
365  if ($predicate) {
366  $predicate .= ' AND ';
367  } else {
368  $predicate = 'true AND ';
369  }
370 
371  $sql = 'SELECT id FROM '.$this->table_name.' WHERE '.$predicate.'id='.intval($in_id);
372 
373  $ret = $this->execute_query($sql, Array());
374 
375  if (isset($ret) && is_array($ret) && count($ret)) {
376  $ret = intval($ret[0]['id']) == intval($in_id);
377  }
378  }
379 
380  return $ret;
381  }
382 
383  /***********************/
391  public function get_single_raw_row_by_id( $in_id,
392  $and_write = false
393  ) {
394  $ret = NULL;
395 
396  $predicate = $this->_create_security_predicate($and_write);
397 
398  if ($predicate) {
399  $predicate .= ' AND ';
400  } else {
401  $predicate = 'true AND ';
402  }
403 
404  $sql = 'SELECT * FROM '.$this->table_name.' WHERE '.$predicate.'id='.intval($in_id);
405 
406  $ret = $this->execute_query($sql, Array());
407 
408  if (isset($ret) && is_array($ret) && count($ret)) {
409  $ret = $ret[0];
410  }
411  return $ret;
412  }
413 
414  /***********************/
422  public function get_single_record_by_id( $in_id,
423  $and_write = false
424  ) {
425  $ret = NULL;
426 
427  $temp = $this->get_multiple_records_by_id(Array($in_id), $and_write);
428 
429  if (isset($temp) && $temp && is_array($temp) && count($temp) ) {
430  $ret = $temp[0];
431  }
432 
433  return $ret;
434  }
435 
436  /***********************/
444  public function get_multiple_records_by_id( $in_id_array,
445  $and_write = false
446  ) {
447  $ret = NULL;
448 
449  $predicate = $this->_create_security_predicate($and_write);
450 
451  if ($predicate) { // If we got a predicate, then we AND it with the rest of the statement.
452  $predicate .= ' AND ';
453  }
454 
455  $sql = 'SELECT * FROM '.$this->table_name.' WHERE '.$predicate.'(';
456  $params = Array();
457  // Clean the array to make sure they are all integers.
458  $id_array = array_map('intval', $in_id_array);
459  foreach ($id_array as $id) {
460  if (0 < $id) {
461  if (0 < count($params)) {
462  $sql .= ' OR ';
463  }
464  $sql.= '(id=?)';
465  array_push($params, $id);
466  }
467  }
468 
469  $sql .= ')';
470 
471  $temp = $this->execute_query($sql, $params);
472 // Commented out, but useful for debug.
473 // echo('SQL:<pre>'.htmlspecialchars(print_r($sql, true)).'</pre>');
474 // echo('PARAMS:<pre>'.htmlspecialchars(print_r($params, true)).'</pre>');
475 // echo('RESPONSE:<pre>'.htmlspecialchars(print_r($temp, true)).'</pre>');
476  if (isset($temp) && $temp && is_array($temp) && count($temp) ) {
477  $ret = Array();
478  foreach ($temp as $result) {
479  $result = $this->_instantiate_record($result);
480  if (isset($result) && ($result instanceof A_CO_DB_Table_Base)) {
481  $weregood = $and_write ? $result->user_can_write() : $result->user_can_read();
482  // Belt and suspenders. Make sure nothing leaks through.
483  if ($weregood) {
484  $ret[] = $result;
485  }
486  }
487  }
488  usort($ret, function($a, $b){return ($a->id() > $b->id());});
489  }
490 
491  return $ret;
492  }
493 
494  /***********************/
502  public function get_all_readable_records( $open_only = false,
503  $in_this_id = NULL
504  ) {
505  $ret = NULL;
506 
507  $predicate = $open_only ? '((read_security_id=0) OR (read_security_id IS NULL))' : $this->_create_security_predicate();
508 
509  // No need for an AND, as the predicate is the only qualifier.
510  if (!$predicate) {
511  $in_this_id = intval($in_this_id);
512  if ($in_this_id) { // We can look for certain IDs in God Mode.
513  $predicate = "(read_security_id=$in_this_id)";
514 
515  } else {
516  $predicate = 'true'; // If we are in "God Mode," we could get no predicate, so we just go with "1".
517  }
518  }
519 
520  $sql = 'SELECT * FROM '.$this->table_name.' WHERE '.$predicate;
521  $temp = $this->execute_query($sql, Array());
522  if (isset($temp) && $temp && is_array($temp) && count($temp) ) {
523  $ret = Array();
524  foreach ($temp as $result) {
525  if (isset($result) && is_array($result) && count($result)) {
526  $result = $this->_instantiate_record($result);
527  // Belt and suspenders. Make sure nothing leaks through.
528  if ($result && $result->user_can_read()) {
529  array_push($ret, $result);
530  }
531  }
532  }
533  usort($ret, function($a, $b){return ($a->id() > $b->id());});
534  }
535 
536  return $ret;
537  }
538 
539  /***********************/
547  public function get_all_writeable_records( $in_this_id = NULL
548  ) {
549  $ret = NULL;
550 
551  $access_ids = $this->access_object->get_security_ids();
552 
553  // Only logged-in users can write.
554  if (isset($access_ids) && is_array($access_ids) && count($access_ids)) {
555  $predicate = $this->_create_security_predicate(true);
556 
557  // No need for an AND, as the predicate is the only qualifier.
558  if (!$predicate) {
559  $in_this_id = intval($in_this_id);
560  if ($in_this_id) { // We can look for certain IDs in God Mode.
561  $predicate = "(write_security_id=$in_this_id)";
562 
563  } else {
564  $predicate = 'true'; // If we are in "God Mode," we could get no predicate, so we just go with "1".
565  }
566  }
567 
568  $sql = 'SELECT * FROM '.$this->table_name.' WHERE '.$predicate;
569  $temp = $this->execute_query($sql, Array());
570  if (isset($temp) && $temp && is_array($temp) && count($temp) ) {
571  $ret = Array();
572  foreach ($temp as $result) {
573  $result = $this->_instantiate_record($result);
574  // Belt and suspenders. Make sure nothing leaks through.
575  if ($result && $result->user_can_write()) {
576  array_push($ret, $result);
577  }
578  }
579  usort($ret, function($a, $b){return ($a->id() > $b->id());});
580  }
581  }
582 
583  return $ret;
584  }
585 
586  /***********************/
592  public function lock_record( $in_record_id
593  ) {
594  $ret = 0;
595 
596  $predicate = $this->_create_security_predicate(true);
597 
598  if ($predicate) {
599  $predicate .= ' AND ';
600  }
601 
602  $in_record_id = intval($in_record_id); // Make sure we are an integer.
603 
604  if (0 < $in_record_id) {
605  // First, we look for a record with our ID, and for which we have write permission, and we get the original Read ID.
606  $sql = 'SELECT read_security_id FROM '.$this->table_name.' WHERE '.$predicate.'id='.$in_record_id;
607 
608  $temp = $this->execute_query($sql, Array());
609 
610  if (isset($temp) && $temp && is_array($temp) && (1 == count($temp)) ) { // If we got a record, then we'll be updating it.
611  $ret = intval($temp[0]); // Get the original value.
612 
613  $sql = 'UPDATE '.$this->table_name.' SET read_security_id=-2 WHERE id=?';
614 
615  $temp = $this->execute_query($sql, Array($in_record_id));
616 
617  if ($this->error) { // If we had an error, we return squat.
618  $ret = 0;
619  }
620  }
621  }
622 
623  return $ret;
624  }
625 
626  /***********************/
636  public function write_record( $params_associative_array
637  ) {
638  $ret = false;
639  if (isset($params_associative_array) && is_array($params_associative_array) && count($params_associative_array)) {
640  $access_ids = $this->access_object->get_security_ids();
641  $params_associative_array['last_access'] = date('Y-m-d H:i:s');
642  if (isset($access_ids) && is_array($access_ids) && count($access_ids)) {
643  $predicate = $this->_create_security_predicate(true);
644 
645  if ($predicate) {
646  $predicate .= ' AND ';
647  }
648 
649  $id = isset($params_associative_array['id']) ? intval($params_associative_array['id']) : 0; // We extract the ID from the fields, or assume a new record.
650 
651  if (0 < $id) {
652  // First, we look for a record with our ID, and for which we have write permission.
653  $sql = 'SELECT * FROM '.$this->table_name.' WHERE '.$predicate.'id='.$id;
654 
655  $temp = $this->execute_query($sql, Array());
656 
657  if (isset($temp) && $temp && is_array($temp) && (1 == count($temp)) ) { // If we got a record, then we'll be updating it.
658  $sql = 'UPDATE '.$this->table_name.'';
659  if (!CO_Config::use_personal_tokens()) {
660  unset($params_associative_array['personal_ids']); // We do not change the personal ID column, if we don't have it enabled.
661  }
662 
663  unset($params_associative_array['id']); // We remove the ID parameter. That can't be changed.
664 
665  $params = array_values($params_associative_array);
666  $keys = array_keys($params_associative_array);
667  $set_sql = ''.implode('=?,', $keys).'=?';
668 
669  $sql .= ' SET '.$set_sql.' WHERE ('.$predicate.'id='.$id.')';
670  $this->execute_query($sql, $params, true);
671  if (!$this->error) { // Make sure that we update the access time on cached objects.
672  if (isset($this->_existing_record_objects[$id])) {
673  $this->_existing_record_objects[$id]->last_access = strtotime($params_associative_array['last_access']);
674  }
675  $ret = true;
676  }
677  } else {
678  $sql = 'SELECT * FROM '.$this->table_name.' WHERE id='.$id; // Look for a record with the proposed ID (assume we don't have permission).
679 
680  $temp = $this->execute_query($sql, Array());
681 
682  if (isset($temp) && $temp && is_array($temp) && count($temp)) { // If we got a record, then we're aborting, as we tried to change an existing rcord.
686  }
687  }
688  } else {
689  $sql = 'SELECT * FROM '.$this->table_name.' WHERE id=1'; // We simply get the template.
690 
691  $temp = $this->execute_query($sql, Array());
692 
693  if (isset($temp) && $temp && is_array($temp) && count($temp) ) {
694  $sql = 'INSERT INTO '.$this->table_name.'';
695  if (!CO_Config::use_personal_tokens()) {
696  unset($params_associative_array['personal_ids']); // We do not change the personal ID column, if we don't have it enabled.
697  }
698  unset($params_associative_array['id']);
699 
700  // If there is no read ID specified, it becomes public.
701  if (!isset($params_associative_array['read_security_id'])) {
702  $params_associative_array['read_security_id'] = 0;
703  }
704 
705  // If there is no write ID specified, then we simply take the first one off the list.
706  if (!isset($params_associative_array['write_security_id']) && isset($access_ids[1])) {
707  $params_associative_array['write_security_id'] = $access_ids[1];
708  }
709 
710  foreach ($params_associative_array as $key => $value) {
711  if (isset($temp[$key])) {
712  $temp[$key] = isset($value) ? $value : NULL;
713  }
714  }
715 
716  $keys = array_keys($params_associative_array);
717 
718  $keys_sql = '('.implode(',', $keys).')';
719  $values_sql = '('.implode(',', array_map(function($in){return '?';}, $keys)).') RETURNING id;';
720 
721  $sql .= " $keys_sql VALUES $values_sql";
722  $this->execute_query($sql, array_values($params_associative_array), true);
723 
724  if (!$this->error) {
725  // Get the ID of the new row we just created.
726  $ret = $this->_pdo_object->last_insert;
727  }
728  } else {
732  }
733  }
734  }
735  }
736 
737  return $ret;
738  }
739 
740  /***********************/
750  public function delete_record( $id
751  ) {
752  $ret = false;
753 
754  $id = intval($id);
755  $predicate = $this->_create_security_predicate(true);
756 
757  if ($predicate) {
758  $predicate .= ' AND ';
759  }
760 
761  // First, make sure we have write permission for this record, and that the record exists.
762  $sql = 'SELECT id FROM '.$this->table_name.' WHERE ('.$predicate.'id='.$id.')';
763  $temp = $this->execute_query($sql);
764 
765  if (!$this->error && isset($temp) && $temp && is_array($temp) && (1 == count($temp))) {
766  $sql = 'DELETE FROM '.$this->table_name.' WHERE ('.$predicate.'id='.$id.')';
767  $temp = $this->execute_query($sql, Array(), true); // We call this as an "execute-only" query.
768  if (!$temp || $this->error) {
772  } else {
773  // Make sure she's dead, Jim. We do an open-ended check.
774  $sql = 'SELECT id FROM '.$this->table_name.' WHERE id='.$id;
775  $temp = $this->execute_query($sql);
776  if (!$this->error && isset($temp) && is_array($temp) && (0 == count($temp))) {
777  // Make sure that we also remove it from our cache.
778  if (isset($this->_existing_record_objects[$id])) {
779  $this->_existing_record_objects[$id]->danger_will_robinson_danger_clear_id();
780  unset($this->_existing_record_objects[$id]);
781  }
782 
783  $ret = true;
784  } else {
788  }
789  }
790  } else {
794  }
795 
796  return $ret;
797  }
798 };
if(!defined( 'LGV_DBF_CATCHER')) if(!defined( 'LGV_ERROR_CATCHER'))
_create_read_security_predicate()
execute_query( $in_sql, $in_parameters=NULL, $exec_only=false)
get_all_writeable_records( $in_this_id=NULL)
_instantiate_record( $in_db_result)
get_single_record_by_id( $in_id, $and_write=false)
get_all_readable_records( $open_only=false, $in_this_id=NULL)
get_access_class_by_id( $in_id)
delete_record( $id)
$_existing_record_objects
get_multiple_records_by_id( $in_id_array, $and_write=false)
__construct( $in_pdo_object, $in_access_object)
write_record( $params_associative_array)
_create_security_predicate( $write=false)
_create_write_security_predicate()
_execute_query( $in_sql, $in_parameters=NULL, $exec_only=false)
get_single_raw_row_by_id( $in_id, $and_write=false)
can_i_see_this_record( $in_id)
lock_record( $in_record_id)
static $db_error_code_class_file_not_found
Definition: common.inc.php:49
static $db_error_code_class_not_created
Definition: common.inc.php:50
static $pdo_error_code_failed_delete_attempt
Definition: common.inc.php:47
static $pdo_error_code_illegal_delete_attempt
Definition: common.inc.php:46
static $pdo_error_code_failed_to_open_security_db
Definition: common.inc.php:43
static $pdo_error_code_illegal_write_attempt
Definition: common.inc.php:45
static $pdo_error_name_failed_to_open_security_db
Definition: en.php:35
static $pdo_error_desc_illegal_write_attempt
Definition: en.php:42
static $db_error_name_class_file_not_found
Definition: en.php:50
static $pdo_error_desc_failed_to_open_security_db
Definition: en.php:36
static $pdo_error_desc_illegal_delete_attempt
Definition: en.php:45
static $db_error_desc_class_not_created
Definition: en.php:53
static $pdo_error_name_illegal_delete_attempt
Definition: en.php:44
static $db_error_desc_class_file_not_found
Definition: en.php:51
static $pdo_error_desc_failed_delete_attempt
Definition: en.php:48
static $db_error_name_class_not_created
Definition: en.php:52
static $pdo_error_name_failed_delete_attempt
Definition: en.php:47
static $pdo_error_name_illegal_write_attempt
Definition: en.php:41
This class provides a general error report, with file, method and error information.
Definition: error.class.php:32