00001 <?php
00002
00009 abstract class TSDObject extends SObject {
00010 protected $table;
00011 protected $attributes;
00012 protected $access;
00013 protected $unique;
00014 protected $DBI;
00015
00016 protected $inDB;
00017
00029 public function __construct($table, $attrs, $unique, $id) {
00030 parent::__construct();
00031
00032 $this->table = $table;
00033 $this->attributes = array();
00034 foreach($attrs as $a => $prot) {
00035 $this->setAttr($a, null);
00036 $this->access[$a] = $prot;
00037 }
00038 $this->unique = $unique;
00039 $this->DBI = TSD::getDBI();
00040
00041 if($id != '') {
00042 $this->setAttr($unique, $id);
00043 $this->populate();
00044 }
00045 }
00046
00055 protected function checkInit($errmsg) {
00056 if(!TSD::checkInit()) {
00057 $this->setError($errmsg . ' (because TSD cannot be initialized)');
00058 return false;
00059 }
00060 else
00061 return true;
00062 }
00063
00071 protected function populate() {
00072 if(!$this->checkInit('Cannot populate')) return false;
00073
00074 $query = 'SELECT ' . implode(',', array_keys($this->attributes)) . ' FROM ' . $this->table . ' WHERE '
00075 . $this->unique . ' = \'' . mysql_escape_string($this->attributes[$this->unique]) . '\'';
00076 $result = $this->DBI->query($query);
00077 if(count($this->DBI->getError()) > 0) {
00078 $this->setError('Failed to populate ' . $this->table . ' on \'' . $this->unique . '\': '
00079 . implode('; ', $this->DBI->getError()));
00080 return false;
00081 }
00082
00083 if(count($result) == 0) {
00084 return true;
00085 }
00086
00087 if(count($result) > 1)
00088 $this->setWarning('Multiple results for key \'' . $this->attributes[$this->unique] . '\'; using first result');
00089
00090 foreach($result[0] as $a => $v)
00091 $this->setAttr($a, $v);
00092
00093 $this->inDB = true;
00094
00095 return true;
00096 }
00097
00105 public function commit() {
00106 if(!$this->checkInit('Cannot commit')) return false;
00107 if(!$this->isValid()) {
00108 $this->setError('Cannot commit invalid object');
00109 return false;
00110 }
00111 if(!TSD::can('edit')) {
00112 $this->setError('Permission denied');
00113 return false;
00114 }
00115
00116 if($this->inDB) {
00117 $query = 'UPDATE ' . $this->table . ' SET ';
00118 foreach($this->attributes as $a => $v) {
00119 $v = mysql_escape_string($v);
00120 $query .= " $a = '$v',";
00121 }
00122 $query = rtrim($query, ',');
00123 $query .= ' WHERE ' . $this->unique . ' = \'' . mysql_escape_string($this->attributes[$this->unique]) . '\'';
00124 }
00125 else {
00126 $query = 'INSERT INTO ' . $this->table . ' SET ';
00127 foreach($this->attributes as $a => $v) {
00128 if($a == $this->unique && $v == '')
00129 continue;
00130 $v = mysql_escape_string($v);
00131 $query .= " $a = '$v',";
00132 }
00133 $query = rtrim($query, ',');
00134 }
00135
00136 $this->DBI->query($query);
00137 if(count($this->DBI->getError()) > 0) {
00138 $this->setError('Failed to commit because of database error(s): ' . implode('; ', $this->DBI->getError()));
00139 return false;
00140 }
00141
00142 if($this->attributes[$this->unique] == '') {
00143 $this->setAttr($this->unique, $this->DBI->mysql_insert_id());
00144 }
00145
00146 $this->inDB = true;
00147
00148 return true;
00149 }
00150
00159 public function delete() {
00160 if(!$this->checkInit('Cannot delete')) return false;
00161 if(!$this->isValid()) {
00162 $this->setError('Cannot delete invalid object!');
00163 return false;
00164 }
00165 if(!TSD::can('approve')) {
00166 $this->setError('Permission denied');
00167 return false;
00168 }
00169
00170 $query = 'DELETE FROM ' . $this->table . ' WHERE ' . $this->unique . ' = \''
00171 . mysql_escape_string($this->attributes[$this->unique]) . '\'';
00172 $this->DBI->query($query);
00173 if(count($this->DBI->getError()) > 0) {
00174 $this->setError('Failed to delete because of database error(s): ' . implode('; ', $this->DBI->getError()));
00175 return false;
00176 }
00177
00178 $this->attributes = array();
00179
00180 return true;
00181 }
00182
00188 public function getAttributes() {
00189 return array_keys($this->attributes);
00190 }
00191
00197 public function getValues() {
00198 return array_merge($this->attributes);
00199 }
00200
00208 public function getUnique() {
00209 return $this->unique;
00210 }
00211
00219 public function getTable() {
00220 return $this->table;
00221 }
00222
00230 public function isInDB() {
00231 return $this->inDB;
00232 }
00233
00242 public function get($name) {
00243 if(array_key_exists($name, $this->attributes))
00244 return $this->attributes[$name];
00245 $this->setError("No such attribute '$name' in $this->table");
00246 return null;
00247 }
00248
00258 public function set($name, $value) {
00259 if(array_key_exists($name, $this->attributes)) {
00260 if($this->access[$name] === false || (is_string($this->access[$name]) && !TSD::can($this->access[$name]))) {
00261 $this->setError("You do not have permission to change attribute '$name'");
00262 return false;
00263 }
00264 $this->setAttr($name, $value);
00265 return true;
00266 }
00267 $this->setError("No such attribute '$name' in $this->table");
00268 return false;
00269 }
00270
00279 public function __get($name) {
00280 return $this->get($name);
00281 }
00282
00292 public function __set($name, $value) {
00293 return $this->set($name, $value);
00294 }
00295
00305 public function __call($func, $args) {
00306 if(preg_match('/^get([a-zA-Z0-9_]*)$/i', $func, $matches)) {
00307 $name = $matches[1];
00308 if(array_key_exists($name, $this->attributes))
00309 return $this->attributes[$name];
00310 else {
00311 $nameLower = strtolower($name);
00312 foreach($this->attributes as $a => $dummy) {
00313 if(strtolower($a) == $nameLower)
00314 return $this->attributes[$a];
00315 }
00316 }
00317 $this->setError("No such attribute '$name' in $this->table");
00318 return false;
00319 }
00320 else if(preg_match('/^set([a-zA-Z0-9_]*)$/i', $func, $matches)) {
00321 if(count($args) != 1) {
00322 $this->setError('Calls to setXxx() require an argument!');
00323 return false;
00324 }
00325 $name = $matches[1];
00326 if(array_key_exists($name, $this->attributes)) {
00327 if($this->access[$name] === false || (is_string($this->access[$name]) && !TSD::can($this->access[$name]))) {
00328 $this->setError("You do not have permission to change attribute '$name'");
00329 return false;
00330 }
00331 $this->setAttr($name, $args[0]);
00332 return true;
00333 }
00334 else {
00335 $nameLower = strtolower($name);
00336 foreach($this->attributes as $a => $dummy) {
00337 if(strtolower($a) == $nameLower) {
00338 if($this->access[$name] === false ||
00339 (is_string($this->access[$name]) && !TSD::can($this->access[$name])))
00340 {
00341 $this->setError("You do not have permission to change attribute '$a'");
00342 return false;
00343 }
00344 $this->setAttr($a, $args[0]);
00345 return true;
00346 }
00347 }
00348 }
00349 $this->setError("No such attribute '$name' in $this->table");
00350 return false;
00351 }
00352 else {
00353 $this->setError("No such method '$func'");
00354 return false;
00355 }
00356 }
00357
00367 protected function setAttr($field, $value) {
00368 $this->attributes[$field] = $value;
00369 }
00370
00380 protected function getAttr($field, $value) {
00381 return $this->attribute[$field];
00382 }
00383
00400 protected static function getListImpl($class, $table, $attrs, $constraints, $addlConstraints, $limit, $order, $cnt, $ljoin = '') {
00401 $DBI = TSD::getDBI();
00402 if(!$DBI) {
00403 self::setStaticError('Cannot connect to TSD!');
00404 return null;
00405 }
00406
00407 $constraints = array_merge($constraints, $addlConstraints);
00408 $constraintC = self::criteriaToString($constraints, $attrs);
00409 if($constraintC === false)
00410 return null;
00411 $limitC = self::limitToString($limit);
00412 if($limitC === false)
00413 return null;
00414 $orderC = self::orderToString($order, $attrs);
00415 if($orderC === false)
00416 return null;
00417
00418 if($cnt)
00419 $query = 'SELECT COUNT(*) FROM ' . $table;
00420 else
00421 $query = 'SELECT ' . implode(',', $attrs) . ' FROM ' . $table;
00422 if($ljoin != '')
00423 $query .= " $ljoin ";
00424 if($constraintC != '')
00425 $query .= ' WHERE ' . $constraintC;
00426 $query .= ' ' . $limitC . ' ' . $orderC;
00427
00428 $result = $DBI->query($query);
00429 if(count($DBI->getError()) > 0) {
00430 self::setStaticError('Error doing query: ' . implode('; ', $DBI->getError()));
00431 return null;
00432 }
00433
00434 if($cnt)
00435 return $result[0]['COUNT(*)'];
00436
00437 $ret = array();
00438 foreach($result as $r) {
00439 $obj = new $class();
00440 foreach($r as $a => $v)
00441 $obj->setAttr($a, $v);
00442 $obj->inDB = true;
00443 $ret[] = $obj;
00444 }
00445
00446 return $ret;
00447 }
00448
00456 public function toXML() {
00457 $xml = '<' . get_class($this) . '>';
00458 foreach($this->attributes as $a => $v)
00459 $xml .= '<' . $a . '>' . htmlspecialchars($v) . '</' . $a . '>';
00460 $xml .= '<identifier>' . htmlspecialchars($this->getIdentifier()) . '</identifier>';
00461 $xml .= '</' . get_class($this) . '>';
00462 return $xml;
00463 }
00464
00472 public function isLoaded() {
00473 return $this->inDB;
00474 }
00475
00485 protected function addAttribute($name, $access = true) {
00486 $this->setAttr($name, '');
00487 $this->access[$name] = $access;
00488 }
00489
00490 public abstract function isValid();
00491 public abstract function getParent();
00492 public abstract function getIdentifier();
00493 public abstract function listChildren($constraints = array(), $limit = array(), $order = array(), $cnt = false);
00494 public abstract function listValidChildren($constraints = array(), $limit = array(), $order = array(), $cnt = false);
00495
00505 protected static function criteriaToString($arr, $attributes) {
00506 if(is_string($arr))
00507 return $arr;
00508 else if(!is_array($arr)) {
00509 self::setStaticError('Criteria must be string or array!');
00510 return false;
00511 }
00512 else if(count($arr) == 0)
00513 return '';
00514
00515 $collect = "";
00516 $op = "=";
00517 $conj = "AND";
00518 foreach($arr as $k => $v) {
00519
00520 if(ctype_digit((string) $k)) {
00521
00522 if(is_string($v)) {
00523 switch(strtoupper($v)) {
00524 case 'AND':
00525 case 'OR':
00526 $conj = $v;
00527 break;
00528 case '=':
00529 case '>=':
00530 case '<=':
00531 case '<':
00532 case '>':
00533 case '<>':
00534 case 'LIKE':
00535 case 'LIKE BINARY':
00536 case 'IS':
00537 case 'IS NOT':
00538 case 'IS NULL':
00539 case 'IS NOT NULL':
00540 $op = $v;
00541 break;
00542 default:
00543 self::setStaticError("Invalid operator or conjunction in criteria: $v");
00544 return false;
00545 }
00546 }
00547
00548 else if(is_array($v)) {
00549 $res = self::criteriaToString($v, $attributes);
00550 if($res === false)
00551 return false;
00552 if($collect != "")
00553
00554 $collect .= " $conj " . $res;
00555 else
00556 $collect .= $res;
00557 }
00558
00559 else {
00560 self::setStaticError("Invalid operator or conjunction in criteria: $v");
00561 return false;
00562 }
00563 }
00564
00565 else if(is_string($k)) {
00566
00567 if($attributes && !in_array($k, $attributes)) {
00568 self::setStaticError("Invalid field '$k' in criteria");
00569 return false;
00570 }
00571
00572
00573 if(is_array($v)) {
00574 foreach($v as $value) {
00575
00576
00577
00578 if($op == 'IS NULL' || $op == 'IS NOT NULL') {
00579 if($collect != "")
00580 $collect .= " $conj $k $op";
00581 else
00582 $collect = "$k $op";
00583 }
00584 else {
00585
00586 $value = mysql_escape_string($value);
00587 if($collect != "")
00588 $collect .= " $conj $k $op '$value'";
00589 else
00590 $collect = "$k $op '$value'";
00591 }
00592 }
00593 }
00594 else {
00595
00596 if($op == 'IS NULL' || $op == 'IS NOT NULL') {
00597 if($collect != "")
00598 $collect .= " $conj $k $op";
00599 else
00600 $collect = "$k $op";
00601 }
00602 else {
00603 $v = mysql_escape_string($v);
00604 if($collect != "")
00605 $collect .= " $conj $k $op '$v'";
00606 else
00607 $collect = "$k $op '$v'";
00608 }
00609 }
00610 }
00611 }
00612
00613
00614 if($collect != "")
00615 $collect = "($collect)";
00616
00617 return $collect;
00618 }
00619
00629 protected static function limitToString($limit) {
00630 if($limit == "" || count($limit) == 0)
00631 return '';
00632
00633 if(is_array($limit)) {
00634 if(count($limit) == 2) {
00635 if(!ctype_digit((string) $limit[0]) || $limit[0] < 0 || !ctype_digit((string) $limit[1]) || $limit[1] < 0) {
00636 self::setStaticError('Invalid limit values');
00637 return false;
00638 }
00639 return " LIMIT $limit[0], $limit[1]";
00640 }
00641 else if(count($limit) == 1) {
00642 if(!ctype_digit((string) $limit[0]) || $limit[0] < 0) {
00643 self::setStaticError('Invalid limit values');
00644 return false;
00645 }
00646 return " LIMIT $limit[0]";
00647 }
00648 }
00649 else if(ctype_digit((string) $limit)) {
00650 if($limit < 0) {
00651 self::setStaticError('Invalid limit values');
00652 return false;
00653 }
00654 return " LIMIT $limit";
00655 }
00656
00657 self::setStaticError('Invalid limit array!');
00658 return false;
00659 }
00660
00672 protected static function orderToString($order, $attributes = null) {
00673 if($order == "" || count($order) == 0)
00674 return '';
00675
00676 if(is_array($order)) {
00677 $orderStr = ' ORDER BY ';
00678 foreach($order as $key => $value) {
00679 if(ctype_digit((string) $key)) {
00680 if($attributes != null && !in_array($value, $attributes)) {
00681 self::setStaticError('Invalid attribute \'' . $value . '\'');
00682 return false;
00683 }
00684 $orderStr .= $value . ', ';
00685 }
00686 else {
00687 if($attributes != null && !in_array($key, $attributes)) {
00688 self::setStaticError('Invalid attribute \'' . $key . '\'');
00689 return false;
00690 }
00691 $value = strtoupper($value);
00692 if($value != 'ASC' && $value != 'DESC') {
00693 self::setStaticError('Ordering must be ASC or DESC');
00694 return false;
00695 }
00696 $orderStr .= $key . ' ' . $value . ', ';
00697 }
00698 }
00699 return rtrim($orderStr, ', ');
00700 }
00701 else if(is_string($order)) {
00702 return " ORDER BY $order";
00703 }
00704 else {
00705 self::setStaticError('Invalid order by array');
00706 return false;
00707 }
00708 }
00709
00718 protected static function getCWISID($urlOrId) {
00719 if(ctype_digit((string) $urlOrId))
00720 $id = $urlOrId;
00721 else {
00722 $id = SDRService::getCserdIdForURL($urlOrId);
00723 if(!$id)
00724 return false;
00725 }
00726 return $id;
00727 }
00728 }
00729
00730 ?>