PageRenderTime 73ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/php4store/Endpoint.php

https://github.com/BorderCloud/4store-php
PHP | 673 lines | 278 code | 55 blank | 340 comment | 59 complexity | 0213c38a78a238f1354b8c514ba6b8f9 MD5 | raw file
  1. <?php
  2. /**
  3. * @version 0.4.1
  4. * @package Bourdercloud/PHP4store
  5. * @copyright (c) 2011 Bourdercloud.com
  6. * @author Karima Rafes <karima.rafes@bordercloud.com>
  7. * @license http://www.opensource.org/licenses/mit-license.php
  8. */
  9. //require_once(dirname(__FILE__) . '/../arc2/ARC2.php');
  10. /**
  11. * bordercloud/arc2 is a lib RDF and Sparql (channel bordercloud.github.com/pear )
  12. */
  13. require_once('arc2/ARC2.php');
  14. require_once("Curl.php");
  15. require_once("Net.php");
  16. /**
  17. * Sparql HTTP Client for 4store Endpoint around basic php function.
  18. *
  19. *
  20. * You can send a query to any endpoint sparql
  21. * and read the result in an array.
  22. *
  23. * Example : send a simple query to DBpedia
  24. * <code>
  25. * <?php
  26. * require_once('php4store/Endpoint.php');
  27. *
  28. * $endpoint ="http://dbpedia.org/";
  29. * $sp_readonly = new Endpoint($endpoint);
  30. * $q = "select * where {?x ?y ?z.} LIMIT 5";
  31. * $rows = $sp_readonly->query($q, 'rows');
  32. * $err = $sp_readonly->getErrors();
  33. * if ($err) {
  34. * print_r($err);
  35. * throw new Exception(print_r($err,true));
  36. * }
  37. * var_dump($rows);
  38. * ?>
  39. * </code>
  40. *
  41. *
  42. * With a query ASK, you can use the parameter 'raw'
  43. * in the function query and read directly the result true or false.
  44. *
  45. * Example : send a query ASK with the parameter raw
  46. * <code>
  47. * <?php
  48. * require_once('php4store/Endpoint.php');
  49. *
  50. * $q = "PREFIX a: <http://example.com/test/a/>
  51. * PREFIX b: <http://example.com/test/b/>
  52. * ask where { GRAPH <".$graph."> {a:A b:Name \"Test3\" .}} ";
  53. * $res = $sp_readonly->query($q, 'raw');
  54. * $err = $sp_readonly->getErrors();
  55. * if ($err) {
  56. * print_r($err);
  57. * throw new Exception(print_r($err,true));
  58. * }
  59. * var_dump($res);
  60. * ?>
  61. * </code>
  62. *
  63. *
  64. * You can insert data also with SPARQL and the function query.
  65. * If the graph doesn't exist, 4store will create the graph.
  66. *
  67. * Example : send a query Insert
  68. * <code>
  69. * <?php
  70. * require_once('php4store/Endpoint.php');
  71. *
  72. * $graph = "http://www.bordercloud.com";
  73. * $endpoint ="http://localhost:8080/sparql/";
  74. *
  75. * //put argument false to write
  76. * $readonly = false;
  77. * $sp_write = new Endpoint('http://localhost:8080/',$readonly);
  78. *
  79. * $q = "
  80. * PREFIX a: <http://example.com/test/a/>
  81. * PREFIX b: <http://example.com/test/b/>
  82. * INSERT DATA {
  83. * GRAPH <".$graph."> {
  84. * a:A b:Name \"Test1\" .
  85. * a:A b:Name \"Test2\" .
  86. * a:A b:Name \"Test3\" .
  87. * }}";
  88. * $res = $sp_write->query($q,'raw');
  89. * $err = $sp_write->getErrors();
  90. * if ($err) {
  91. * print_r($err);
  92. * throw new Exception(print_r($err,true));
  93. * }
  94. * var_dump($res);
  95. * ?>
  96. * </code>
  97. *
  98. *
  99. * You can delete data also with SPARQL and the function query.
  100. *
  101. * Example : send a query Delete
  102. * <code>
  103. * <?php
  104. * require_once('php4store/Endpoint.php');
  105. *
  106. * $graph = "http://www.bordercloud.com";
  107. * $endpoint ="http://localhost:8080/sparql/";
  108. *
  109. * $q = "
  110. * PREFIX a: <http://example.com/test/a/>
  111. * PREFIX b: <http://example.com/test/b/>
  112. * DELETE DATA {
  113. * GRAPH <".$graph."> {
  114. * a:A b:Name \"Test2\" .
  115. * }}";
  116. *
  117. * $res = $sp_write->query($q,'raw');
  118. * $err = $sp_write->getErrors();
  119. * if ($err) {
  120. * print_r($err);
  121. * throw new Exception(print_r($err,true));
  122. * }
  123. * var_dump($res);
  124. * ?>
  125. * </code>
  126. *
  127. *
  128. * You can create or replace a graph with the function set.
  129. *
  130. * Example :
  131. * <code>
  132. * <?php
  133. * require_once('php4store/Endpoint.php');
  134. *
  135. * $readonly = false;
  136. * $s = new Endpoint('http://localhost:8080/',$readonly);
  137. *
  138. * $r = $s->set('http://example/test', "
  139. * @ prefix foaf: <http://xmlns.com/foaf/0.1/> .
  140. * <http://github.com/bordercloud/4store-php> foaf:maker <http://www.bordercloud.com/wiki/user:Karima_Rafes> . ");
  141. *
  142. * ?>
  143. * </code>
  144. *
  145. *
  146. * You can add data (Turtle format) in a graph with the function add.
  147. *
  148. * Example :
  149. * <code>
  150. * <?php
  151. * require_once('php4store/Endpoint.php');
  152. *
  153. * $readonly = false;
  154. * $s = new Endpoint('http://localhost:8080/',$readonly);
  155. * $r = $s->add('http://example/test', "
  156. * @ prefix foaf: <http://xmlns.com/foaf/0.1/> .
  157. * <http://www.bordercloud.com/wiki/user:Karima_Rafes> foaf:workplaceHomepage <http://www.bordercloud.com> . ");
  158. * var_dump($r);
  159. *
  160. * ?>
  161. * </code>
  162. *
  163. *
  164. * You can delete a graph with the function delete.
  165. *
  166. * Example :
  167. * <code>
  168. * <?php
  169. * require_once('php4store/Endpoint.php');
  170. *
  171. * $readonly = false;
  172. * $s = new Endpoint('http://localhost:8080/',$readonly);
  173. *
  174. * $r = $s->delete('http://example/test');
  175. * var_dump($r);
  176. * ?>
  177. * </code>
  178. *
  179. * @example examples/1_set.php Example Set
  180. * @example examples/2_add.php Example Add
  181. * @example examples/3_delete.php Example Delete
  182. * @example examples/4_query.php Example Query
  183. * @example examples/5_queryDBpedia.php Example Query with DBpedia
  184. * @example examples/init4Store.php Example Start and Stop 4Store
  185. * @package Bourdercloud/PHP4store
  186. */
  187. class Endpoint {
  188. /**
  189. * URL of Endpoint to read
  190. * @access private
  191. * @var string
  192. */
  193. private $_endpoint;
  194. /**
  195. * in the constructor set debug to true in order to get usefull output
  196. * @access private
  197. * @var string
  198. */
  199. private $_debug;
  200. /**
  201. * in the constructor set the right to write or not in the store
  202. * @access private
  203. * @var string
  204. */
  205. private $_readOnly;
  206. /**
  207. * in the constructor set the proxy_host if necessary
  208. * @access private
  209. * @var string
  210. */
  211. private $_proxy_host;
  212. /**
  213. * in the constructor set the proxy_port if necessary
  214. * @access private
  215. * @var int
  216. */
  217. private $_proxy_port;
  218. /** For Arc2 **/
  219. private $_arc2_RemoteStore;
  220. private $_arc2_Reader;
  221. private $_config;
  222. /**
  223. * Constructor of Graph
  224. * @param string $endpoint : url of endpoint, example : http://lod.bordercloud.com/sparql
  225. * @param boolean $readOnly : true by default, if you allow the function query to write in the database
  226. * @param boolean $debug : false by default, set debug to true in order to get usefull output
  227. * @param string $proxy_host : null by default, IP of your proxy
  228. * @param string $proxy_port : null by default, port of your proxy
  229. * @access public
  230. */
  231. public function __construct($endpoint,
  232. $readOnly = true,
  233. $debug = false,
  234. $proxy_host = null,
  235. $proxy_port = null)
  236. {
  237. $this->_debug = $debug;
  238. $this->_endpoint = $endpoint;
  239. $this->_readOnly = $readOnly;
  240. $this->_proxy_host = $proxy_host;
  241. $this->_proxy_port = $proxy_port;
  242. if($this->_proxy_host != null && $this->_proxy_port != null){
  243. $this->_config = array(
  244. /* remote endpoint */
  245. 'remote_store_endpoint' => $this->_endpoint."sparql/",
  246. /* network */
  247. 'proxy_host' => $this->_proxy_host,
  248. 'proxy_port' => $this->_proxy_port,
  249. );
  250. }else{
  251. $this->_config = array(
  252. /* remote endpoint */
  253. 'remote_store_endpoint' => $this->_endpoint."sparql/",
  254. );
  255. }
  256. $this->_arc2_RemoteStore = ARC2::getRemoteStore($this->_config);
  257. }
  258. /**
  259. * Check if the server is up.
  260. * @return boolean true if the triplestore is up.
  261. * @access public
  262. */
  263. public function check() {
  264. return Net::ping($this->_endpoint) != -1;
  265. }
  266. /**
  267. * Create or replace the data in a graph.
  268. * @param string $graph : name of the graph
  269. * @param string $turtle : list of the triples
  270. * @return boolean : true if it did
  271. * @access public
  272. */
  273. public function set($graph, $turtle) {
  274. if($this->_readOnly){
  275. return $this->_arc2_RemoteStore->addError('No right to write in the triplestore.');
  276. }
  277. $client = $this->initCurl();
  278. $headers = array( 'Content-Type: application/x-turtle' );
  279. $sUri = $this->_endpoint. "data/" . $graph;
  280. $response = $client->send_put_data($sUri,$headers, $turtle);
  281. $code = $client->get_http_response_code();
  282. if($code == 201)
  283. {
  284. return true;
  285. }
  286. else
  287. {
  288. $datastr = print_r($turtle, true);
  289. $headerstr = print_r($headers, true);
  290. $this->errorLog("Set:\nHeader :".$headerstr."\n Data:".$datastr,$sUri,$code,$response);
  291. return false;
  292. }
  293. }
  294. /**
  295. * Add new data in a graph.
  296. * @param string $graph : name of the graph
  297. * @param string $turtle : list of the triples
  298. * @return boolean : true if it did
  299. * @access public
  300. */
  301. public function add($graph, $turtle) {
  302. if($this->_readOnly){
  303. return $this->_arc2_RemoteStore->addError('No right to write in the triplestore.');
  304. }
  305. $data = array( "graph" => $graph, "data" => $turtle , "mime-type" => 'application/x-turtle' );
  306. $sUri = $this->_endpoint. "data/";
  307. $client = $this->initCurl();
  308. $response = $client->send_post_data($sUri, $data);
  309. $code = $client->get_http_response_code();
  310. if($code == 200)
  311. {
  312. return true;
  313. }
  314. else
  315. {
  316. $datastr = print_r($data, true);
  317. $this->errorLog("Add:\n".$datastr,$sUri,$code,$response);
  318. return false;
  319. }
  320. }
  321. /**
  322. * Delete a graph with its data.
  323. * @param string $graph : name of the graph
  324. * @return boolean : true if it did
  325. * @access public
  326. */
  327. public function delete($graph) {
  328. if($this->_readOnly){
  329. return $this->_arc2_RemoteStore->addError('No right to write in the triplestore.');
  330. }
  331. $client = $this->initCurl();
  332. $sUri = $this->_endpoint. "data/". $graph ;
  333. $response = $client->send_delete($sUri);
  334. $code = $client->get_http_response_code();
  335. if($code == 200)
  336. {
  337. return true;
  338. }
  339. else
  340. {
  341. $this->errorLog("DELETE:<".$graph.">",$sUri,$code,$response);
  342. return false;
  343. }
  344. }
  345. /**
  346. * This function parse a SPARQL query, send the query and parse the SPARQL result in a array.
  347. * You can custom the result with the parameter $result_format :
  348. * <ul>
  349. * <li>rows to return array of results
  350. * <li>row to return array of first result
  351. * <li>raw to return boolean for request ask, insert and delete
  352. * </ul>
  353. * @param string $q : Query SPARQL
  354. * @param string $result_format : Optional, rows, row or raw
  355. * @return array|boolean in function of parameter $result_format
  356. * @access public
  357. */
  358. public function query($q, $result_format = '') {
  359. if($this->_debug){
  360. print date('Y-m-d\TH:i:s\Z', time()) . ' : ' . $q . '' . "\n\n";
  361. }
  362. $p = ARC2::getSPARQLPlusParser();
  363. $p->parse($q);
  364. $infos = $p->getQueryInfos();
  365. $t1 = ARC2::mtime();
  366. if (!$errs = $p->getErrors()) {
  367. $qt = $infos['query']['type'];
  368. $r = array('query_type' => $qt, 'result' => $this->runQuery($q, $qt, $infos));
  369. }
  370. else {
  371. $r = array('result' => '');
  372. if($this->_debug){
  373. print date('Y-m-d\TH:i:s\Z', time()) . ' : ERROR ' . $q . '' . "\n\n";
  374. print_r($errs);
  375. }
  376. return $this->_arc2_RemoteStore->addError($p->getErrors() );
  377. }
  378. $t2 = ARC2::mtime();
  379. $r['query_time'] = $t2 - $t1;
  380. /* query result */
  381. if ($result_format == 'raw') {
  382. return $r['result'];
  383. }
  384. if ($result_format == 'rows') {
  385. return $this->_arc2_RemoteStore->v('rows', array(), $r['result']);
  386. }
  387. if ($result_format == 'row') {
  388. if (!isset($r['result']['rows'])) return array();
  389. return $r['result']['rows'] ? $r['result']['rows'][0] : array();
  390. }
  391. return $r;
  392. }
  393. /**
  394. * Give the errors
  395. * @return array
  396. * @access public
  397. */
  398. public function getErrors() {
  399. return $this->_arc2_RemoteStore->getErrors();
  400. }
  401. /**
  402. * Count the number of triples in a graph or in the endpoint.
  403. * @param string $graph : put name of the graph or nothing to count all triples in the endpoint
  404. * @return number
  405. * @access public
  406. */
  407. public function count($graph= null ) {
  408. $r="";
  409. $count = 0;
  410. if($graph != null){
  411. //FIXME count(*) doesn't work
  412. $r = $this->queryRead("SELECT (count(?a) AS ?count) WHERE { GRAPH <".$graph."> {?a ?b ?c}}");
  413. }else{
  414. $r = $this->queryRead("SELECT (count(?a) AS ?count) WHERE {?a ?b ?c}");
  415. }
  416. if(preg_match_all('%<binding name="count"><literal[^>]*>([0-9]+)<%m',$r,$countResponse))
  417. $count = $countResponse[1][0];
  418. return $count;
  419. }
  420. /**
  421. * Send a request SPARQL of type select or ask to endpoint directly and output the response
  422. * of server. If you want parse the result of this function, it's better and simpler
  423. * to use the function query().
  424. *
  425. * if you want use another format, you can use directly the function queryReadJSON and queryReadTabSeparated
  426. * @param string $query : Query Sparql
  427. * @param string $typeOutput by default "application/sparql-results+xml",
  428. * @return string response of server or false if error (to do getErrors())
  429. * @access public
  430. */
  431. public function queryRead($query,$typeOutput=null ) {
  432. $client = $this->initCurl();
  433. $sUri = $this->_endpoint."sparql/";
  434. $data = array("query" => $query);
  435. if($typeOutput == null){
  436. $response = $client->send_post_data($sUri,$data);
  437. }else{
  438. $response = $client->fetch_url($sUri."?query=".$query."&output=".$typeOutput);
  439. }
  440. $code = $client->get_http_response_code();
  441. $this->debugLog($query,$sUri,$code,$response);
  442. if($code != 200)
  443. {
  444. $error = $this->errorLog($query,$data,$sUri,$code,$response);
  445. $this->_arc2_RemoteStore->addError($error);
  446. return false;
  447. }
  448. return $response;
  449. }
  450. /**
  451. * Send a request SPARQL of type select or ask to endpoint directly and output the response
  452. * of server in the format JSON
  453. * @param string $query : Query Sparql
  454. * @return string response of server in the format JSON
  455. * @access public
  456. */
  457. public function queryReadJSON($query ){
  458. return $this->queryRead($query,"application/sparql-results+json" );
  459. }
  460. /**
  461. * Send a request SPARQL of type select or ask to endpoint directly and output the response
  462. * of server in the format TabSeparated
  463. * @param string $query : Query Sparql
  464. * @return string response of server in the format TabSeparated
  465. * @access public
  466. */
  467. public function queryReadTabSeparated ($query ){
  468. return $this->queryRead($query,"text" );
  469. }
  470. /**
  471. * Send a request SPARQL of type insert data or delete data to endpoint directly.
  472. * If you want check the query before to send, it's better to use the function query()
  473. * in the class StorePlus.
  474. * <ul>
  475. * <li>Example insert : PREFIX ex: <http://example.com/> INSERT DATA { GRAPH <http://mygraph> { ex:a ex:p 12 .}}
  476. * <li>Example delete : PREFIX ex: <http://example.com/> DELETE DATA { GRAPH <http://mygraph> { ex:a ex:p 12 .}}
  477. * </ul>
  478. * @param string $query : Query Sparql of type insert data or delete data only
  479. * @return boolean true if it did or false if error (to do getErrors())
  480. * @access public
  481. */
  482. public function queryUpdate($query) {
  483. $sUri = $this->_endpoint . "update/";
  484. $data =array("update" => $query) ;
  485. $this->debugLog($query,$sUri);
  486. $client = $this->initCurl();
  487. $response = $client->send_post_data($sUri, $data);
  488. $code = $client->get_http_response_code();
  489. $this->debugLog($query,$sUri,$code,$response);
  490. if($code == 200 )
  491. {
  492. return true;
  493. }
  494. else
  495. {
  496. $error = $this->errorLog($query,$data,$sUri,$code,$response);
  497. $this->_arc2_RemoteStore->addError($error);
  498. return false;
  499. }
  500. }
  501. /************************************************************************/
  502. //PRIVATE Function
  503. /**
  504. * Execute the query
  505. * @access private
  506. */
  507. private function runQuery($q, $qt = '', $infos = '') {
  508. /* ep */
  509. $ep = $this->_arc2_RemoteStore->v('remote_store_endpoint', 0, $this->_arc2_RemoteStore->a);
  510. if (!$ep) return $this->_arc2_RemoteStore->addError('No Endpoint defined.');
  511. /* prefixes */
  512. $q = $this->_arc2_RemoteStore->completeQuery($q);
  513. /* custom handling */
  514. $mthd = 'run' . $this->_arc2_RemoteStore->camelCase($qt) . 'Query';
  515. if (method_exists($this, $mthd)) {
  516. return $this->_arc2_RemoteStore->$mthd($q, $infos);
  517. }
  518. if(in_array($qt, array('insert', 'delete'))){
  519. if($this->_readOnly){
  520. return $this->_arc2_RemoteStore->addError('No right to write in the triplestore.');
  521. }else{
  522. $r = $this->queryUpdate($q);
  523. if(! $r){
  524. $errmsg = "Error unknown.";
  525. if(Net::ping($ep) == -1)
  526. $errmsg = "Could not connect to ".$ep;
  527. return $this->_arc2_RemoteStore->addError($errmsg );
  528. }
  529. }
  530. }else{
  531. $resp = $this->queryRead($q );
  532. if($resp == ""){
  533. $errmsg = "Error unknown.";
  534. if(Net::ping($ep) == -1)
  535. $errmsg = "Could not connect to ".$ep;
  536. return $this->_arc2_RemoteStore->addError($errmsg );
  537. }
  538. if(preg_match_all('%<!--(.*error.*)-->%m',$resp,$errorResponse)){
  539. $message4s = $errorResponse[1][0];
  540. return $this->_arc2_RemoteStore->addError("5Store message : ".$message4s ."\n query :\n".$q );
  541. }
  542. $parser = @ARC2::getSPARQLXMLResultParser() ;
  543. $parser->parse('', $resp);
  544. $err = $parser->getErrors();
  545. if($err)
  546. return $this->_arc2_RemoteStore->addError($err);
  547. if ($qt == 'ask') {
  548. $bid = $parser->getBooleanInsertedDeleted();
  549. $r = $bid['boolean'];
  550. }
  551. /* select */
  552. elseif (($qt == 'select') && !method_exists($parser, 'getRows')) {
  553. $r = $resp;
  554. }
  555. elseif ($qt == 'select') {
  556. $r = array('rows' => $parser->getRows(), 'variables' => $parser->getVariables());
  557. }
  558. /* any other */
  559. else {
  560. $r = $parser->getSimpleIndex(0);
  561. }
  562. unset($parser);
  563. }
  564. return $r;
  565. }
  566. /**
  567. * write error for human
  568. * @param string $query
  569. * @param string $endPoint
  570. * @param number $httpcode
  571. * @param string $response
  572. * @access private
  573. */
  574. private function errorLog($query,$data,$endPoint,$httpcode=0,$response=''){
  575. $error = "Error query : " .$query."\n" .
  576. "Error endpoint: " .$endPoint."\n" .
  577. "Error http_response_code: " .$httpcode."\n" .
  578. "Error message: " .$response."\n";
  579. if($this->_debug)
  580. {
  581. echo '=========================>>>>>>'.$error ;
  582. }else{
  583. error_log($error);
  584. }
  585. }
  586. /**
  587. * Print infos
  588. * @param unknown_type $query
  589. * @param unknown_type $endPoint
  590. * @param unknown_type $httpcode
  591. * @param unknown_type $response
  592. * @access private
  593. */
  594. private function debugLog($query,$endPoint,$httpcode='',$response=''){
  595. if($this->_debug)
  596. {
  597. $error = "\n#######################\n".
  598. "query : " .$query."\n" .
  599. "endpoint : " .$endPoint."\n" .
  600. "http_response_code : " .$httpcode."\n" .
  601. "message : " .$response.
  602. "\n#######################\n";
  603. echo $error ;
  604. }
  605. }
  606. /**
  607. * Init an object Curl in function of proxy.
  608. * @return an object of type Curl
  609. * @access private
  610. */
  611. private function initCurl(){
  612. $objCurl = new Curl();
  613. if($this->_proxy_host != null && $this->_proxy_port != null){
  614. $objCurl->set_proxy($this->_proxy_host.":".$this->_proxy_port);
  615. }
  616. return $objCurl;
  617. }
  618. }