diff --git a/OAI2ServerTest.php b/OAI2ServerTest.php
index 99da377..a1ae340 100644
--- a/OAI2ServerTest.php
+++ b/OAI2ServerTest.php
@@ -274,6 +274,7 @@ class OAI2ServerTest extends PHPUnit_Framework_TestCase {
';
+
$f = $this->xml->createDocumentFragment();
$f->appendXML($xml);
$this->xml->documentElement->appendChild($f);
@@ -300,6 +301,39 @@ class OAI2ServerTest extends PHPUnit_Framework_TestCase {
}
function testIllegalVerb() {
+ $xml = '
+ | Code Coverage |
+ ||||||||
+ | Lines |
+ Functions and Methods |
+ Classes and Traits |
+ ||||||
Total | +
+
+
+ |
+ 69.51% |
+ 269 / 387 |
+
+
+
+ |
+ 50.00% |
+ 10 / 20 |
+
+
+
+ |
+ 33.33% |
+ 1 / 3 |
+
oai2.php | +
+
+
+ |
+ 85.51% |
+ 59 / 69 |
+ + | + | + | + | ||
oai2exception.php | +
+
+
+ |
+ 100.00% |
+ 43 / 43 |
+
+
+
+ |
+ 100.00% |
+ 2 / 2 |
+
+
+
+ |
+ 100.00% |
+ 1 / 1 |
+
oai2server.php | +
+
+
+ |
+ 58.55% |
+ 137 / 234 |
+
+
+
+ |
+ 38.46% |
+ 5 / 13 |
+
+
+
+ |
+ 0.00% |
+ 0 / 1 |
+
oai2xml.php | +
+
+
+ |
+ 73.17% |
+ 30 / 41 |
+
+
+
+ |
+ 60.00% |
+ 3 / 5 |
+
+
+
+ |
+ 0.00% |
+ 0 / 1 |
+
t |
+ | Code Coverage |
+ |||||||||
+ | Classes and Traits |
+ Functions and Methods |
+ Lines |
+ |||||||
Total | ++ | + | + | + | CRAP | +
+
+
+ |
+ 85.51% |
+ 59 / 69 |
+ ||
anonymous function($identifier, $metadataPrefix) | +
+
+
+ |
+ 0.00% |
+ 0 / 1 |
+ 0 | +
+
+
+ |
+ 75.00% |
+ 3 / 4 |
+
<?php | |
require_once('oai2server.php'); | |
/** | |
* Identifier settings. It needs to have proper values to reflect the settings of the data provider. | |
* Is MUST be declared in this order | |
* | |
* - $identifyResponse['repositoryName'] : compulsory. A human readable name for the repository; | |
* - $identifyResponse['baseURL'] : compulsory. The base URL of the repository; | |
* - $identifyResponse['protocolVersion'] : compulsory. The version of the OAI-PMH supported by the repository; | |
* - $identifyResponse['earliestDatestamp'] : compulsory. A UTCdatetime that is the guaranteed lower limit of all datestamps recording changes, modifications, or deletions in the repository. A repository must not use datestamps lower than the one specified by the content of the earliestDatestamp element. earliestDatestamp must be expressed at the finest granularity supported by the repository. | |
* - $identifyResponse['deletedRecord'] : the manner in which the repository supports the notion of deleted records. Legitimate values are no ; transient ; persistent with meanings defined in the section on deletion. | |
* - $identifyResponse['granularity'] : the finest harvesting granularity supported by the repository. The legitimate values are YYYY-MM-DD and YYYY-MM-DDThh:mm:ssZ with meanings as defined in ISO8601. | |
* | |
*/ | |
$identifyResponse = array(); | |
$identifyResponse["repositoryName"] = 'OAI2 PMH Test'; | |
$identifyResponse["baseURL"] = 'http://198.199.108.242/~neis/oai_pmh/oai2.php'; | |
$identifyResponse["protocolVersion"] = '2.0'; | |
$identifyResponse['adminEmail'] = 'danielneis@gmail.com'; | |
$identifyResponse["earliestDatestamp"] = '2013-01-01T12:00:00Z'; | |
$identifyResponse["deletedRecord"] = 'no'; // How your repository handles deletions | |
// no: The repository does not maintain status about deletions. | |
// It MUST NOT reveal a deleted status. | |
// persistent: The repository persistently keeps track about deletions | |
// with no time limit. It MUST consistently reveal the status | |
// of a deleted record over time. | |
// transient: The repository does not guarantee that a list of deletions is | |
// maintained. It MAY reveal a deleted status for records. | |
$identifyResponse["granularity"] = 'YYYY-MM-DDThh:mm:ssZ'; | |
$example_record = array('identifier' => 'a.b.c', | |
'datestamp' => date('Y-m-d-H:s'), | |
'set' => 'class:activity', | |
'metadata' => array( | |
'container_name' => 'oai_dc:dc', | |
'container_attributes' => array( | |
'xmlns:oai_dc' => "http://www.openarchives.org/OAI/2.0/oai_dc/", | |
'xmlns:dc' => "http://purl.org/dc/elements/1.1/", | |
'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance", | |
'xsi:schemaLocation' => | |
'http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd' | |
), | |
'fields' => array( | |
'dc:title' => 'Testing records', | |
'dc:author' => 'Neis' | |
) | |
)); | |
/* unit tests ;) */ | |
if (!isset($args)) { | |
$args = $_GET; | |
} | |
if (!isset($uri)) { | |
$uri = 'test.oai_pmh'; | |
} | |
$oai2 = new OAI2Server($uri, $args, $identifyResponse, | |
array( | |
'ListMetadataFormats' => | |
function($identifier = '') { | |
if (!empty($identifier) && $identifier != 'a.b.c') { | |
throw new OAI2Exception('idDoesNotExist', '', $identifier); | |
} | |
return | |
array('rif' => array('metadataPrefix'=>'rif', | |
'schema'=>'http://services.ands.org.au/sandbox/orca/schemata/registryObjects.xsd', | |
'metadataNamespace'=>'http://ands.org.au/standards/rif-cs/registryObjects/', | |
), | |
'oai_dc' => array('metadataPrefix'=>'oai_dc', | |
'schema'=>'http://www.openarchives.org/OAI/2.0/oai_dc.xsd', | |
'metadataNamespace'=>'http://www.openarchives.org/OAI/2.0/oai_dc/', | |
'record_prefix'=>'dc', | |
'record_namespace' => 'http://purl.org/dc/elements/1.1/')); | |
}, | |
'ListSets' => | |
function($resumptionToken = '') { | |
return | |
array ( | |
array('setSpec'=>'class:collection', 'setName'=>'Collections'), | |
array('setSpec'=>'math', 'setName'=>'Mathematics') , | |
array('setSpec'=>'phys', 'setName'=>'Physics'), | |
array('setSpec'=>'phdthesis', 'setName'=>'PHD Thesis', | |
'setDescription'=> | |
'<oai_dc:dc xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" '. | |
' xmlns:dc="http://purl.org/dc/elements/1.1/" '. | |
' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '. | |
' xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ '. | |
' http://www.openarchives.org/OAI/2.0/oai_dc.xsd"> '. | |
' <dc:description>This set contains metadata describing '. | |
' electronic music recordings made during the 1950ies</dc:description> '. | |
' </oai_dc:dc>')); | |
}, | |
'ListRecords' => | |
function($metadataPrefix, $from = '', $until = '', $set = '', $count = false, $deliveredRecords = 0, $maxItems = 0) use ($example_record) { | |
// throws new OAI2Exception('noRecordsMatch') | |
// throws new OAI2Exception('noSetHierarchy') | |
if ($count) { | |
return 1; | |
} | |
return array($example_record); | |
}, | |
'GetRecord' => | |
function($identifier, $metadataPrefix) use ($example_record) { | |
if ($identifier != 'a.b.c') { | |
throw new OAI2Exception('idDoesNotExist', '', $identifier); | |
} | |
return $example_record; | |
}, | |
) | |
); | |
$response = $oai2->response(); | |
if (isset($return)) { | |
return $response; | |
} else { | |
$response->formatOutput = true; | |
$response->preserveWhiteSpace = false; | |
header('Content-Type: text/xml'); | |
echo $response->saveXML(); | |
} |
+ | Code Coverage |
+ |||||||||
+ | Classes and Traits |
+ Functions and Methods |
+ Lines |
+ |||||||
Total | +
+
+
+ |
+ 100.00% |
+ 1 / 1 |
+
+
+
+ |
+ 100.00% |
+ 2 / 2 |
+ CRAP | +
+
+
+ |
+ 100.00% |
+ 43 / 43 |
+
OAI2Exception | +
+
+
+ |
+ 100.00% |
+ 1 / 1 |
+
+
+
+ |
+ 100.00% |
+ 2 / 2 |
+ 2 | +
+
+
+ |
+ 100.00% |
+ 43 / 43 |
+
__construct($code = 0, $argument = '', $value = '') | +
+
+
+ |
+ 100.00% |
+ 1 / 1 |
+ 1 | +
+
+
+ |
+ 100.00% |
+ 42 / 42 |
+ |||
getOAI2Code() | +
+
+
+ |
+ 100.00% |
+ 1 / 1 |
+ 1 | +
+
+
+ |
+ 100.00% |
+ 1 / 1 |
+
<?php | |
class OAI2Exception extends Exception { | |
function __construct($code = 0, $argument = '', $value = '') { | |
$this->errorTable = array( | |
'badArgument' => array( | |
'text' => "Attribute '{$argument}' is not allowed to appear in element 'request'.", | |
), | |
'badResumptionToken' => array( | |
'text' => "The resumptionToken '{$value}' does not exist or has already expired.", | |
), | |
'badGranularity' => array( | |
'text' => "The value '{$value}' of attribute '{$argument}' on element 'request' is not valid with respect to its type, 'UTCdatetimeType'.", | |
'code' => 'badArgument', | |
), | |
'badVerb' => array( | |
'text' => "Illegal OAI verb", | |
'code' => 'badVerb', | |
), | |
'cannotDisseminateFormat' => array( | |
'text' => "The metadata format '{$value}' given by {$argument} is not supported by this repository.", | |
), | |
'exclusiveArgument' => array( | |
'text' => 'The usage of resumptionToken as an argument allows no other arguments.', | |
'code' => 'badArgument', | |
), | |
'idDoesNotExist' => array( | |
'text' => "The value '{$value}' of the identifier does not exist in this repository.", | |
), | |
'missingArgument' => array( | |
'text' => "The required argument '{$argument}' is missing in the request.", | |
'code' => 'badArgument', | |
), | |
'noRecordsMatch' => array( | |
'text' => 'The combination of the given values results in an empty list.', | |
), | |
'noMetadataFormats' => array( | |
'text' => 'There are no metadata formats available for the specified item.', | |
), | |
'noSetHierarchy' => array( | |
'text' => 'This repository does not support sets.', | |
), | |
'sameArgument' => array( | |
'text' => 'Do not use the same argument more than once.', | |
'code' => 'badArgument', | |
), | |
'sameVerb' => array( | |
'text' => 'Do not use verb more than once.', | |
'code' => 'badVerb', | |
), | |
'notImp' => array( | |
'text' => 'Not yet implemented.', | |
'code' => 'debug', | |
), | |
''=> array( | |
'text' => "Unknown error: code: '{'code'}', argument: '{$argument}', value: '{$value}'", | |
'code' => 'badArgument', | |
) | |
); | |
parent::__construct($this->errorTable[$code]['text']); | |
$this->code = $code; | |
} | |
public function getOAI2Code() { | |
return $this->code; | |
} | |
} |
+ | 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); | |
} | |
} | |
} |
+ | Code Coverage |
+ |||||||||
+ | Classes and Traits |
+ Functions and Methods |
+ Lines |
+ |||||||
Total | +
+
+
+ |
+ 0.00% |
+ 0 / 1 |
+
+
+
+ |
+ 60.00% |
+ 3 / 5 |
+ CRAP | +
+
+
+ |
+ 73.17% |
+ 30 / 41 |
+
OAI2XMLResponse | +
+
+
+ |
+ 0.00% |
+ 0 / 1 |
+
+
+
+ |
+ 60.00% |
+ 3 / 5 |
+ 13.34 | +
+
+
+ |
+ 73.17% |
+ 30 / 41 |
+
__construct($uri, $verb, $request_args) | +
+
+
+ |
+ 100.00% |
+ 1 / 1 |
+ 3 | +
+
+
+ |
+ 100.00% |
+ 17 / 17 |
+ |||
addChild($mom_node,$name, $value='') | +
+
+
+ |
+ 100.00% |
+ 1 / 1 |
+ 1 | +
+
+
+ |
+ 100.00% |
+ 3 / 3 |
+ |||
addToVerbNode($nodeName, $value=null) | +
+
+
+ |
+ 100.00% |
+ 1 / 1 |
+ 1 | +
+
+
+ |
+ 100.00% |
+ 1 / 1 |
+ |||
createHeader($identifier,$timestamp,$ands_class, $add_to_node=null) | +
+
+
+ |
+ 0.00% |
+ 0 / 1 |
+ 4.47 | +
+
+
+ |
+ 69.23% |
+ 9 / 13 |
+ |||
createResumptionToken($token, $expirationdatetime, $num_rows, $cursor=null) | +
+
+
+ |
+ 0.00% |
+ 0 / 1 |
+ 6 | +
+
+
+ |
+ 0.00% |
+ 0 / 7 |
+
<?php | |
class OAI2XMLResponse { | |
public $doc; // DOMDocument. Handle of current XML Document object | |
function __construct($uri, $verb, $request_args) { | |
$this->verb = $verb; | |
$this->doc = new DOMDocument("1.0","UTF-8"); | |
$oai_node = $this->doc->createElement("OAI-PMH"); | |
$oai_node->setAttribute("xmlns","http://www.openarchives.org/OAI/2.0/"); | |
$oai_node->setAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); | |
$oai_node->setAttribute("xsi:schemaLocation","http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd"); | |
$this->addChild($oai_node,"responseDate",gmdate("Y-m-d\TH:i:s\Z")); | |
$this->doc->appendChild($oai_node); | |
$request = $this->addChild($this->doc->documentElement,"request",$uri); | |
$request->setAttribute('verb', $this->verb); | |
foreach($request_args as $key => $value) { | |
$request->setAttribute($key,$value); | |
} | |
if (!empty($this->verb)) { | |
$this->verbNode = $this->addChild($this->doc->documentElement,$this->verb); | |
} | |
} | |
/** | |
* Add a child node to a parent node on a XML Doc: a worker function. | |
* | |
* @param $mom_node Type: DOMNode. The target node. | |
* @param $name Type: string. The name of child nade is being added | |
* @param $value Type: string. Text for the adding node if it is a text node. | |
* | |
* @return DOMElement $added_node * The newly created node | |
*/ | |
function addChild($mom_node,$name, $value='') { | |
$added_node = $this->doc->createElement($name,$value); | |
$added_node = $mom_node->appendChild($added_node); | |
return $added_node; | |
} | |
/** | |
* Add direct child nodes to verb node (OAI-PMH), e.g. response to ListMetadataFormats. | |
* Different verbs can have different required child nodes. | |
* \see create_record, create_header | |
* | |
* \param $nodeName Type: string. The name of appending node. | |
* \param $value Type: string. The content of appending node. | |
*/ | |
function addToVerbNode($nodeName, $value=null) { | |
return $this->addChild($this->verbNode,$nodeName,$value); | |
} | |
/** | |
* Headers are enclosed inside of \<record\> to the query of ListRecords, ListIdentifiers and etc. | |
* | |
* \param $identifier Type: string. The identifier string for node \<identifier\>. | |
* \param $timestamp Type: timestamp. Timestapme in UTC format for node \<datastamp\>. | |
* \param $ands_class Type: mix. Can be an array or just a string. Content of \<setSpec\>. | |
* \param $add_to_node Type: DOMElement. Default value is null. | |
* In normal cases, $add_to_node is the \<record\> node created previously. When it is null, the newly created header node is attatched to $this->verbNode. | |
* Otherwise it will be attatched to the desired node defined in $add_to_node. | |
*/ | |
function createHeader($identifier,$timestamp,$ands_class, $add_to_node=null) { | |
if(is_null($add_to_node)) { | |
$header_node = $this->addToVerbNode("header"); | |
} else { | |
$header_node = $this->addChild($add_to_node,"header"); | |
} | |
$this->addChild($header_node,"identifier",$identifier); | |
$this->addChild($header_node,"datestamp",$timestamp); | |
if (is_array($ands_class)) { | |
foreach ($ands_class as $setspec) { | |
$this->addChild($header_node,"setSpec",$setspec); | |
} | |
} else { | |
$this->addChild($header_node,"setSpec",$ands_class); | |
} | |
return $header_node; | |
} | |
/** | |
* If there are too many records request could not finished a resumpToken is generated to let harvester know | |
* | |
* @param $token Type: string. A random number created somewhere? | |
* @param $expirationdatetime Type: string. A string representing time. | |
* @param $num_rows Type: integer. Number of records retrieved. | |
* @param $cursor Type: string. Cursor can be used for database to retrieve next time. | |
*/ | |
function createResumptionToken($token, $expirationdatetime, $num_rows, $cursor=null) { | |
$resump_node = $this->addChild($this->verbNode,"resumptionToken",$token); | |
if(isset($expirationdatetime)) { | |
$resump_node->setAttribute("expirationDate",$expirationdatetime); | |
} | |
$resump_node->setAttribute("completeListSize",$num_rows); | |
$resump_node->setAttribute("cursor",$cursor); | |
} | |
} |