$matches[1][0], 'uid' => $matches[2][0]); } public static function typeAndUidToResourceUri($type, $uid) { return self::RESOURCES_NS . $type . '/' . $uid; } // type functions private static $fieldsCache = array(); private static function TYPE() { return get_called_class(); } private static function FIELDS() { $type = static::TYPE(); if (!isset(self::$fieldsCache[$type])) { self::$fieldsCache[$type] = static::createFIELDS(); } return self::$fieldsCache[$type]; } /** Provides the field of this object - implemented by specific type classes * Retrieves an array with the specification of the fields of this object. * The array contains RdfField objects. This function is called only once * per type, because the result is cached. */ protected abstract static function createFIELDS(); // instance fields private $uid = null; private $uri = null; private $values = array(); public function getUri() { return $this->uri; } public function getUid() { return $this->uid; } public function getType() { return static::TYPE(); } // PHP magic functions to expose values public function __construct($uid = null, $values = array()) { if (empty($uid)) { $this->uid = md5(uniqid(rand(), true)); } else { $this->uid = $uid; } $this->uri = self::typeAndUidToResourceUri(static::TYPE(), $this->uid); foreach ($values as $name => $value) { $this->$name = $value; } } private function ensure_field($name) { if (!array_key_exists($name, static::FIELDS())) { throw new Exception("Field " . $name . " not defined on " . self::TYPE()); } } private function get_field($name) { self::ensure_field($name); $fields = static::FIELDS(); return $fields[$name]; } public function __isset($name) { $this->ensure_field($name); return isset($this->values[$name]); } public function __get($name) { $field = self::get_field($name); if (isset($this->values[$name])) { return $this->values[$name]; } return $field->get_null(); } public function __set($name, $value) { self::ensure_field($name); $this->values[$name] = $value; } public function __unset($name) { unset($this->values[$name]); } public function getIterator() { return new ArrayIterator($this->values); } public function __toString() { return self::TYPE() . '(uid=' . $this->uid . ')'; } // RDF database functions private static function get_db_filename() { $fileName = 'data/' . static::TYPE() . 's/' . static::TYPE() . 's.rdf'; $dirName = dirname($fileName); if (!is_dir($dirName)) { mkdir($dirName, 0777 /* PHP's default value */, $recursive = TRUE); } return $fileName; } private static function get_model() { $fileName = self::get_db_filename(); $model = ModelFactory::getDefaultModel(); if (file_exists($fileName)) { $model->load($fileName); } $model->addNamespace('rtr', self::RESOURCES_NS); $model->addNamespace('rtp', self::PREDICATES_NS); return $model; } private static function save_model($model) { $fileName = self::get_db_filename(); return $model->saveAs($fileName, 'rdf'); } protected function evaluate() { foreach (static::FIELDS() as $name => $field) { if ($field->required() && (!is_set($this->values[$name]) || ( $field->multiple() && empty($this->values[$name]) ) )) { throw new Exception("Required field $name not found."); } } } public function save() { $this->evaluate(); $model = self::get_model(); // create new resource and remove old saved resource $resource = new Resource($this->uri); $model->subtract($model->find($resource, null, null)); // set type $model->add(new Statement($resource, new Resource(RDF_NAMESPACE_URI . 'type'), new Resource(self::RESOURCES_NS . static::TYPE()))); // add fields foreach (static::FIELDS() as $name => $field) { if (isset($this->values[$name])) { $values = $this->values[$name]; if (!$field->multiple()) { $values = array($values); } foreach ($values as $value) { $type = $field->type(); $type::save($model, $resource, $name, $value); } } } if (!self::save_model($model)) { throw new Exception('Save failed: internal RDFAPI error.'); } } public static function get($predicates = array()) { $model = self::get_model(); $query = ''; $query .= "PREFIX predicates: <" . self::PREDICATES_NS . ">\n"; $query .= "PREFIX resources: <" . self::RESOURCES_NS . ">\n"; $query .= "SELECT *\n"; $query .= "WHERE {\n"; $query .= "\t?object a resources:" . static::TYPE() . " .\n"; foreach (static::FIELDS() as $name => $field) { if (!$field->multiple()) { $cond = "?object predicates:" . $name . " ?" . $name; if ($field->required() || array_key_exists($name, $predicates)) { $query .= "\t$cond .\n"; } else { $query .= "\tOPTIONAL { $cond . }\n"; } } } if (!empty($predicates)) { $preds = array(); foreach ($predicates as $name => $value) { if ($name == 'uid') { if (!is_string($value)) { $value = $value->getUid(); } $preds[] = 'str(?object) = "' . self::RESOURCES_NS . self::TYPE() . '/' . $value . '"'; } else { $type = self::get_field($name)->type(); $preds[] = $type::predicate($name, $value); } } $query .= "\tFILTER ( " . join(" && ", $preds) . " ) .\n"; } $query .= "}\n"; $results = $model->sparqlQuery($query); $objects = array(); if (!empty($results)) { $realClass = self::TYPE(); foreach ($results as $result) { $uri = $result['?object']->uri; $values = array(); foreach (static::FIELDS() as $name => $field) { if (!$field->multiple()) { $queryName = "?$name"; if (isset($result[$queryName]) && !empty($result[$queryName])) { $type = $field->type(); $values[$name] = $type::load($result[$queryName]); } } else { $values[$name] = self::getMultiple($model, $uri, $name, $field); } } $info = self::resourceUriToTypeAndUid($uri); $objects[] = new $realClass($info['uid'], $values); } } return $objects; } private static function getMultiple($model, $uri, $name, $field) { $query = ''; $query .= "PREFIX predicates: <" . self::PREDICATES_NS . ">\n"; $query .= "SELECT DISTINCT ?item\n"; $query .= "WHERE { <$uri> predicates:$name ?item . }\n"; $results = $model->sparqlQuery($query); $values = array(); if (!empty($results)) { foreach ($results as $result) { $type = $field->type(); $values[] = $type::load($result['?item']); } } return $values; } } class RdfPointer implements RdfResource { private $uri = null; private $uid = null; private $type = null; public function __construct($uri) { $this->uri = $uri; $info = RdfObject::resourceUriToTypeAndUid($uri); $this->uid = $info['uid']; $this->type = $info['type']; } public function getUri() { return $this->uri; } public function getUid() { return $this->uid; } public function getType() { return $this->type; } public function get() { $type = $this->type; return $type::get(array('uid' => $this->uid)); } } class RdfField { const REQUIRED = 1; const MULTIPLE = 2; private $type; private $conditions; public function __construct($type, $conditions = array()) { $this->type = $type; $this->conditions = $conditions; } public function type() { return $this->type; } public function required() { return in_array(self::REQUIRED, $this->conditions); } public function multiple() { return in_array(self::MULTIPLE, $this->conditions); } } function RdfField($type, $conditions = array()) { return new RdfField($type, $conditions); } interface RdfType { static function save($model, $resource, $predicateUri, $value); static function load($value); static function predicate($name, $value); } class RdfString implements RdfType { static function save($model, $resource, $name, $value) { $obj = new Literal($value); $model->add(new Statement($resource, new Resource(RdfObject::PREDICATES_NS . $name), $obj)); } static function load($value) { return $value->label; } static function predicate($name, $value) { return 'str(?' . $name . ') = "' . $value . '"'; } } function RdfString() { return new RdfString(); } class RdfDatetime implements RdfType { static function save($model, $resource, $name, $value) { $obj = new Literal($value->getTimestamp()); $model->add(new Statement($resource, new Resource(RdfObject::PREDICATES_NS . $name), $obj)); } static function load($value) { $obj = new DateTime(); $obj->setTimestamp(intval($value->label)); return $obj; } static function predicate($name, $value) { return 'str(?' . $name . ') = "' . $value . '"'; } } function RdfDatetime() { return new RdfDatetime(); } class RdfBoolean implements RdfType { static function save($model, $resource, $name, $value) { $model->add(new Statement($resource, new Resource(RdfObject::PREDICATES_NS . $name), new Literal((bool) $value))); } static function load($value) { return (bool) $value->label; } static function predicate($name, $value) { return 'str(?' . $name . ') = "' . $value . '"'; } } function RdfBoolean() { return new RdfBoolean(); } class RdfReference implements RdfType { static function save($model, $resource, $name, $value) { if (!is_string($value)) { $value = $value->getUri(); } $obj = new Resource($value); $model->add(new Statement($resource, new Resource(RdfObject::PREDICATES_NS . $name), $obj)); } static function load($value) { return new RdfPointer($value->uri); } static function predicate($name, $value) { return '?' . $name . ' = <' . $value . '>'; } } function RdfReference() { return new RdfReference(); } class Answer extends RdfObject { protected static function createFIELDS() { return array( 'question' => RdfField(RdfReference()), 'values' => RdfField(RdfString(), array(RdfField::MULTIPLE)) ); } } class AnswerSet extends RdfObject { protected static function createFIELDS() { return array( 'survey' => RdfField(RdfReference()), 'respondent' => RdfField(RdfReference()), 'datetime' => RdfField(RdfDatetime()), 'answers' => RdfField(RdfReference(), array(RdfField::MULTIPLE)) ); } } class Application extends RdfObject { protected static function createFIELDS() { return array( 'title' => RdfField(RdfString()), 'description' => RdfField(RdfString()), 'style' => RdfField(RdfString()) ); } } class ApplicationInstance extends RdfObject { protected static function createFIELDS() { return array( 'application' => RdfField(RdfReference()), 'starttime' => RdfField(RdfDatetime()), 'endtime' => RdfField(RdfDatetime()), 'open' => RdfField(RdfBoolean()) ); } } class Question extends RdfObject { protected static function createFIELDS() { return array( 'code' => RdfField(RdfString()), 'title' => RdfField(RdfString()), 'type' => RdfField(RdfString()), 'description' => RdfField(RdfString()), 'category' => RdfField(RdfString()), 'answers' => RdfField(RdfString(), array(RdfField::MULTIPLE)) ); } } class Session extends RdfObject { protected static function createFIELDS() { return array( 'title' => RdfField(RdfString()), 'creator' => RdfField(RdfReference()), 'creationdate' => RdfField(RdfDatetime()), 'pipeline' => RdfField(RdfReference(), array(RdfField::MULTIPLE)) ); } } class SessionInstance extends RdfObject { protected static function createFIELDS() { return array( 'title' => RdfField(RdfString()), 'location' => RdfField(RdfString()), 'facilitator' => RdfField(RdfReference()), 'starttime' => RdfField(RdfDatetime()), 'endtime' => RdfField(RdfDatetime()), 'notes' => RdfField(RdfString(), array(RdfField::MULTIPLE)), 'session' => RdfField(RdfReference()), 'surveyinstances' => RdfField(RdfReference(), array(RdfField::MULTIPLE)), 'applicationinstances' => RdfField(RdfReference(), array(RdfField::MULTIPLE)) ); } } class Survey extends RdfObject { protected static function createFIELDS() { return array( 'title' => RdfField(RdfString()), 'description' => RdfField(RdfString()), 'creator' => RdfField(RdfReference()), 'questions' => RdfField(RdfString(), array(RdfField::MULTIPLE)) ); } } class SurveyInstance extends RdfObject { protected static function createFIELDS() { return array( 'survey' => RdfField(RdfReference()), 'starttime' => RdfField(RdfDatetime()), 'endtime' => RdfField(RdfDatetime()), 'open' => RdfField(RdfBoolean()), 'presetanswers' => RdfField(RdfReference(), array(RdfField::MULTIPLE)), 'answersets' => RdfField(RdfReference(), array(RdfField::MULTIPLE)) ); } } class User extends RdfObject { protected static function createFIELDS() { return array( 'email' => RdfField(RdfString()), 'passwordHash' => RdfField(RdfString()), 'passwordSalt' => RdfField(RdfString()) ); } } ?>