Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
38.46% |
5 / 13 |
CRAP | |
58.55% |
137 / 234 |
OAI2Server | |
0.00% |
0 / 1 |
|
38.46% |
5 / 13 |
522.67 | |
58.19% |
135 / 232 |
__construct($uri, $args, $identifyResponse, $callbacks) | |
100.00% |
1 / 1 |
4 | |
100.00% |
19 / 19 |
|||
response() | |
100.00% |
1 / 1 |
3 | |
100.00% |
9 / 9 |
|||
Identify() | |
0.00% |
0 / 1 |
5.40 | |
55.56% |
5 / 9 |
|||
ListMetadataFormats() | |
0.00% |
0 / 1 |
9.80 | |
69.57% |
16 / 23 |
|||
ListSets() | |
0.00% |
0 / 1 |
11.69 | |
67.86% |
19 / 28 |
|||
GetRecord() | |
0.00% |
0 / 1 |
16.96 | |
63.33% |
19 / 30 |
|||
ListIdentifiers() | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
ListRecords() | |
0.00% |
0 / 1 |
121.82 | |
49.33% |
37 / 75 |
|||
add_metadata($cur_record, $record) | |
100.00% |
1 / 1 |
3 | |
100.00% |
9 / 9 |
|||
createResumptionToken($delivered_records) | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 12 |
|||
readResumptionToken($resumptionToken) | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 10 |
|||
formatDatestamp($datestamp) | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
checkDateFormat($date) | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 6 |
<?php | |
require_once('oai2exception.php'); | |
require_once('oai2xml.php'); | |
/** | |
* This is an implementation of OAI Data Provider version 2.0. | |
* @see http://www.openarchives.org/OAI/2.0/openarchivesprotocol.htm | |
*/ | |
class OAI2Server { | |
public $errors = array(); | |
private $args = array(); | |
private $verb = ''; | |
private $token_prefix = '/tmp/oai_pmh-'; | |
private $token_valid = 86400; | |
function __construct($uri, $args, $identifyResponse, $callbacks) { | |
$this->uri = $uri; | |
if (!isset($args['verb']) || empty($args['verb'])) { | |
$this->errors[] = new OAI2Exception('badVerb'); | |
} else { | |
$verbs = array('Identify', 'ListMetadataFormats', 'ListSets', 'ListIdentifiers', 'ListRecords', 'GetRecord'); | |
if (in_array($args['verb'], $verbs)) { | |
$this->verb = $args['verb']; | |
unset($args['verb']); | |
$this->args = $args; | |
$this->identifyResponse = $identifyResponse; | |
$this->listMetadataFormatsCallback = $callbacks['ListMetadataFormats']; | |
$this->listSetsCallback = $callbacks['ListSets']; | |
$this->listRecordsCallback = $callbacks['ListRecords']; | |
$this->getRecordCallback = $callbacks['GetRecord']; | |
$this->response = new OAI2XMLResponse($this->uri, $this->verb, $this->args); | |
call_user_func(array($this, $this->verb)); | |
} else { | |
$this->errors[] = new OAI2Exception('badVerb', $args['verb']); | |
} | |
} | |
} | |
public function response() { | |
if (empty($this->errors)) { | |
return $this->response->doc; | |
} else { | |
$errorResponse = new OAI2XMLResponse($this->uri, $this->verb, $this->args); | |
$oai_node = $errorResponse->doc->documentElement; | |
foreach($this->errors as $e) { | |
$node = $errorResponse->addChild($oai_node,"error",$e->getMessage()); | |
$node->setAttribute("code",$e->getOAI2Code()); | |
} | |
return $errorResponse->doc; | |
} | |
} | |
public function Identify() { | |
if (count($this->args) > 0) { | |
foreach($this->args as $key => $val) { | |
$this->errors[] = new OAI2Exception('badArgument', $key, $val); | |
} | |
} else { | |
foreach($this->identifyResponse as $key => $val) { | |
$this->response->addToVerbNode($key, $val); | |
} | |
} | |
} | |
public function ListMetadataFormats() { | |
foreach ($this->args as $argument => $value) { | |
if ($argument != 'identifier') { | |
$this->errors[] = new OAI2Exception('badArgument', $argument, $value); | |
} | |
} | |
if (isset($this->args['identifier'])) { | |
$identifier = $this->args['identifier']; | |
} else { | |
$identifier = ''; | |
} | |
if (empty($this->errors)) { | |
try { | |
if ($formats = call_user_func($this->listMetadataFormatsCallback, $identifier)) { | |
foreach($formats as $key => $val) { | |
$cmf = $this->response->addToVerbNode("metadataFormat"); | |
$this->response->addChild($cmf,'metadataPrefix',$key); | |
$this->response->addChild($cmf,'schema',$val['schema']); | |
$this->response->addChild($cmf,'metadataNamespace',$val['metadataNamespace']); | |
} | |
} else { | |
$this->errors[] = new OAI2Exception('noMetadataFormats'); | |
} | |
} catch (OAI2Exception $e) { | |
$this->errors[] = $e; | |
} | |
} | |
} | |
public function ListSets() { | |
if (isset($this->args['resumptionToken'])) { | |
if (count($this->args) > 1) { | |
$this->errors[] = new OAI2Exception('exclusiveArgument'); | |
} else { | |
if ((int)$val+$this->token_valid < time()) { | |
$this->errors[] = new OAI2Exception('badResumptionToken'); | |
} | |
} | |
$resumptionToken = $this->args['resumptionToken']; | |
} else { | |
$resumptionToken = null; | |
} | |
if (empty($this->errors)) { | |
if ($sets = call_user_func($this->listSetsCallback, $resumptionToken)) { | |
foreach($sets as $set) { | |
$setNode = $this->response->addToVerbNode("set"); | |
foreach($set as $key => $val) { | |
if($key=='setDescription') { | |
$desNode = $this->response->addChild($setNode,$key); | |
$des = $this->response->doc->createDocumentFragment(); | |
$des->appendXML($val); | |
$desNode->appendChild($des); | |
} else { | |
$this->response->addChild($setNode,$key,$val); | |
} | |
} | |
} | |
} else { | |
$this->errors[] = new OAI2Exception('noSetHierarchy'); | |
} | |
} | |
} | |
public function GetRecord() { | |
if (!isset($this->args['metadataPrefix'])) { | |
$this->errors[] = new OAI2Exception('missingArgument', 'metadataPrefix'); | |
} else { | |
$metadataFormats = call_user_func($this->listMetadataFormatsCallback); | |
if (!isset($metadataFormats[$this->args['metadataPrefix']])) { | |
$this->errors[] = new OAI2Exception('cannotDisseminateFormat', 'metadataPrefix', $this->args['metadataPrefix']); | |
} | |
} | |
if (!isset($this->args['identifier'])) { | |
$this->errors[] = new OAI2Exception('missingArgument', 'identifier'); | |
} | |
if (empty($this->errors)) { | |
try { | |
if ($record = call_user_func($this->getRecordCallback, $this->args['identifier'], $this->args['metadataPrefix'])) { | |
$identifier = $record['identifier']; | |
$datestamp = $this->formatDatestamp($record['datestamp']); | |
$set = $record['set']; | |
$status_deleted = (isset($record['deleted']) && ($record['deleted'] == 'true') && | |
(($this->identifyResponse['deletedRecord'] == 'transient') || | |
($this->identifyResponse['deletedRecord'] == 'persistent'))); | |
$cur_record = $this->response->addToVerbNode('record'); | |
$cur_header = $this->response->createHeader($identifier, $datestamp, $set, $cur_record); | |
if ($status_deleted) { | |
$cur_header->setAttribute("status","deleted"); | |
} else { | |
$this->add_metadata($cur_record, $record); | |
} | |
} else { | |
$this->errors[] = new OAI2Exception('idDoesNotExist', 'identifier', $identifier); | |
} | |
} catch (OAI2Exception $e) { | |
$this->errors[] = $e; | |
} | |
} | |
} | |
public function ListIdentifiers() { | |
return $this->ListRecords(); | |
} | |
public function ListRecords() { | |
$maxItems = 1000; | |
$deliveredRecords = 0; | |
$metadataPrefix = $this->args['metadataPrefix']; | |
$from = isset($this->args['from']) ? $this->args['from'] : ''; | |
$until = isset($this->args['until']) ? $this->args['until'] : ''; | |
$set = isset($this->args['set']) ? $this->args['set'] : ''; | |
if (isset($this->args['resumptionToken'])) { | |
if (count($this->args) > 1) { | |
$this->errors[] = new OAI2Exception('exclusiveArgument'); | |
} else { | |
if ((int)$val+$this->token_valid < time()) { | |
$this->errors[] = new OAI2Exception('badResumptionToken'); | |
} else { | |
if (!file_exists($this->token_prefix.$this->args['resumptionToken'])) { | |
$this->errors[] = new OAI2Exception('badResumptionToken', '', $this->args['resumptionToken']); | |
} else { | |
if ($readings = $this->readResumptionToken($this->token_prefix.$this->args['resumptionToken'])) { | |
list($deliveredRecords, $metadataPrefix, $from, $until, $set) = $readings; | |
} else { | |
$this->errors[] = new OAI2Exception('badResumptionToken', '', $this->args['resumptionToken']); | |
} | |
} | |
} | |
} | |
} else { | |
if (!isset($this->args['metadataPrefix'])) { | |
$this->errors[] = new OAI2Exception('missingArgument', 'metadataPrefix'); | |
} else { | |
$metadataFormats = call_user_func($this->listMetadataFormatsCallback); | |
if (!isset($metadataFormats[$this->args['metadataPrefix']])) { | |
$this->errors[] = new OAI2Exception('cannotDisseminateFormat', 'metadataPrefix', $this->args['metadataPrefix']); | |
} | |
} | |
if (isset($this->args['from'])) { | |
if(!$this->checkDateFormat($this->args['from'])) { | |
$this->errors[] = new OAI2Exception('badGranularity', 'from', $this->args['from']); | |
} | |
} | |
if (isset($this->args['until'])) { | |
if(!$this->checkDateFormat($this->args['until'])) { | |
$this->errors[] = new OAI2Exception('badGranularity', 'until', $this->args['until']); | |
} | |
} | |
} | |
if (empty($this->errors)) { | |
try { | |
$records_count = call_user_func($this->listRecordsCallback, $metadataPrefix, $from, $until, $set, true); | |
$records = call_user_func($this->listRecordsCallback, $metadataPrefix, $from, $until, $set, false, $deliveredRecords, $maxItems); | |
foreach ($records as $record) { | |
$identifier = $record['identifier']; | |
$datestamp = $this->formatDatestamp($record['datestamp']); | |
$setspec = $record['set']; | |
$status_deleted = (isset($record['deleted']) && ($record['deleted'] === true) && | |
(($this->identifyResponse['deletedRecord'] == 'transient') || | |
($this->identifyResponse['deletedRecord'] == 'persistent'))); | |
if($this->verb == 'ListRecords') { | |
$cur_record = $this->response->addToVerbNode('record'); | |
$cur_header = $this->response->createHeader($identifier, $datestamp,$setspec,$cur_record); | |
if (!$status_deleted) { | |
$this->add_metadata($cur_record, $record); | |
} | |
} else { // for ListIdentifiers, only identifiers will be returned. | |
$cur_header = $this->response->createHeader($identifier, $datestamp,$setspec); | |
} | |
if ($status_deleted) { | |
$cur_header->setAttribute("status","deleted"); | |
} | |
} | |
// Will we need a new ResumptionToken? | |
if ($records_count - $deliveredRecords > $maxItems) { | |
$deliveredRecords += $maxItems; | |
$restoken = $this->createResumptionToken($deliveredRecords); | |
$expirationDatetime = gmstrftime('%Y-%m-%dT%TZ', time()+$this->token_valid); | |
} elseif (isset($args['resumptionToken'])) { | |
// Last delivery, return empty ResumptionToken | |
$restoken = null; | |
$expirationDatetime = null; | |
} | |
if (isset($restoken)) { | |
$this->response->createResumptionToken($restoken,$expirationDatetime,$records_count,$deliveredRecords); | |
} | |
} catch (OAI2Exception $e) { | |
$this->errors[] = $e; | |
} | |
} | |
} | |
private function add_metadata($cur_record, $record) { | |
$meta_node = $this->response->addChild($cur_record ,"metadata"); | |
$schema_node = $this->response->addChild($meta_node, $record['metadata']['container_name']); | |
foreach ($record['metadata']['container_attributes'] as $name => $value) { | |
$schema_node->setAttribute($name, $value); | |
} | |
foreach ($record['metadata']['fields'] as $name => $value) { | |
$this->response->addChild($schema_node, $name, $value); | |
} | |
} | |
private function createResumptionToken($delivered_records) { | |
list($usec, $sec) = explode(" ", microtime()); | |
$token = ((int)($usec*1000) + (int)($sec*1000)); | |
$fp = fopen ($this->token_prefix.$token, 'w'); | |
if($fp==false) { | |
exit("Cannot write. Writer permission needs to be changed."); | |
} | |
fputs($fp, "$delivered_records#"); | |
fputs($fp, "$metadataPrefix#"); | |
fputs($fp, "{$this->args['from']}#"); | |
fputs($fp, "{$this->args['until']}#"); | |
fputs($fp, "{$this->args['set']}#"); | |
fclose($fp); | |
return $token; | |
} | |
private function readResumptionToken($resumptionToken) { | |
$rtVal = false; | |
$fp = fopen($resumptionToken, 'r'); | |
if ($fp != false) { | |
$filetext = fgets($fp, 255); | |
$textparts = explode('#', $filetext); | |
fclose($fp); | |
unlink($resumptionToken); | |
$rtVal = array_values($textparts); | |
} | |
return $rtVal; | |
} | |
/** | |
* All datestamps used in this system are GMT even | |
* return value from database has no TZ information | |
*/ | |
private function formatDatestamp($datestamp) { | |
return date("Y-m-d\TH:i:s\Z",strtotime($datestamp)); | |
} | |
/** | |
* The database uses datastamp without time-zone information. | |
* It needs to clean all time-zone informaion from time string and reformat it | |
*/ | |
private function checkDateFormat($date) { | |
$date = str_replace(array("T","Z")," ",$date); | |
$time_val = strtotime($date); | |
if(!$time_val) return false; | |
if(strstr($date,":")) { | |
return date("Y-m-d H:i:s",$time_val); | |
} else { | |
return date("Y-m-d",$time_val); | |
} | |
} | |
} |