PageRenderTime 63ms CodeModel.GetById 37ms RepoModel.GetById 0ms app.codeStats 1ms

/arc2/store/ARC2_StoreEndpoint.php

https://bitbucket.org/igharib/eth
PHP | 1080 lines | 946 code | 81 blank | 53 comment | 88 complexity | ab95859ac6c90608e66f58223dce1425 MD5 | raw file
  1. <?php
  2. /**
  3. * ARC2 SPARQL Endpoint
  4. *
  5. * @author Benjamin Nowack
  6. * @license <http://arc.semsol.org/license>
  7. * @homepage <http://arc.semsol.org/>
  8. * @package ARC2
  9. * @version 2010-11-16
  10. */
  11. ARC2::inc('Store');
  12. class ARC2_StoreEndpoint extends ARC2_Store {
  13. function __construct($a, &$caller) {
  14. parent::__construct($a, $caller);
  15. }
  16. function __init() {
  17. parent::__init();
  18. $this->headers = array('http' => 'HTTP/1.1 200 OK', 'vary' => 'Vary: Accept');
  19. $this->read_key = $this->v('endpoint_read_key', '', $this->a);
  20. $this->write_key = $this->v('endpoint_write_key', '', $this->a);
  21. $this->timeout = $this->v('endpoint_timeout', 0, $this->a);
  22. $this->a['store_allow_extension_functions'] = $this->v('store_allow_extension_functions', 0, $this->a);
  23. $this->allow_sql = $this->v('endpoint_enable_sql_output', 0, $this->a);
  24. $this->result = '';
  25. }
  26. /* */
  27. function getQueryString($mthd = '') {
  28. $r = '';
  29. if (!$mthd || ($mthd == 'post')) {
  30. $r = @file_get_contents('php://input');
  31. }
  32. $r = !$r ?$this->v1('QUERY_STRING', '', $_SERVER) : $r;
  33. return $r;
  34. }
  35. function p($name='', $mthd = '', $multi = '', $default = '') {
  36. $mthd = strtolower($mthd);
  37. if($multi){
  38. $qs = $this->getQueryString($mthd);
  39. if (preg_match_all('/\&' . $name . '=([^\&]+)/', $qs, $m)){
  40. foreach ($m[1] as $i => $val) {
  41. $m[1][$i] = stripslashes($val);
  42. }
  43. return $m[1];
  44. }
  45. return $default ? $default : array();
  46. }
  47. $args = array_merge($_GET, $_POST);
  48. $r = isset($args[$name]) ? $args[$name] : $default;
  49. return is_array($r) ? $r : stripslashes($r);
  50. }
  51. /* */
  52. function getFeatures() {
  53. return $this->v1('endpoint_features', array(), $this->a);
  54. }
  55. function setHeader($k, $v) {
  56. $this->headers[$k] = $v;
  57. }
  58. function sendHeaders() {
  59. if (!isset($this->is_dump) || !$this->is_dump) {
  60. $this->setHeader('content-length', 'Content-Length: ' . strlen($this->getResult()));
  61. foreach ($this->headers as $k => $v) {
  62. header($v);
  63. }
  64. }
  65. }
  66. function getResult() {
  67. return $this->result;
  68. }
  69. /* */
  70. function handleRequest($auto_setup = 0) {
  71. if (!$this->isSetUp()) {
  72. if ($auto_setup) {
  73. $this->setUp();
  74. return $this->handleRequest(0);
  75. }
  76. else {
  77. $this->setHeader('http', 'HTTP/1.1 400 Bad Request');
  78. $this->setHeader('content-type', 'Content-type: text/plain; charset=utf-8');
  79. $this->result = 'Missing configuration or the endpoint store was not set up yet.';
  80. }
  81. }
  82. elseif (($img = $this->p('img'))) {
  83. $this->handleImgRequest($img);
  84. }
  85. elseif (($q = $this->p('query'))) {
  86. $this->checkProcesses();
  87. $this->handleQueryRequest($q);
  88. if ($this->p('show_inline')) {
  89. $this->query_result = '
  90. <div class="results">
  91. ' . ($this->p('output') != 'htmltab' ? '<pre>' . htmlspecialchars($this->getResult()) . '</pre>' : $this->getResult()) . '
  92. </div>
  93. ';
  94. $this->handleEmptyRequest(1);
  95. }
  96. }
  97. else {
  98. $this->handleEmptyRequest();
  99. }
  100. }
  101. function go($auto_setup = 0) {
  102. $this->handleRequest($auto_setup);
  103. $this->sendHeaders();
  104. echo $this->getResult();
  105. }
  106. /* */
  107. function handleImgRequest($img) {
  108. $this->setHeader('content-type', 'Content-type: image/gif');
  109. $imgs = array(
  110. 'bg_body' => base64_decode('R0lGODlhAQBkAMQAAPf39/Hx8erq6vPz8/Ly8u/v7+np6fT09Ovr6/b29u3t7ejo6Pz8/Pv7+/39/fr6+vj4+P7+/vn5+f///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAABAGQAAAUp4GIIiFIExHAkAAC9cAxJdG3TT67vTe//jKBQ6Cgaj5GkcpmcOJ/QZwgAOw=='),
  111. );
  112. $this->result = isset($imgs[$img]) ? $imgs[$img] : '';
  113. $this->sendHeaders();
  114. echo $this->getResult();
  115. exit;
  116. }
  117. /* */
  118. function handleEmptyRequest($force = 0) {
  119. /* service description */
  120. $formats = array(
  121. 'rdfxml' => 'RDFXML', 'rdf+xml' => 'RDFXML', 'html' => 'HTML'
  122. );
  123. if (!$force && $this->getResultFormat($formats, 'html') != 'HTML') {
  124. $this->handleServiceDescriptionRequest();
  125. }
  126. else {
  127. $this->setHeader('content-type', 'Content-type: text/html; charset=utf-8');
  128. $this->result = $this->getHTMLFormDoc();
  129. }
  130. }
  131. /* */
  132. function handleServiceDescriptionRequest() {
  133. $q = '
  134. PREFIX void: <http://rdfs.org/ns/void#>
  135. CONSTRUCT {
  136. <> void:sparqlEndpoint <> .
  137. }
  138. WHERE {
  139. ?s ?p ?o .
  140. } LIMIT 1
  141. ';
  142. $this->handleQueryRequest($q);
  143. }
  144. /* */
  145. function checkProcesses() {
  146. if (method_exists($this->caller, 'checkSPARQLEndpointProcesses')) {
  147. $sub_r = $this->caller->checkSPARQLEndpointProcesses();
  148. }
  149. elseif ($this->timeout) {
  150. $this->killDBProcesses('', $this->timeout);
  151. }
  152. }
  153. /* */
  154. function handleQueryRequest($q) {
  155. if (preg_match('/^dump/i', $q)) {
  156. $infos = array('query' => array('type' => 'dump'));
  157. $this->is_dump = 1;
  158. }
  159. else {
  160. ARC2::inc('SPARQLPlusParser');
  161. $p = new ARC2_SPARQLPlusParser($this->a, $this);
  162. $p->parse($q);
  163. $infos = $p->getQueryInfos();
  164. }
  165. /* errors? */
  166. if ($errors = $this->getErrors()) {
  167. $this->setHeader('http', 'HTTP/1.1 400 Bad Request');
  168. $this->setHeader('content-type', 'Content-type: text/plain; charset=utf-8');
  169. $this->result = join("\n", $errors);
  170. return true;
  171. }
  172. $qt = $infos['query']['type'];
  173. /* wrong read key? */
  174. if ($this->read_key && ($this->p('key') != $this->read_key) && preg_match('/^(select|ask|construct|describe|dump)$/', $qt)) {
  175. $this->setHeader('http', 'HTTP/1.1 401 Access denied');
  176. $this->setHeader('content-type', 'Content-type: text/plain; charset=utf-8');
  177. $this->result = 'Access denied. Missing or wrong "key" parameter.';
  178. return true;
  179. }
  180. /* wrong write key? */
  181. if ($this->write_key && ($this->p('key') != $this->write_key) && preg_match('/^(load|insert|delete|update)$/', $qt)) {
  182. $this->setHeader('http', 'HTTP/1.1 401 Access denied');
  183. $this->setHeader('content-type', 'Content-type: text/plain; charset=utf-8');
  184. $this->result = 'Access denied. Missing or wrong "key" parameter.';
  185. return true;
  186. }
  187. /* non-allowed query type? */
  188. if (!in_array($qt, $this->getFeatures())) {
  189. $this->setHeader('http', 'HTTP/1.1 401 Access denied');
  190. $this->setHeader('content-type', 'Content-type: text/plain; charset=utf-8');
  191. $this->result = 'Access denied for "' .$qt. '" query';
  192. return true;
  193. }
  194. /* load/insert/delete via GET */
  195. if (in_array($qt, array('load', 'insert', 'delete')) && isset($_GET['query'])) {
  196. $this->setHeader('http', 'HTTP/1.1 501 Not Implemented');
  197. $this->setHeader('content-type', 'Content-type: text/plain; charset=utf-8');
  198. $this->result = 'Query type "' .$qt. '" not supported via GET';
  199. return true;
  200. }
  201. /* unsupported query type */
  202. if (!in_array($qt, array('select', 'ask', 'describe', 'construct', 'load', 'insert', 'delete', 'dump'))) {
  203. $this->setHeader('http', 'HTTP/1.1 501 Not Implemented');
  204. $this->setHeader('content-type', 'Content-type: text/plain; charset=utf-8');
  205. $this->result = 'Unsupported query type "' .$qt. '"';
  206. return true;
  207. }
  208. /* adjust infos */
  209. $infos = $this->adjustQueryInfos($infos);
  210. $t1 = ARC2::mtime();
  211. $r = array('result' => $this->runQuery($infos, $qt));
  212. $t2 = ARC2::mtime();
  213. $r['query_time'] = $t2 - $t1;
  214. /* query errors? */
  215. if ($errors = $this->getErrors()) {
  216. $this->setHeader('http', 'HTTP/1.1 400 Bad Request');
  217. $this->setHeader('content-type', 'Content-type: text/plain; charset=utf-8');
  218. $this->result = 'Error: ' . join("\n", $errors);
  219. return true;
  220. }
  221. /* result */
  222. $m = 'get' . ucfirst($qt) . 'ResultDoc';
  223. if (method_exists($this, $m)) {
  224. $this->result = $this->$m($r);
  225. }
  226. else {
  227. $this->setHeader('content-type', 'Content-type: text/plain; charset=utf-8');
  228. $this->result = 'Result serializer not available, dumping raw data:' . "\n" . print_r($r, 1);
  229. }
  230. }
  231. /* */
  232. function adjustQueryInfos($infos) {
  233. /* limit */
  234. if ($max_l = $this->v('endpoint_max_limit', 0, $this->a)) {
  235. if ($this->v('limit', $max_l + 1, $infos['query']) > $max_l) {
  236. $infos['query']['limit'] = $max_l;
  237. }
  238. }
  239. /* default-graph-uri / named-graph-uri */
  240. $dgs = $this->p('default-graph-uri', '', 1);
  241. $ngs = $this->p('named-graph-uri', '', 1);
  242. if (count(array_merge($dgs, $ngs))) {
  243. $ds = array();
  244. foreach ($dgs as $g) {
  245. $ds[] = array('graph' => $this->calcURI($g), 'named' => 0);
  246. }
  247. foreach ($ngs as $g) {
  248. $ds[] = array('graph' => $this->calcURI($g), 'named' => 1);
  249. }
  250. $infos['query']['dataset'] = $ds;
  251. }
  252. /* infos result format */
  253. if (($this->p('format') == 'infos') || ($this->p('output') == 'infos')) {
  254. $infos['result_format'] = 'structure';
  255. }
  256. /* sql result format */
  257. if (($this->p('format') == 'sql') || ($this->p('output') == 'sql')) {
  258. $infos['result_format'] = 'sql';
  259. }
  260. return $infos;
  261. }
  262. /* */
  263. function getResultFormat($formats, $default) {
  264. $prefs = array();
  265. /* arg */
  266. if (($v = $this->p('format')) || ($v = $this->p('output'))) {
  267. $prefs[] = $v;
  268. }
  269. /* accept header */
  270. $vals = explode(',', $_SERVER['HTTP_ACCEPT']);
  271. if ($vals) {
  272. $o_vals = array();
  273. foreach ($vals as $val) {
  274. if (preg_match('/(rdf\+n3|x\-turtle|rdf\+xml|sparql\-results\+xml|sparql\-results\+json|json)/', $val, $m)) {
  275. $o_vals[$m[1]] = 1;
  276. if (preg_match('/\;q\=([0-9\.]+)/', $val, $sub_m)) {
  277. $o_vals[$m[1]] = 1 * $sub_m[1];
  278. }
  279. }
  280. }
  281. arsort($o_vals);
  282. foreach ($o_vals as $val => $prio) {
  283. $prefs[] = $val;
  284. }
  285. }
  286. /* default */
  287. $prefs[] = $default;
  288. foreach ($prefs as $pref) {
  289. if (isset($formats[$pref])) {
  290. return $formats[$pref];
  291. }
  292. }
  293. }
  294. /* SELECT */
  295. function getSelectResultDoc($r) {
  296. $formats = array(
  297. 'xml' => 'SPARQLXML', 'sparql-results+xml' => 'SPARQLXML',
  298. 'json' => 'SPARQLJSON', 'sparql-results+json' => 'SPARQLJSON',
  299. 'php_ser' => 'PHPSER', 'plain' => 'Plain',
  300. 'sql' => ($this->allow_sql ? 'Plain' : 'xSQL'),
  301. 'infos' => 'Plain',
  302. 'htmltab' => 'HTMLTable',
  303. 'tsv' => 'TSV',
  304. );
  305. if ($f = $this->getResultFormat($formats, 'xml')) {
  306. $m = 'get' . $f . 'SelectResultDoc';
  307. return method_exists($this, $m) ? $this->$m($r) : 'not implemented';
  308. }
  309. return '';
  310. }
  311. function getSPARQLXMLSelectResultDoc($r) {
  312. $this->setHeader('content-type', 'Content-Type: application/sparql-results+xml');
  313. $vars = $r['result']['variables'];
  314. $rows = $r['result']['rows'];
  315. $dur = $r['query_time'];
  316. $nl = "\n";
  317. /* doc */
  318. $r = '' .
  319. '<?xml version="1.0"?>' .
  320. $nl . '<sparql xmlns="http://www.w3.org/2005/sparql-results#">' .
  321. '';
  322. /* head */
  323. $r .= $nl . ' <head>';
  324. $r .= $nl . ' <!-- query time: '. round($dur, 4) .' sec -->';
  325. if (is_array($vars)) {
  326. foreach ($vars as $var) {
  327. $r .= $nl . ' <variable name="' .$var. '"/>';
  328. }
  329. }
  330. $r .= $nl . ' </head>';
  331. /* results */
  332. $r .= $nl . ' <results>';
  333. if (is_array($rows)) {
  334. foreach ($rows as $row) {
  335. $r .= $nl . ' <result>';
  336. foreach ($vars as $var) {
  337. if (isset($row[$var])) {
  338. $r .= $nl . ' <binding name="' .$var. '">';
  339. if ($row[$var . ' type'] == 'uri') {
  340. $r .= $nl . ' <uri>' .htmlspecialchars($row[$var]). '</uri>';
  341. }
  342. elseif ($row[$var . ' type'] == 'bnode') {
  343. $r .= $nl . ' <bnode>' .substr($row[$var], 2). '</bnode>';
  344. }
  345. else {
  346. $dt = isset($row[$var . ' datatype']) ? ' datatype="' .htmlspecialchars($row[$var . ' datatype']). '"' : '';
  347. $lang = isset($row[$var . ' lang']) ? ' xml:lang="' .htmlspecialchars($row[$var . ' lang']). '"' : '';
  348. $r .= $nl . ' <literal' . $dt . $lang . '>' .htmlspecialchars($row[$var]). '</literal>';
  349. }
  350. $r .= $nl . ' </binding>';
  351. }
  352. }
  353. $r .= $nl . ' </result>';
  354. }
  355. }
  356. $r .= $nl . ' </results>';
  357. /* /doc */
  358. $r .= $nl . '</sparql>';
  359. return $r;
  360. }
  361. function getSPARQLJSONSelectResultDoc($r) {
  362. $con = $this->getDBCon();
  363. $this->setHeader('content-type', 'Content-Type: application/sparql-results+json');
  364. $vars = $r['result']['variables'];
  365. $rows = $r['result']['rows'];
  366. $dur = $r['query_time'];
  367. $nl = "\n";
  368. /* doc */
  369. $r = '{';
  370. /* head */
  371. $r .= $nl . ' "head": {';
  372. $r .= $nl . ' "vars": [';
  373. $first_var = 1;
  374. foreach ($vars as $var) {
  375. $r .= $first_var ? $nl : ',' . $nl;
  376. $r .= ' "' .$var. '"';
  377. $first_var = 0;
  378. }
  379. $r .= $nl . ' ]';
  380. $r .= $nl . ' },';
  381. /* results */
  382. $r .= $nl . ' "results": {';
  383. $r .= $nl . ' "bindings": [';
  384. $first_row = 1;
  385. foreach ($rows as $row) {
  386. $r .= $first_row ? $nl : ',' . $nl;
  387. $r .= ' {';
  388. $first_var = 1;
  389. foreach ($vars as $var) {
  390. if (isset($row[$var])) {
  391. $r .= $first_var ? $nl : ',' . $nl . $nl;
  392. $r .= ' "' .$var. '": {';
  393. if ($row[$var . ' type'] == 'uri') {
  394. $r .= $nl . ' "type": "uri",';
  395. $r .= $nl . ' "value": "' .mysql_real_escape_string($row[$var], $con). '"';
  396. }
  397. elseif ($row[$var . ' type'] == 'bnode') {
  398. $r .= $nl . ' "type": "bnode",';
  399. $r .= $nl . ' "value": "' . substr($row[$var], 2) . '"';
  400. }
  401. else {
  402. $dt = isset($row[$var . ' datatype']) ? ',' . $nl .' "datatype": "' .mysql_real_escape_string($row[$var . ' datatype'], $con). '"' : '';
  403. $lang = isset($row[$var . ' lang']) ? ',' . $nl .' "xml:lang": "' .mysql_real_escape_string($row[$var . ' lang'], $con). '"' : '';
  404. $type = $dt ? 'typed-literal' : 'literal';
  405. $r .= $nl . ' "type": "' . $type . '",';
  406. $r .= $nl . ' "value": "' . $this->jsonEscape($row[$var]) . '"';
  407. $r .= $dt . $lang;
  408. }
  409. $r .= $nl . ' }';
  410. $first_var = 0;
  411. }
  412. }
  413. $r .= $nl . ' }';
  414. $first_row = 0;
  415. }
  416. $r .= $nl . ' ]';
  417. $r .= $nl . ' }';
  418. /* /doc */
  419. $r .= $nl . '}';
  420. if (($v = $this->p('jsonp')) || ($v = $this->p('callback'))) {
  421. $r = $v . '(' . $r . ')';
  422. }
  423. return $r;
  424. }
  425. function getPHPSERSelectResultDoc($r) {
  426. $this->setHeader('content-type', 'Content-Type: text/plain');
  427. return serialize($r);
  428. }
  429. function getPlainSelectResultDoc($r) {
  430. $this->setHeader('content-type', 'Content-Type: text/plain');
  431. return print_r($r['result'], 1);
  432. }
  433. function getHTMLTableSelectResultDoc($r) {
  434. $this->setHeader('content-type', 'Content-Type: text/html; charset=utf-8');
  435. $vars = $r['result']['variables'];
  436. $rows = $r['result']['rows'];
  437. $dur = $r['query_time'];
  438. if ($this->p('show_inline')) return '<table>' . $this->getHTMLTableRows($rows, $vars) . '</table>';
  439. return '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  440. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  441. ' .$this->getHTMLDocHead() . '
  442. <body>
  443. <table>
  444. ' . $this->getHTMLTableRows($rows, $vars) . '
  445. </table>
  446. </body>
  447. </html>
  448. ';
  449. }
  450. function getHTMLTableRows($rows, $vars) {
  451. $r = '';
  452. foreach ($rows as $row) {
  453. $hr = '';
  454. $rr = '';
  455. foreach ($vars as $var) {
  456. $hr .= $r ? '' : '<th>' . htmlspecialchars($var) . '</th>';
  457. $rr .= '<td>' . @htmlspecialchars($row[$var]) . '</td>';
  458. }
  459. $r .= $hr ? '<tr>' . $hr . '</tr>' : '';
  460. $r .= '<tr>' . $rr . '</tr>';
  461. }
  462. return $r ? $r : '<em>No results found</em>';
  463. }
  464. function getTSVSelectResultDoc($r) {
  465. $this->setHeader('content-type', 'Content-Type: text/plain; charset=utf-8');
  466. $vars = $r['result']['variables'];
  467. $rows = $r['result']['rows'];
  468. $dur = $r['query_time'];
  469. return $this->getTSVRows($rows, $vars);
  470. }
  471. function getTSVRows($rows, $vars) {
  472. $r = '';
  473. $delim = "\t";
  474. $esc_delim = "\\t";
  475. foreach ($rows as $row) {
  476. $hr = '';
  477. $rr = '';
  478. foreach ($vars as $var) {
  479. $hr .= $r ? '' : ($hr ? $delim . $var : $var);
  480. $val = isset($row[$var]) ? str_replace($delim, $esc_delim, $row[$var]) : '';
  481. $rr .= $rr ? $delim . $val : $val;
  482. }
  483. $r .= $hr . "\n" . $rr;
  484. }
  485. return $r ? $r : 'No results found';
  486. }
  487. /* ASK */
  488. function getAskResultDoc($r) {
  489. $formats = array(
  490. 'xml' => 'SPARQLXML', 'sparql-results+xml' => 'SPARQLXML',
  491. 'json' => 'SPARQLJSON', 'sparql-results+json' => 'SPARQLJSON',
  492. 'plain' => 'Plain',
  493. 'php_ser' => 'PHPSER',
  494. 'sql' => ($this->allow_sql ? 'Plain' : 'xSQL'),
  495. 'infos' => 'Plain',
  496. );
  497. if ($f = $this->getResultFormat($formats, 'xml')) {
  498. $m = 'get' . $f . 'AskResultDoc';
  499. return method_exists($this, $m) ? $this->$m($r) : 'not implemented';
  500. }
  501. return '';
  502. }
  503. function getSPARQLXMLAskResultDoc($r) {
  504. $this->setHeader('content-type', 'Content-Type: application/sparql-results+xml');
  505. $r_val = $r['result'] ? 'true' : 'false';
  506. $dur = $r['query_time'];
  507. $nl = "\n";
  508. return '' .
  509. '<?xml version="1.0"?>' .
  510. $nl . '<sparql xmlns="http://www.w3.org/2005/sparql-results#">' .
  511. $nl . ' <head>' .
  512. $nl . ' <!-- query time: '. round($dur, 4) .' sec -->' .
  513. $nl . ' </head>' .
  514. $nl . ' <boolean>' .$r_val. '</boolean>' .
  515. $nl . '</sparql>' .
  516. '';
  517. }
  518. function getSPARQLJSONAskResultDoc($r) {
  519. $this->setHeader('content-type', 'Content-Type: application/sparql-results+json');
  520. $r_val = $r['result'] ? 'true' : 'false';
  521. $dur = $r['query_time'];
  522. $nl = "\n";
  523. $r = '' .
  524. $nl . '{' .
  525. $nl . ' "head": {' .
  526. $nl . ' },' .
  527. $nl . ' "boolean" : ' . $r_val .
  528. $nl . '}' .
  529. '';
  530. if (($v = $this->p('jsonp')) || ($v = $this->p('callback'))) {
  531. $r = $v . '(' . $r . ')';
  532. }
  533. return $r;
  534. }
  535. function getPHPSERAskResultDoc($r) {
  536. $this->setHeader('content-type', 'Content-Type: text/plain');
  537. return serialize($r);
  538. }
  539. function getPlainAskResultDoc($r) {
  540. $this->setHeader('content-type', 'Content-Type: text/plain');
  541. return $r['result'] ? 'true' : 'false';
  542. }
  543. /* CONSTRUCT */
  544. function getConstructResultDoc($r) {
  545. $formats = array(
  546. 'rdfxml' => 'RDFXML', 'rdf+xml' => 'RDFXML',
  547. 'json' => 'RDFJSON', 'rdf+json' => 'RDFJSON',
  548. 'turtle' => 'Turtle', 'x-turtle' => 'Turtle', 'rdf+n3' => 'Turtle',
  549. 'php_ser' => 'PHPSER',
  550. 'sql' => ($this->allow_sql ? 'Plain' : 'xSQL'),
  551. 'infos' => 'Plain',
  552. );
  553. if ($f = $this->getResultFormat($formats, 'rdfxml')) {
  554. $m = 'get' . $f . 'ConstructResultDoc';
  555. return method_exists($this, $m) ? $this->$m($r) : 'not implemented';
  556. }
  557. return '';
  558. }
  559. function getRDFXMLConstructResultDoc($r) {
  560. $this->setHeader('content-type', 'Content-Type: application/rdf+xml');
  561. $index = $r['result'];
  562. $ser = ARC2::getRDFXMLSerializer($this->a);
  563. $dur = $r['query_time'];
  564. return $ser->getSerializedIndex($index) . "\n" . '<!-- query time: ' . $dur . ' -->';
  565. }
  566. function getTurtleConstructResultDoc($r) {
  567. $this->setHeader('content-type', 'Content-Type: application/x-turtle');
  568. $index = $r['result'];
  569. $ser = ARC2::getTurtleSerializer($this->a);
  570. $dur = $r['query_time'];
  571. return '# query time: ' . $dur . "\n" . $ser->getSerializedIndex($index);
  572. }
  573. function getRDFJSONConstructResultDoc($r) {
  574. $this->setHeader('content-type', 'Content-Type: application/json');
  575. $index = $r['result'];
  576. $ser = ARC2::getRDFJSONSerializer($this->a);
  577. $dur = $r['query_time'];
  578. $r = $ser->getSerializedIndex($index);
  579. if (($v = $this->p('jsonp')) || ($v = $this->p('callback'))) {
  580. $r = $v . '(' . $r . ')';
  581. }
  582. return $r;
  583. }
  584. function getPHPSERConstructResultDoc($r) {
  585. $this->setHeader('content-type', 'Content-Type: text/plain');
  586. return serialize($r);
  587. }
  588. function getPlainConstructResultDoc($r) {
  589. $this->setHeader('content-type', 'Content-Type: text/plain');
  590. return print_r($r['result'], 1);
  591. }
  592. /* DESCRIBE */
  593. function getDescribeResultDoc($r) {
  594. $formats = array(
  595. 'rdfxml' => 'RDFXML', 'rdf+xml' => 'RDFXML',
  596. 'json' => 'RDFJSON', 'rdf+json' => 'RDFJSON',
  597. 'turtle' => 'Turtle', 'x-turtle' => 'Turtle', 'rdf+n3' => 'Turtle',
  598. 'php_ser' => 'PHPSER',
  599. 'sql' => ($this->allow_sql ? 'Plain' : 'xSQL'),
  600. 'infos' => 'Plain'
  601. );
  602. if ($f = $this->getResultFormat($formats, 'rdfxml')) {
  603. $m = 'get' . $f . 'DescribeResultDoc';
  604. return method_exists($this, $m) ? $this->$m($r) : 'not implemented';
  605. }
  606. return '';
  607. }
  608. function getRDFXMLDescribeResultDoc($r) {
  609. $this->setHeader('content-type', 'Content-Type: application/rdf+xml');
  610. $index = $r['result'];
  611. $ser = ARC2::getRDFXMLSerializer($this->a);
  612. $dur = $r['query_time'];
  613. return $ser->getSerializedIndex($index) . "\n" . '<!-- query time: ' . $dur . ' -->';
  614. }
  615. function getTurtleDescribeResultDoc($r) {
  616. $this->setHeader('content-type', 'Content-Type: application/x-turtle');
  617. $index = $r['result'];
  618. $ser = ARC2::getTurtleSerializer($this->a);
  619. $dur = $r['query_time'];
  620. return '# query time: ' . $dur . "\n" . $ser->getSerializedIndex($index);
  621. }
  622. function getRDFJSONDescribeResultDoc($r) {
  623. $this->setHeader('content-type', 'Content-Type: application/json');
  624. $index = $r['result'];
  625. $ser = ARC2::getRDFJSONSerializer($this->a);
  626. $dur = $r['query_time'];
  627. $r = $ser->getSerializedIndex($index);
  628. if (($v = $this->p('jsonp')) || ($v = $this->p('callback'))) {
  629. $r = $v . '(' . $r . ')';
  630. }
  631. return $r;
  632. }
  633. function getPHPSERDescribeResultDoc($r) {
  634. $this->setHeader('content-type', 'Content-Type: text/plain');
  635. return serialize($r);
  636. }
  637. function getPlainDescribeResultDoc($r) {
  638. $this->setHeader('content-type', 'Content-Type: text/plain');
  639. return print_r($r['result'], 1);
  640. }
  641. /* DUMP */
  642. function getDumpResultDoc() {
  643. $this->headers = array();
  644. return '';
  645. }
  646. /* LOAD */
  647. function getLoadResultDoc($r) {
  648. $formats = array(
  649. 'xml' => 'SPARQLXML', 'sparql-results+xml' => 'SPARQLXML',
  650. 'json' => 'SPARQLJSON', 'sparql-results+json' => 'SPARQLJSON',
  651. 'plain' => 'Plain',
  652. 'php_ser' => 'PHPSER',
  653. 'sql' => ($this->allow_sql ? 'Plain' : 'xSQL'),
  654. 'infos' => 'Plain',
  655. );
  656. if ($f = $this->getResultFormat($formats, 'xml')) {
  657. $m = 'get' . $f . 'LoadResultDoc';
  658. return method_exists($this, $m) ? $this->$m($r) : 'not implemented';
  659. }
  660. return '';
  661. }
  662. function getSPARQLXMLLoadResultDoc($r) {
  663. $this->setHeader('content-type', 'Content-Type: application/sparql-results+xml');
  664. $r_val = $r['result']['t_count'];
  665. $dur = $r['query_time'];
  666. $nl = "\n";
  667. return '' .
  668. '<?xml version="1.0"?>' .
  669. $nl . '<sparql xmlns="http://www.w3.org/2005/sparql-results#">' .
  670. $nl . ' <head>' .
  671. $nl . ' <!-- query time: '. round($dur, 4) .' sec -->' .
  672. $nl . ' </head>' .
  673. $nl . ' <inserted>' .$r_val. '</inserted>' .
  674. $nl . '</sparql>' .
  675. '';
  676. }
  677. function getSPARQLJSONLoadResultDoc($r) {
  678. $this->setHeader('content-type', 'Content-Type: application/sparql-results+json');
  679. $r_val = $r['result']['t_count'];
  680. $dur = $r['query_time'];
  681. $nl = "\n";
  682. $r = '' .
  683. $nl . '{' .
  684. $nl . ' "head": {' .
  685. $nl . ' },' .
  686. $nl . ' "inserted" : ' . $r_val .
  687. $nl . '}' .
  688. '';
  689. if (($v = $this->p('jsonp')) || ($v = $this->p('callback'))) {
  690. $r = $v . '(' . $r . ')';
  691. }
  692. return $r;
  693. }
  694. function getPHPSERLoadResultDoc($r) {
  695. $this->setHeader('content-type', 'Content-Type: text/plain');
  696. return serialize($r);
  697. }
  698. function getPlainLoadResultDoc($r) {
  699. $this->setHeader('content-type', 'Content-Type: text/plain');
  700. return print_r($r['result'], 1);
  701. }
  702. /* DELETE */
  703. function getDeleteResultDoc($r) {
  704. $formats = array(
  705. 'xml' => 'SPARQLXML', 'sparql-results+xml' => 'SPARQLXML',
  706. 'json' => 'SPARQLJSON', 'sparql-results+json' => 'SPARQLJSON',
  707. 'plain' => 'Plain',
  708. 'php_ser' => 'PHPSER'
  709. );
  710. if ($f = $this->getResultFormat($formats, 'xml')) {
  711. $m = 'get' . $f . 'DeleteResultDoc';
  712. return method_exists($this, $m) ? $this->$m($r) : 'not implemented';
  713. }
  714. return '';
  715. }
  716. function getSPARQLXMLDeleteResultDoc($r) {
  717. $this->setHeader('content-type', 'Content-Type: application/sparql-results+xml');
  718. $r_val = $r['result']['t_count'];
  719. $dur = $r['query_time'];
  720. $nl = "\n";
  721. return '' .
  722. '<?xml version="1.0"?>' .
  723. $nl . '<sparql xmlns="http://www.w3.org/2005/sparql-results#">' .
  724. $nl . ' <head>' .
  725. $nl . ' <!-- query time: '. round($dur, 4) .' sec -->' .
  726. $nl . ' </head>' .
  727. $nl . ' <deleted>' .$r_val. '</deleted>' .
  728. $nl . '</sparql>' .
  729. '';
  730. }
  731. function getSPARQLJSONDeleteResultDoc($r) {
  732. $this->setHeader('content-type', 'Content-Type: application/sparql-results+json');
  733. $r_val = $r['result']['t_count'];
  734. $dur = $r['query_time'];
  735. $nl = "\n";
  736. $r = '' .
  737. $nl . '{' .
  738. $nl . ' "head": {' .
  739. $nl . ' },' .
  740. $nl . ' "deleted" : ' . $r_val .
  741. $nl . '}' .
  742. '';
  743. if (($v = $this->p('jsonp')) || ($v = $this->p('callback'))) {
  744. $r = $v . '(' . $r . ')';
  745. }
  746. return $r;
  747. }
  748. function getPHPSERDeleteResultDoc($r) {
  749. $this->setHeader('content-type', 'Content-Type: text/plain');
  750. return serialize($r);
  751. }
  752. function getPlainDeleteResultDoc($r) {
  753. $this->setHeader('content-type', 'Content-Type: text/plain');
  754. return print_r($r['result'], 1);
  755. }
  756. /* INSERT */
  757. function getInsertResultDoc($r) {
  758. $formats = array(
  759. 'xml' => 'SPARQLXML', 'sparql-results+xml' => 'SPARQLXML',
  760. 'json' => 'SPARQLJSON', 'sparql-results+json' => 'SPARQLJSON',
  761. 'plain' => 'Plain',
  762. 'php_ser' => 'PHPSER'
  763. );
  764. if ($f = $this->getResultFormat($formats, 'xml')) {
  765. $m = 'get' . $f . 'InsertResultDoc';
  766. return method_exists($this, $m) ? $this->$m($r) : 'not implemented';
  767. }
  768. return '';
  769. }
  770. function getSPARQLXMLInsertResultDoc($r) {
  771. $this->setHeader('content-type', 'Content-Type: application/sparql-results+xml');
  772. $r_val = $r['result']['t_count'];
  773. $dur = $r['query_time'];
  774. $nl = "\n";
  775. return '' .
  776. '<?xml version="1.0"?>' .
  777. $nl . '<sparql xmlns="http://www.w3.org/2005/sparql-results#">' .
  778. $nl . ' <head>' .
  779. $nl . ' <!-- query time: '. round($dur, 4) .' sec -->' .
  780. $nl . ' </head>' .
  781. $nl . ' <inserted>' .$r_val. '</inserted>' .
  782. $nl . '</sparql>' .
  783. '';
  784. }
  785. function getSPARQLJSONInsertResultDoc($r) {
  786. $this->setHeader('content-type', 'Content-Type: application/sparql-results+json');
  787. $r_val = $r['result']['t_count'];
  788. $dur = $r['query_time'];
  789. $nl = "\n";
  790. $r = '' .
  791. $nl . '{' .
  792. $nl . ' "head": {' .
  793. $nl . ' },' .
  794. $nl . ' "inserted" : ' . $r_val .
  795. $nl . '}' .
  796. '';
  797. if (($v = $this->p('jsonp')) || ($v = $this->p('callback'))) {
  798. $r = $v . '(' . $r . ')';
  799. }
  800. return $r;
  801. }
  802. function getPHPSERInsertResultDoc($r) {
  803. $this->setHeader('content-type', 'Content-Type: text/plain');
  804. return serialize($r);
  805. }
  806. function getPlainInsertResultDoc($r) {
  807. $this->setHeader('content-type', 'Content-Type: text/plain');
  808. return print_r($r['result'], 1);
  809. }
  810. /* */
  811. function jsonEscape($v) {
  812. if (function_exists('json_encode')) return trim(json_encode($v), '"');
  813. $from = array("\\", "\r", "\t", "\n", '"', "\b", "\f", "/");
  814. $to = array('\\\\', '\r', '\t', '\n', '\"', '\b', '\f', '\/');
  815. return str_replace($from, $to, $v);
  816. }
  817. /* */
  818. function getHTMLFormDoc() {
  819. return '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  820. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  821. ' . $this->getHTMLDocHead() . '
  822. ' . $this->getHTMLDocBody() . '
  823. </html>
  824. ';
  825. }
  826. function getHTMLDocHead() {
  827. return '
  828. <head>
  829. <title>' . $this->getHTMLDocTitle() . '</title>
  830. <style type="text/css">
  831. ' . $this->getHTMLDocCSS() . '
  832. </style>
  833. </head>
  834. ';
  835. }
  836. function getHTMLDocTitle() {
  837. return $this->v('endpoint_title', 'ARC SPARQL+ Endpoint', $this->a);
  838. }
  839. function getHTMLDocHeading() {
  840. return $this->v('endpoint_heading', 'ARC SPARQL+ Endpoint (v' . ARC2::getVersion() . ')', $this->a);
  841. }
  842. function getHTMLDocCSS() {
  843. $default = '
  844. body {
  845. font-size: 14px;
  846. font-family: Trebuchet MS, Verdana, Geneva, sans-serif;
  847. background: #fff url(?img=bg_body) top center repeat-x;
  848. padding: 5px 20px 20px 20px;
  849. color: #666;
  850. }
  851. h1 { font-size: 1.6em; font-weight: normal; }
  852. a { color: #c00000; }
  853. th, td {
  854. border: 1px dotted #eee;
  855. padding: 2px 4px;
  856. }
  857. #sparql-form {
  858. margin-bottom: 30px;
  859. }
  860. #query {
  861. float: left;
  862. width: 60%;
  863. display: block;
  864. height: 265px;
  865. margin-bottom: 10px;
  866. }
  867. .options {
  868. float: right;
  869. font-size: 0.9em;
  870. width: 35%;
  871. border-top: 1px solid #ccc;
  872. }
  873. .options h3 {
  874. margin: 5px;
  875. }
  876. .options dl{
  877. margin: 0px;
  878. padding: 0px 10px 5px 20px;
  879. }
  880. .options dl dt {
  881. border-top: 1px dotted #ddd;
  882. padding-top: 10px;
  883. }
  884. .options dl dt.first {
  885. border: none;
  886. }
  887. .options dl dd {
  888. padding: 5px 0px 7px 0px;
  889. }
  890. .options-2 {
  891. clear: both;
  892. margin: 10px 0px;
  893. }
  894. .form-buttons {
  895. }
  896. .results {
  897. border: 1px solid #eee;
  898. padding: 5px;
  899. background-color: #fcfcfc;
  900. }
  901. ';
  902. return $this->v('endpoint_css', $default, $this->a);
  903. }
  904. function getHTMLDocBody() {
  905. return '
  906. <body>
  907. <h1>' . $this->getHTMLDocHeading() . '</h1>
  908. <div class="intro">
  909. <p>
  910. <a href="?">This interface</a> implements
  911. <a href="http://www.w3.org/TR/rdf-sparql-query/">SPARQL</a> and
  912. <a href="https://github.com/semsol/arc2/wiki/SPARQL%2B">SPARQL+</a> via <a href="http://www.w3.org/TR/rdf-sparql-protocol/#query-bindings-http">HTTP Bindings</a>.
  913. </p>
  914. <p>
  915. Enabled operations: ' . join(', ', $this->getFeatures()) . '
  916. </p>
  917. <p>
  918. Max. number of results : ' . $this->v('endpoint_max_limit', '<em>unrestricted</em>', $this->a) . '
  919. </p>
  920. </div>
  921. ' . $this->getHTMLDocForm() .'
  922. ' . ($this->p('show_inline') ? $this->query_result : '') . '
  923. </body>
  924. ';
  925. }
  926. function getHTMLDocForm() {
  927. $q = $this->p('query') ? htmlspecialchars($this->p('query')) : "SELECT * WHERE {\n GRAPH ?g { ?s ?p ?o . }\n}\nLIMIT 10";
  928. return '
  929. <form id="sparql-form" action="?" enctype="application/x-www-form-urlencoded" method="' . ($_SERVER['REQUEST_METHOD'] == 'GET' ? 'get' : 'post' ) . '">
  930. <textarea id="query" name="query" rows="20" cols="80">' . $q . '</textarea>
  931. ' . $this->getHTMLDocOptions() . '
  932. <div class="form-buttons">
  933. <input type="submit" value="Send Query" />
  934. <input type="reset" value="Reset" />
  935. </div>
  936. </form>
  937. ';
  938. }
  939. function getHTMLDocOptions() {
  940. $sel = $this->p('output');
  941. $sel_code = ' selected="selected"';
  942. return '
  943. <div class="options">
  944. <h3>Options</h3>
  945. <dl>
  946. <dt class="first">Output format (if supported by query type):</dt>
  947. <dd>
  948. <select id="output" name="output">
  949. <option value="" ' . (!$sel ? $sel_code : '') . '>default</option>
  950. <option value="xml" ' . ($sel == 'xml' ? $sel_code : '') . '>XML</option>
  951. <option value="json" ' . ($sel == 'json' ? $sel_code : '') . '>JSON</option>
  952. <option value="plain" ' . ($sel == 'plain' ? $sel_code : '') . '>Plain</option>
  953. <option value="php_ser" ' . ($sel == 'php_ser' ? $sel_code : '') . '>Serialized PHP</option>
  954. <option value="turtle" ' . ($sel == 'turtle' ? $sel_code : '') . '>Turtle</option>
  955. <option value="rdfxml" ' . ($sel == 'rdfxml' ? $sel_code : '') . '>RDF/XML</option>
  956. <option value="infos" ' . ($sel == 'infos' ? $sel_code : '') . '>Query Structure</option>
  957. ' . ($this->allow_sql ? '<option value="sql" ' . ($sel == 'sql' ? $sel_code : '') . '>SQL</option>' : '') . '
  958. <option value="htmltab" ' . ($sel == 'htmltab' ? $sel_code : '') . '>HTML Table</option>
  959. <option value="tsv" ' . ($sel == 'tsv' ? $sel_code : '') . '>TSV</option>
  960. </select>
  961. </dd>
  962. <dt>jsonp/callback (for JSON results)</dt>
  963. <dd>
  964. <input type="text" id="jsonp" name="jsonp" value="' . htmlspecialchars($this->p('jsonp')) . '" />
  965. </dd>
  966. <dt>API key (if required)</dt>
  967. <dd>
  968. <input type="text" id="key" name="key" value="' . htmlspecialchars($this->p('key')) . '" />
  969. </dd>
  970. <dt>Show results inline: </dt>
  971. <dd>
  972. <input type="checkbox" name="show_inline" value="1" ' . ($this->p('show_inline') ? ' checked="checked"' : '') . ' />
  973. </dd>
  974. </dl>
  975. </div>
  976. <div class="options-2">
  977. Change HTTP method:
  978. <a href="javascript:;" onclick="javascript:document.getElementById(\'sparql-form\').method=\'get\'">GET</a>
  979. <a href="javascript:;" onclick="javascript:document.getElementById(\'sparql-form\').method=\'post\'">POST</a>
  980. </div>
  981. ';
  982. }
  983. /* */
  984. }