PageRenderTime 56ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/rdfapi-php/api/sparql/SparqlEngine.php

https://github.com/komagata/plnet
PHP | 1356 lines | 1112 code | 81 blank | 163 comment | 115 complexity | 44fb783f04934fffaad10525657d49d4 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. // ----------------------------------------------------------------------------------
  3. // Class: SparqlEngine
  4. // ----------------------------------------------------------------------------------
  5. /**
  6. * This engine executes SPARQL queries against an RDF Datatset.
  7. *
  8. * @version $Id$
  9. * @author Tobias Gauß <tobias.gauss@web.de>
  10. *
  11. * @package sparql
  12. */
  13. Class SparqlEngine extends Object{
  14. /**
  15. * @var Query The query object.
  16. */
  17. private $query;
  18. /**
  19. * @var Dataset The RDF Dataset.
  20. */
  21. private $dataset;
  22. /**
  23. * The query engines main method.
  24. *
  25. * @param Dataset $dataset the RDF Dataset
  26. * @param Query $query the parsed SPARQL query
  27. * @param String $resultform the result form. If set to 'xml' the result will be
  28. * SPARQL Query Results XML Format as described in http://www.w3.org/TR/rdf-sparql-XMLres/ .
  29. * @return Array/String Type of the result depends on $resultform.
  30. */
  31. public function queryModel($dataset,$query,$resultform = false){
  32. $this->query = $query;
  33. $this->dataset = $dataset;
  34. if($this->query->isEmpty){
  35. $vartable[0]['patternResult'] = null;
  36. return $this->returnResult($vartable,$resultform);
  37. }
  38. $graphlist = $this->preselectGraphs();
  39. /// match graph patterns against the RDF Dataset
  40. $patternlist = $this->matchPatterns($graphlist);
  41. // filter results- apply inner filters
  42. $patternlist = $this->filterPatterns($patternlist,false);
  43. // join pattern results
  44. $vartable = $this->joinResults($patternlist);
  45. // filter results- apply outer filters
  46. $vartable = $this->filterPatterns($vartable,true);
  47. return $this->returnResult($vartable,$resultform);
  48. }
  49. /**
  50. * Matches all graph Patterns against the dataset and generates an array which
  51. * contains the result sets for every given GraphPattern.
  52. *
  53. * @param Array $graphlist the graphlist which contains the names of the named
  54. * graphs which has to be queried.
  55. * @return Array
  56. */
  57. protected function matchPatterns($graphlist){
  58. $patternlist = array();
  59. // get the result part from the query
  60. $resultPart = $this->query->getResultPart();
  61. // for each GrapPattern in the result part
  62. if($resultPart)
  63. foreach($resultPart as $graphPattern){
  64. $this->matchPattern($patternlist, $graphlist, $graphPattern);
  65. }
  66. return $patternlist;
  67. }
  68. /**
  69. * Finds tuples that match one graph pattern.
  70. *
  71. * @param Array $patternlist list that contains the graphPatterns
  72. * @param array $graphlist the graphlist
  73. * @param GraphPattern $graphPattern the pattern which has to be matched
  74. * @return void
  75. */
  76. protected function matchPattern(&$patternlist, $graphlist, &$graphPattern) {
  77. // generate an empty result set
  78. $finalRes = null;
  79. // if the GraphPattern has triple patterns
  80. if($graphPattern->getTriplePattern()>0){
  81. // check if the pattern has a GRAPH clause and if this Iri is in $graphlist
  82. $newGraphList = $this->_checkGraphs($graphPattern,$graphlist);
  83. if($newGraphList){
  84. $qt = $graphPattern->getTriplePattern();
  85. $resultSet = $this->findTuplesMatchingOnePattern($qt[0], $newGraphList);
  86. for ($i=1; $i<count($qt); $i++) {
  87. $rs = $this->findTuplesMatchingOnePattern($qt[$i], $newGraphList);
  88. $resultSet = $this->joinTuples($resultSet, $rs);
  89. if(!$resultSet)
  90. break;
  91. }
  92. if($finalRes != null){
  93. $finalRes = $this->joinTuples($finalRes,$resultSet);
  94. }else{
  95. $finalRes = $resultSet;
  96. }
  97. }
  98. }
  99. // dependencies between pattern results
  100. $patternlist[$graphPattern->getId()]['hasOptional'] = 0;
  101. $patternlist[$graphPattern->getId()]['hasUnion'] = 0;
  102. $patternlist[$graphPattern->getId()]['patternResult'] = $finalRes;
  103. $op = $graphPattern->getOptional();
  104. $un = $graphPattern->getUnion();
  105. $patternlist[$graphPattern->getId()]['optionalTo'] = $op;
  106. if(is_int($op))
  107. $patternlist[$op]['hasOptional']++;
  108. $patternlist[$graphPattern->getId()]['unionWith'] = $un;
  109. if(is_int($un))
  110. $patternlist[$un]['hasUnion']++;
  111. $constraint = $graphPattern->getConstraint();
  112. if($constraint != null){
  113. foreach($constraint as $constr){
  114. if($constr->isOuterFilter()){
  115. $patternlist[$graphPattern->getId()]['outerFilter'][] = $constr;
  116. $patternlist[$graphPattern->getId()]['innerFilter'][] = null;
  117. }else{
  118. $patternlist[$graphPattern->getId()]['innerFilter'][] = $constr;
  119. $patternlist[$graphPattern->getId()]['outerFilter'][] = null;
  120. }
  121. }
  122. }else{
  123. $patternlist[$graphPattern->getId()]['innerFilter'] = null;
  124. $patternlist[$graphPattern->getId()]['outerFilter'] = null;
  125. }
  126. }
  127. /**
  128. * Finds Tuples matching one TriplePattern.
  129. *
  130. * @param TriplePattern $pattern
  131. * @param Array $graphlist
  132. * @return Array
  133. */
  134. protected function findTuplesMatchingOnePattern($pattern, $graphlist){
  135. $var = null;
  136. $sub = $pattern->getSubject();
  137. $pred = $pattern->getPredicate();
  138. $obj = $pattern->getObject();
  139. if(is_string($sub)||$sub instanceof BlankNode){
  140. if(is_string($sub))
  141. $var['sub'] = $sub;
  142. $sub = null;
  143. }
  144. if(is_string($pred)||$pred instanceof BlankNode ){
  145. if(is_string($pred))
  146. $var['pred'] = $pred;
  147. $pred = null;
  148. }
  149. if(is_string($obj)||$obj instanceof BlankNode){
  150. if(is_string($obj))
  151. $var['obj'] = $obj;
  152. $obj = null;
  153. }
  154. $intBindings = $this->_buildIntBindings($var);
  155. $k = 0;
  156. $key = 0;
  157. // search in named graphs
  158. if($graphlist['var'][0] != null||$graphlist['list'][0] != null){
  159. foreach($graphlist['list'] as $key => $graphnode){
  160. // query the dataset
  161. $it = $this->dataset->findInNamedGraphs($graphnode,$sub,$pred,$obj,false);
  162. if($it->valid()){
  163. // add statements to the result list
  164. while($it->valid()){
  165. if($graphnode == null){
  166. $element = $it->current()->getStatement();
  167. $grname = $it->current()->getGraphname();
  168. }else{
  169. if($it->current() instanceof Quad)
  170. $element = $it->current()->getStatement();
  171. else
  172. $element = $it->current();
  173. $grname = $graphnode;
  174. }
  175. if($this->checkIntBindings($element,$intBindings)){
  176. $resmodel['trip'][$k] = $element;
  177. $resmodel['graph'][$k] = $grname;
  178. // $resmodel['graphvar'][$k] = $graphlist['var'][$key];
  179. $resmodel['graphvar'][$k] = $graphlist['var'][0];
  180. $k++;
  181. }
  182. $it->next();
  183. }
  184. }
  185. }
  186. }
  187. // search in the default graph
  188. if($graphlist['list'][0] == null && $graphlist['var'][0] == null){
  189. $gr = $this->dataset->getDefaultGraph();
  190. $res = $gr->find($sub,$pred,$obj);
  191. foreach($res->triples as $innerkey => $element){
  192. if($this->checkIntBindings($element,$intBindings)){
  193. $resmodel['trip'][$k] = $element;
  194. $resmodel['graph'][$k] = null;
  195. $resmodel['graphvar'][$k] = $graphlist['var'][$key];
  196. $k++;
  197. }
  198. }
  199. }
  200. if($k == 0)
  201. return false;
  202. return $this->_buildResultSet($pattern,$resmodel);
  203. }
  204. /**
  205. * Checks it there are internal bindings between variables.
  206. *
  207. * @param Triple $trip
  208. * @param Array $intBindings
  209. * @return boolean
  210. */
  211. protected function checkIntBindings($trip, $intBindings){
  212. switch($intBindings){
  213. case -1:
  214. return true;
  215. break;
  216. case 0:
  217. if($trip->subj != $trip->pred)
  218. return false;
  219. break;
  220. case 1:
  221. if(is_a($trip->obj,'Literal'))
  222. return false;
  223. if($trip->subj != $trip->obj)
  224. return false;
  225. break;
  226. case 2:
  227. if(is_a($trip->obj,'Literal'))
  228. return false;
  229. if($trip->pred != $trip->obj)
  230. return false;
  231. break;
  232. case 3:
  233. if(is_a($trip->obj,'Literal'))
  234. return false;
  235. if($trip->pred != $trip->obj || $trip->pred != $trip->subj )
  236. return false;
  237. break;
  238. }
  239. return true;
  240. }
  241. /**
  242. * Perform an SQL-like inner join on two resultSets.
  243. *
  244. * @param Array &$finalRes
  245. * @param Array &$res
  246. * @return Array
  247. */
  248. protected function joinTuples(&$finalRes, &$res) {
  249. if (!$finalRes || !$res)
  250. return array();
  251. // find joint variables and new variables to be added to $finalRes
  252. $jointVars = array();
  253. $newVars = array();
  254. $k = key($res);
  255. foreach ($res[$k] as $varname => $node) {
  256. if (array_key_exists($varname, $finalRes[0]))
  257. $jointVars[] = $varname;
  258. else
  259. $newVars[] = $varname;
  260. }
  261. // eliminate rows of $finalRes in which the values of $jointVars do not have
  262. // a corresponding row in $res.
  263. foreach ($finalRes as $n => $fRes) {
  264. foreach ($res as $i => $r) {
  265. $ok = TRUE;
  266. foreach ($jointVars as $j_varname)
  267. if ($r[$j_varname] != $fRes[$j_varname]) {
  268. $ok = FALSE;
  269. break;
  270. }
  271. if ($ok)
  272. break;
  273. }
  274. if (!$ok)
  275. unset($finalRes[$n]);
  276. }
  277. // join $res and $finalRes
  278. $joinedRes = array();
  279. foreach ($res as $r) {
  280. foreach ($finalRes as $n => $fRes) {
  281. $ok = TRUE;
  282. foreach ($jointVars as $j_varname)
  283. if ($r[$j_varname] != $fRes[$j_varname]) {
  284. $ok = FALSE;
  285. break;
  286. }
  287. if ($ok) {
  288. $joinedRow = $finalRes[$n];
  289. foreach($newVars as $n_varname)
  290. $joinedRow[$n_varname] = $r[$n_varname];
  291. $joinedRes[] = $joinedRow;
  292. }
  293. }
  294. }
  295. return $joinedRes;
  296. }
  297. /**
  298. * Joins OPTIONAL pattern results.
  299. *
  300. * @param Array &$finalRes
  301. * @param Array &$res
  302. * @return Array the joined Array
  303. */
  304. protected function joinOptionalTuples(&$finalRes, &$res) {
  305. if(!$finalRes && !$res)
  306. return array();
  307. if(!$finalRes)
  308. return $res;
  309. if(!$res)
  310. return $finalRes;
  311. // find joint variables and new variables to be added to $finalRes
  312. $jointVars = array();
  313. $newVars = array();
  314. $result = array();
  315. $k = key($res);
  316. foreach ($res[$k] as $varname => $node) {
  317. if (array_key_exists($varname, $finalRes[0])){
  318. $jointVars[] = $varname;
  319. }else{
  320. $newVars[] = $varname;
  321. }
  322. }
  323. $joined = array();
  324. foreach($finalRes as $i =>$fRes){
  325. foreach($res as $n =>$r){
  326. $join = false;
  327. foreach($jointVars as $j_varname){
  328. if($r[$j_varname]==$fRes[$j_varname]){
  329. $join = true;
  330. break;
  331. }
  332. }
  333. if($join){
  334. $result[$i] = $fRes;
  335. foreach($newVars as $n_varname)
  336. $result[$i][$n_varname] = $r[$n_varname];
  337. $joined[]=$n;
  338. }
  339. }
  340. }
  341. $count = count($result);
  342. foreach($res as $k =>$val){
  343. if(!in_array($k,$joined)){
  344. $result[$count] = $finalRes[0];
  345. foreach($result[$count] as $varname => $varVal){
  346. $result[$count][$varname]='';
  347. }
  348. foreach($val as $varname2 => $varVal2){
  349. $result[$count][$varname2]=$varVal2;
  350. }
  351. $count++;
  352. }
  353. }
  354. return $result;
  355. }
  356. /**
  357. * Looks in from and from named part of the query and
  358. * adds the graphs to the graphlist.
  359. *
  360. * @return Array
  361. */
  362. protected function preselectGraphs(){
  363. $fromNamed = $this->query->getFromNamedPart();
  364. if($fromNamed == null)
  365. $fromNamed[] = null;
  366. return $fromNamed;
  367. }
  368. /**
  369. * Evaluates the GRPAH clause if there is one. Checks if
  370. * the GRAPH clause contains an IRI, variable or nothing.
  371. * Returns an array which contains the graphs that has to be matched.
  372. *
  373. * @param GraphPattern $pattern
  374. * @param Array $graphlist
  375. * @return Array
  376. */
  377. protected function _checkGraphs(&$pattern,$graphlist){
  378. $gr = $pattern->getGraphname();
  379. if($gr instanceof Resource ){
  380. if($graphlist[0]==null || in_array($gr,$graphlist)){
  381. $newGraphList['list'][] = $gr;
  382. $newGraphList['var'][] = null;
  383. }else{
  384. return false;
  385. }
  386. }elseif (is_string($gr)){
  387. $newGraphList['list'] = $graphlist;
  388. $newGraphList['var'][] = $gr;
  389. }else{
  390. $newGraphList['list'] = $graphlist;
  391. $newGraphList['var'][] = null;
  392. }
  393. return $newGraphList;
  394. }
  395. /**
  396. * Marks triples with internal bindings.
  397. * int bindings -1 :none 0:sub=pred 1:sub=obj 2:pred=obj 3:sub=pred=obj.
  398. *
  399. * @param Array $var
  400. * @return Array
  401. */
  402. protected function _buildIntBindings($var){
  403. $intBindings = -1;
  404. if(!$var)
  405. return $intBindings;
  406. if(isset($var['sub'])){
  407. if(isset($var['pred']))
  408. if($var['sub'] == $var['pred'])
  409. $intBindings = 0;
  410. if(isset($var['obj']))
  411. if($var['sub'] == $var['obj']){
  412. if( $intBindings == 0){
  413. $intBindings = 3;
  414. }else{
  415. $intBindings = 1;
  416. }
  417. }
  418. }
  419. if(isset($var['pred'])){
  420. if(isset($var['obj']))
  421. if($var['pred']==$var['obj']&&$intBindings!=3)
  422. $intBindings = 2;
  423. }
  424. return $intBindings;
  425. }
  426. /**
  427. * Builds the resultset.
  428. *
  429. * @param GraphPattern $pattern
  430. * @param Array $resmodel
  431. * @return Array
  432. */
  433. protected function _buildResultSet($pattern,$resmodel){
  434. // determine variables and their corresponding values
  435. $result = null;
  436. if(is_string($pattern->getSubject())){
  437. $n = 0;
  438. foreach($resmodel['trip'] as $key => $triple){
  439. if(isset($resmodel['graphvar'][$key]))
  440. $result[$n][$resmodel['graphvar'][$key]] = $resmodel['graph'][$key];
  441. $result[$n++][$pattern->getSubject()] = $triple->subj;
  442. }
  443. }
  444. if(is_string($pattern->getPredicate())){
  445. $n = 0;
  446. foreach($resmodel['trip'] as $key => $triple){
  447. if(isset($resmodel['graphvar'][$key]))
  448. $result[$n][$resmodel['graphvar'][$key]] = $resmodel['graph'][$key];
  449. $result[$n++][$pattern->getPredicate()] = $triple->pred;
  450. }
  451. }
  452. if(is_string($pattern->getObject())){
  453. $n = 0;
  454. foreach($resmodel['trip'] as $key => $triple){
  455. if(isset($resmodel['graphvar'][$key]))
  456. $result[$n][$resmodel['graphvar'][$key]] = $resmodel['graph'][$key];
  457. $result[$n++][$pattern->getObject()] = $triple->obj;
  458. }
  459. }
  460. return $result;
  461. }
  462. /**
  463. * Selects the result variables and builds a result table.
  464. *
  465. * @param Array $table the result table
  466. * @param Array $vars the result variables
  467. * @return Array
  468. */
  469. protected function selectVars($table,$vars){
  470. if($vars[0]=='*')
  471. $vars = $this->query->getAllVars();
  472. $resTable = array();
  473. $hits = 0;
  474. foreach($table as $val){
  475. foreach($vars as $var){
  476. if(isset($val[$var])){
  477. $resTable[$hits][$var]=$val[$var];
  478. }else{
  479. $resTable[$hits][$var]="";
  480. }
  481. }
  482. $hits++;
  483. }
  484. return $resTable;
  485. }
  486. /**
  487. * Joins the results of the different Graphpatterns.
  488. *
  489. * @param Array $patternlist
  490. * @return Array
  491. */
  492. protected function joinResults($patternlist){
  493. $joined[0]['patternResult'] = null;
  494. $joined[0]['outerFilter'] = null;
  495. while(count($patternlist)>0){
  496. foreach($patternlist as $key => $pattern){
  497. if($pattern['hasOptional'] == 0 && $pattern['hasUnion'] == 0){
  498. if(is_int($pattern['optionalTo'])){
  499. $patternlist[$pattern['optionalTo']]['hasOptional']--;
  500. $patternlist[$pattern['optionalTo']]['patternResult'] = $this->joinOptionalTuples($pattern['patternResult'],$patternlist[$pattern['optionalTo']]['patternResult']);
  501. unset($patternlist[$key]);
  502. break;
  503. }
  504. else if(is_int($pattern['unionWith'])){
  505. $patternlist[$pattern['unionWith']]['hasUnion']--;
  506. foreach($pattern['patternResult'] as $value)
  507. array_push($patternlist[$pattern['unionWith']]['patternResult'],$value);
  508. unset($patternlist[$key]);
  509. break;
  510. }else{
  511. if($joined[0]['patternResult'] == null){
  512. $joined[0]['patternResult'] = $pattern['patternResult'];
  513. if($joined[0]['outerFilter'] == null )
  514. $joined[0]['outerFilter'] = $pattern['outerFilter'];
  515. unset($patternlist[$key]);
  516. break;
  517. }
  518. // if($pattern['patternResult'] !=null ){
  519. $joined[0]['patternResult'] = $this->joinTuples($joined[0]['patternResult'],$pattern['patternResult']);
  520. $joined[0]['outerFilter'] = $pattern['outerFilter'];
  521. unset($patternlist[$key]);
  522. break;
  523. // }
  524. }
  525. }
  526. }
  527. }
  528. return $joined;
  529. }
  530. /**
  531. * Filters the pattern results.
  532. *
  533. * @param Array $patternlist list containing the results of the GraphPatterns
  534. * @param boolean $outer TRUE if its an outer filter FALSE if not
  535. * @return Array the filtered patternlist
  536. */
  537. protected function filterPatterns($patternlist,$outer){
  538. if($outer)
  539. $filter = 'outerFilter';
  540. else
  541. $filter = 'innerFilter';
  542. foreach($patternlist as $patkey => $pattern){
  543. // get constraints
  544. $constraint = $pattern[$filter];
  545. if(count($constraint)>0){
  546. foreach($constraint as $constr){
  547. if($constr != null){
  548. // extract Vars and function calls
  549. $evalString = $constr->getExpression();
  550. preg_match_all("/\?.[^\s\)\,]*/",$evalString,$vars);
  551. preg_match_all("/bound\((.[^\)]*)\)/i",$evalString,$boundcalls);
  552. preg_match_all("/isuri\((.[^\)]*)\)/i",$evalString,$isUricalls);
  553. preg_match_all("/isblank\((.[^\)]*)\)/i",$evalString,$isBlankcalls);
  554. preg_match_all("/isLiteral\((.[^\)]*)\)/i",$evalString,$isLiteralcalls);
  555. preg_match_all("/lang\((.[^\)]*)\)/i",$evalString,$langcalls);
  556. preg_match_all("/datatype\((.[^\)]*)\)/i",$evalString,$datatypecalls);
  557. preg_match_all("/str\((.[^\)]*)\)/i",$evalString,$stringcalls);
  558. // is Bound
  559. if(count($boundcalls[1])>0)
  560. $function['bound'] = $boundcalls[1];
  561. else
  562. $function['bound'] = false;
  563. // is URI
  564. if(count($isUricalls[1])>0)
  565. $function['isUri'] = $isUricalls[1];
  566. else
  567. $function['isUri'] = false;
  568. // is Blank
  569. if(count($isBlankcalls[1])>0)
  570. $function['isBlank'] = $isBlankcalls[1];
  571. else
  572. $function['isBlank'] = false;
  573. // is Literal
  574. if(count($isLiteralcalls[1])>0)
  575. $function['isLiteral'] = $isLiteralcalls[1];
  576. else
  577. $function['isLiteral'] = false;
  578. // lang
  579. if(count($langcalls[1])>0)
  580. $function['lang'] = $langcalls[1];
  581. else
  582. $function['lang'] = false;
  583. // datatype
  584. if(count($datatypecalls[1])>0)
  585. $function['datatype'] = $datatypecalls[1];
  586. else
  587. $function['datatype'] = false;
  588. // string
  589. if(count($stringcalls[1])>0)
  590. $function['string'] = $stringcalls[1];
  591. else
  592. $function['string'] = false;
  593. foreach($pattern['patternResult'] as $key => $res){
  594. $result = false;
  595. $evalString = $this->fillConstraintString($vars,$res,$constr,$function);
  596. $evalString = '$result =('.$evalString.');';
  597. // evaluate Constraint
  598. @eval($evalString);
  599. if(!$result)
  600. unset($patternlist[$patkey]['patternResult'][$key]);
  601. }
  602. }
  603. }
  604. }
  605. }
  606. return $patternlist;
  607. }
  608. /**
  609. * Builds an evaluation string to determine wether the result passes
  610. * the filter or not. This string is evaluatet by the php buildin eval() function
  611. *
  612. * @param Array $vars a list which contains the used variables
  613. * @param Array $res the result part which have to be evaluated
  614. * @param Constraint $constraint the Constrain object
  615. * @param Array $function an Array which contains the used functions
  616. * @return String
  617. */
  618. protected function fillConstraintString($vars,$res,$constraint,$function){
  619. $boundExpr = false;
  620. $evalString = $constraint->getExpression();
  621. // extract Literals
  622. $pattern1 = "/\".[^\"]*\"[^\^\@]/";
  623. $pattern2 = "/\'.[^\']*\'[^\^\@]/";
  624. preg_match_all($pattern1,$evalString,$hits1);
  625. preg_match_all($pattern2,$evalString,$hits2);
  626. foreach($hits1[0] as $k => $val){
  627. $evalString = preg_replace('/\".[^\"]*\"[^\^]/','_REPLACED1_'.$k++,$evalString,1);
  628. }
  629. foreach($hits2[0] as $k => $val){
  630. $evalString = preg_replace('/\".[^\"]*\"[^\^]/','_REPLACED2_'.$k++,$evalString,1);
  631. }
  632. // replace namespaces
  633. $prefs = $this->query->getPrefixes();
  634. foreach($prefs as $key => $val){
  635. if($key == '')
  636. $key = ' ';
  637. $evalString = preg_replace("/^(".$key."\:)(.[^\s]*)|([\s\(]?[^\^])(".$key."\:)(.[^\s\)]*)([\s\)]?)/","$3'<".$val."$2$5>'$6",$evalString);
  638. $evalString = preg_replace("/(\^)(".$key."\:)(.[^\s]*)/","$1<".$val."$3>",$evalString);
  639. }
  640. $xsd = "http\:\/\/www.w3.org\/2001\/XMLSchema\#";
  641. // evaluate bound calls
  642. if($function['bound']){
  643. $boundExpr = true;
  644. foreach($function['bound'] as $var){
  645. if(isset($res[$var]) && $res[$var]!="")
  646. $replacement = 'true';
  647. else
  648. $replacement = 'false';
  649. $evalString = preg_replace("/bound\(\\".$var."\)/i",$replacement,$evalString);
  650. }
  651. }
  652. // evaluate isBlank calls
  653. if($function['isBlank']){
  654. foreach($function['isBlank'] as $var){
  655. if(isset($res[$var]) && $res[$var]!="" && $res[$var] instanceof BlankNode )
  656. $replacement = 'true';
  657. else
  658. $replacement = 'false';
  659. $evalString = preg_replace("/isBlank\(\\".$var."\)/i",$replacement,$evalString);
  660. }
  661. }
  662. // evaluate isLiteral calls
  663. if($function['isLiteral']){
  664. foreach($function['isLiteral'] as $var){
  665. if(isset($res[$var]) && $res[$var]!="" && $res[$var] instanceof Literal )
  666. $replacement = 'true';
  667. else
  668. $replacement = 'false';
  669. $evalString = preg_replace("/isLiteral\(\\".$var."\)/i",$replacement,$evalString);
  670. }
  671. }
  672. // evaluate isUri calls
  673. if($function['isUri']){
  674. foreach($function['isUri'] as $var){
  675. if(isset($res[$var]) && $res[$var]!="" && $res[$var] instanceof Resource && $res[$var]->getUri() && !$res[$var] instanceof BlankNode )
  676. $replacement = 'true';
  677. else
  678. $replacement = 'false';
  679. $evalString = preg_replace("/isUri\(\\".$var."\)/i",$replacement,$evalString);
  680. }
  681. }
  682. // evaluate lang calls
  683. if($function['lang']){
  684. foreach($function['lang'] as $var){
  685. if(isset($res[$var]) && $res[$var]!="" && $res[$var] instanceof Literal && $res[$var]->getLanguage() )
  686. $replacement = '"'.$res[$var]->getLanguage().'"';
  687. else
  688. $replacement = 'null';
  689. $evalString = preg_replace("/lang\(\\".$var."\)/i",$replacement,$evalString);
  690. }
  691. }
  692. // evaluate datatype calls
  693. if($function['datatype']){
  694. foreach($function['datatype'] as $var){
  695. if(isset($res[$var]) && $res[$var]!="" && $res[$var] instanceof Literal && $res[$var]->getDatatype() )
  696. $replacement = '\'<'.$res[$var]->getDatatype().'>\'';
  697. else
  698. $replacement = 'false';
  699. $evalString = preg_replace("/datatype\(\\".$var."\)/i",$replacement,$evalString);
  700. }
  701. }
  702. // evaluate string calls
  703. if($function['string']){
  704. foreach($function['string'] as $var){
  705. if($var{0}=='?' || $var{0}=='$'){
  706. if(isset($res[$var]) && $res[$var]!=""){
  707. $replacement = "'str_".$res[$var]->getLabel()."'";
  708. if($res[$var] instanceof BlankNode)
  709. $replacement = "''";
  710. }else{
  711. $replacement = 'false';
  712. }
  713. $evalString = preg_replace("/str\(\\".$var."\)/i",$replacement,$evalString);
  714. }else{
  715. if($var{0}=='<'){
  716. $evalString = preg_replace("/str\(\s*\<(.[^\>]*)\>\s*\)/i","'str_$1'",$evalString);
  717. }
  718. if($var{0}=='"'){
  719. $evalString = preg_replace("/str\(\s*\"(.[^\>]*)\"\@[a-z]*\s*\)/i","'str_$1'",$evalString);
  720. }
  721. }
  722. }
  723. }
  724. // evaluate VARS
  725. foreach($vars[0] as $var){
  726. if(isset($res[$var])&&$res[$var]!= ""){
  727. //$replacement = "'".$res[$var]->getLabel()."'";
  728. $replacement = '" "';
  729. if($res[$var] instanceof Literal){
  730. if($res[$var]->getDatatype()!= null){
  731. if($res[$var]->getDatatype() == XML_SCHEMA.'boolean')
  732. $replacement = $res[$var]->getLabel();
  733. if($res[$var]->getDatatype() == XML_SCHEMA.'double')
  734. $replacement = $res[$var]->getLabel();
  735. if($res[$var]->getDatatype() == XML_SCHEMA.'integer')
  736. $replacement = $res[$var]->getLabel();
  737. if($res[$var]->getDatatype() == XML_SCHEMA.'dateTime')
  738. $replacement = strtotime($res[$var]->getLabel());
  739. }else{
  740. if($res[$var]->getLabel()=="")
  741. $replacement = 'false';
  742. else
  743. $replacement = "'str_".$res[$var]->getLabel()."'";
  744. }
  745. }else{
  746. if($res[$var] instanceof Resource){
  747. $replacement = "'<".$res[$var]->getLabel().">'";
  748. }
  749. }
  750. $evalString = preg_replace("/\\".$var."/",$replacement,$evalString);
  751. }
  752. // problem with PHP: false < 13 is true
  753. if(isset($res[$var])){
  754. if($res[$var] == ""){
  755. if($boundExpr)
  756. $evalString = preg_replace("/\\".$var."/","false",$evalString);
  757. else
  758. $evalString = 'false';
  759. }
  760. }else{
  761. $evalString = preg_replace("/\\".$var."/","false",$evalString);
  762. }
  763. }
  764. // replace '=' with '=='
  765. $evalString = preg_replace("/(.[^\=])(\=)(.[^\=])/","$1==$3",$evalString);
  766. // rewrite Literals
  767. foreach($hits1[0] as $k => $val){
  768. $pattern = '/_REPLACED1_'.$k.'/';
  769. $evalString = preg_replace($pattern,$hits1[0][$k],$evalString,1);
  770. }
  771. foreach($hits2[0] as $k => $val){
  772. $pattern = '/_REPLACED2_'.$k.'/';
  773. $evalString = preg_replace($pattern,$hits2[0][$k],$evalString,1);
  774. }
  775. // replace xsd:boolean expressions
  776. $pattern = $pattern = '/\"\s?true\s?\"\^\^\<'.$xsd.'boolean\>|\'\s?true\s?\'\^\^xsd:boolean/';
  777. $evalString = preg_replace($pattern,"true",$evalString);
  778. $pattern = $pattern = '/\"\s?false\s?\"\^\^\<'.$xsd.'boolean\>|\'\s?false\s?\'\^\^xsd:boolean/';
  779. $evalString = preg_replace($pattern,"false",$evalString);
  780. // replace xsd:date expressions
  781. $pattern = "/\"(.[^\"]*)\"\^\^".$xsd."dateTime/";
  782. preg_match_all($pattern,$evalString,$hits);
  783. foreach($hits[1] as $dummy)
  784. $evalString = preg_replace("/\".[^\"]*\"\^\^".$xsd."dateTime/",strtotime($dummy),$evalString,1);
  785. $evalString = preg_replace("/(\'\<".$xsd."dateTime\()(.[^\)]*\))\>\'/","dateTime($2",$evalString);
  786. $evalString = preg_replace("/(\'\<".$xsd."integer\()(.[^\)]*\))\>\'/","integer($2",$evalString);
  787. // tag plain literals
  788. $evalString = preg_replace("/\"(.[^\"]*)\"([^\^])|\"(.[^\"]*)\"$/","'str_$1$3'$2",$evalString);
  789. return $evalString;
  790. }
  791. /**
  792. * Sorts the results.
  793. *
  794. * @param Array $vartable List containing the unsorted result vars
  795. * @return Array List containing the sorted result vars
  796. */
  797. protected function sortVars($vartable){
  798. $newTable = array();
  799. $mod = $this->query->getSolutionModifier();
  800. // if no ORDER BY solution modifier return vartable
  801. if($mod['order by']!= null){
  802. $order = $mod['order by'];
  803. $map = $this->buildVarmap($order,$vartable);
  804. foreach($map as $val){
  805. $newTable[] = $vartable[$val];
  806. }
  807. }else{
  808. $newTable = $vartable;
  809. }
  810. if($mod['offset'] != null){
  811. $newTable = array_slice ($newTable, $mod['offset']);
  812. }
  813. if($mod['limit'] != null){
  814. $newTable = array_slice($newTable,0,$mod['limit']);
  815. }
  816. return $newTable;
  817. }
  818. /**
  819. * Sorts the result table.
  820. *
  821. * @param String $order (ASC/DESC)
  822. * @param Array $vartable the vartable
  823. * @return Array A map that contains the new order of the result vars
  824. */
  825. protected function buildVarmap($order, $vartable){
  826. $n= 0;
  827. $result = array();
  828. $num_var = array();
  829. foreach($order as $variable)
  830. $num_var[$variable['val']] = 0;
  831. foreach($vartable as $k => $x){
  832. foreach($order as $value){
  833. // if the value is a typed Literal try to determine if it
  834. // a numeric datatype
  835. if($x[$value['val']] instanceof Literal){
  836. $dtype = $x[$value['val']]->getDatatype();
  837. if($dtype){
  838. switch($dtype){
  839. case XML_SCHEMA."integer":
  840. $num_var[$value['val']]++;
  841. break;
  842. case XML_SCHEMA."double":
  843. $num_var[$value['val']]++;
  844. break;
  845. }
  846. }
  847. }
  848. if($x[$value['val']]){
  849. if($x[$value['val']]instanceof Literal){
  850. $pref = "2";
  851. }
  852. if($x[$value['val']]instanceof Resource){
  853. $pref = "1";
  854. }
  855. if($x[$value['val']]instanceof BlankNode){
  856. $pref = "0";
  857. }
  858. $result[$value['val']][$n] = $pref.$x[$value['val']]->getLabel();
  859. }else{
  860. $result[$value['val']][$n] = "";
  861. }
  862. }
  863. $result['oldKey'][$n] = $k;
  864. $n++;
  865. }
  866. $sortString = "";
  867. foreach($order as $value){
  868. if($num_var[$value['val']] == $n)
  869. $sort = SORT_NUMERIC;
  870. else
  871. $sort = SORT_STRING;
  872. if($value['type'] == 'asc')
  873. $type = SORT_ASC;
  874. else
  875. $type = SORT_DESC;
  876. $sortString = $sortString.'$result["'.$value['val'].'"],'.$type.','.$sort.',';
  877. }
  878. $sortString = "array_multisort(".$sortString.'$result["oldKey"]);';
  879. @eval($sortString);
  880. return $result['oldKey'];
  881. }
  882. /**
  883. * Constructs a result graph.
  884. *
  885. * @param Array $vartable a table containing the result vars and their bindings
  886. * @param GraphPattern $constructPattern the CONSTRUCT pattern
  887. * @return MemModel the result graph which matches the CONSTRUCT pattern
  888. */
  889. protected function constructGraph($vartable,$constructPattern){
  890. $resultGraph = new MemModel();
  891. if(!$vartable)
  892. return $resultGraph;
  893. $tp = $constructPattern->getTriplePattern();
  894. $bnode = 0;
  895. foreach($vartable as $value){
  896. foreach($tp as $triple){
  897. $sub = $triple->getSubject();
  898. $pred = $triple->getPredicate();
  899. $obj = $triple->getObject();
  900. if(is_string($sub) && $sub{1}=='_' )
  901. $sub = new BlankNode("_bN".$bnode);
  902. if(is_string($pred) && $pred{1}=='_')
  903. $pred = new BlankNode("_bN".$bnode);
  904. if(is_string($obj) && $obj{1}=='_')
  905. $obj = new BlankNode("_bN".$bnode);
  906. if(is_string($sub))
  907. $sub = $value[$sub];
  908. if(is_string($pred))
  909. $pred = $value[$pred];
  910. if(is_string($obj))
  911. $obj = $value[$obj];
  912. if($sub != "" && $pred != "" && $obj != "")
  913. $resultGraph->add(new Statement($sub,$pred,$obj));
  914. }
  915. $bnode++;
  916. }
  917. return $resultGraph;
  918. }
  919. /**
  920. * Builds a describing named graph. To define an attribute list for a
  921. * several rdf:type look at constants.php
  922. *
  923. * @param Array $vartable
  924. * @return MemModel
  925. */
  926. protected function describeGraph($vartable){
  927. // build empty named graph
  928. $resultGraph = new MemModel();
  929. // if no where clause fill $vartable
  930. $vars = $this->query->getResultVars();
  931. if($vartable == null){
  932. if($vars){
  933. $vartable[0] = array('?x' => new Resource(substr($vars[0],1,-1)));
  934. $vars[0] = '?x';
  935. }
  936. }
  937. // fetch attribute list from constants.php
  938. global $sparql_describe;
  939. // for each resultset
  940. foreach($vartable as $resultset){
  941. foreach($vars as $varname){
  942. $varvalue = $resultset[$varname];
  943. // try to determine rdf:type of the variable
  944. $type = $this->_determineType($varvalue,$resultGraph);
  945. // search attribute list defined in constants.php
  946. $list = null;
  947. if($type){
  948. if(isset($sparql_describe[strtolower($type->getUri())]))
  949. $list = $sparql_describe[strtolower($type->getUri())] ;
  950. }
  951. // search in dataset
  952. $this->_getAttributes($list, $resultGraph, $varvalue);
  953. }
  954. }
  955. return $resultGraph;
  956. }
  957. /**
  958. * Tries to determine the rdf:type of the variable.
  959. *
  960. * @param Node $var The variable
  961. * @param MemModel $resultGraph The result graph which describes the Resource
  962. * @return String Uri of the rdf:type
  963. */
  964. protected function _determineType($var ,$resultGraph ){
  965. $type = null;
  966. // find in namedGraphs
  967. if(!$var instanceof Literal){
  968. $iter = $this->dataset->findInNamedGraphs(null,$var,new Resource(RDF_NAMESPACE_URI.'type'),null,true);
  969. while($iter->valid()){
  970. $statement = $iter->current();
  971. $type = $statement->getObject();
  972. $resultGraph->add($iter->current());
  973. break;
  974. }
  975. }
  976. // if no type information found find in default graph
  977. if(!$type){
  978. if(!$var instanceof Literal){
  979. $iter1 = $this->dataset->findInDefaultGraph($var,new Resource(RDF_NAMESPACE_URI.'type'),null);
  980. $type = null;
  981. while($iter1->valid()){
  982. $statement = $iter1->current();
  983. $type = $statement->getObject();
  984. $resultGraph->add($iter1->current());
  985. break;
  986. }
  987. }
  988. }
  989. return $type;
  990. }
  991. /**
  992. * Search the attributes listed in $list in the dataset.
  993. *
  994. * @param Array $list List containing the attributes
  995. * @param MemModel $resultGraph The result graph which describes the Resource
  996. * @return void
  997. */
  998. protected function _getAttributes($list,$resultGraph, $varvalue){
  999. if($list){
  1000. foreach($list as $attribute){
  1001. if(!$varvalue instanceof Literal){
  1002. $iter2 = $this->dataset->findInNamedGraphs(null,$varvalue,new Resource($attribute),null,true);
  1003. while($iter2->valid()){
  1004. $resultGraph->add($iter2->current());
  1005. $iter2->next();
  1006. }
  1007. $iter3 = $this->dataset->findInDefaultGraph($varvalue,new Resource($attribute),null);
  1008. while($iter3->valid()){
  1009. $resultGraph->add($iter3->current());
  1010. $iter3->next();
  1011. }
  1012. }
  1013. }
  1014. }
  1015. }
  1016. /**
  1017. * Eliminates duplicate results.
  1018. *
  1019. * @param Array $vartable a table that contains the result vars and their bindings
  1020. * @return Array the result table without duplicate results
  1021. */
  1022. protected function distinct($vartable){
  1023. $index = array();
  1024. foreach($vartable as $key => $value){
  1025. $key_index="";
  1026. foreach($value as $k => $v)
  1027. if($v instanceof Object)
  1028. $key_index = $key_index.$k.$v->toString();
  1029. if(isset($index[$key_index]))
  1030. unset($vartable[$key]);
  1031. else
  1032. $index[$key_index]= 1;
  1033. }
  1034. return $vartable;
  1035. }
  1036. /**
  1037. * Generates the result object.
  1038. *
  1039. * @param Array $vartable The result table
  1040. * @param String/boolean $resultform If set to 'xml' the result will be
  1041. * SPARQL Query Results XML Format as described in http://www.w3.org/TR/rdf-sparql-XMLres/
  1042. * @return Array/String The result
  1043. */
  1044. protected function returnResult($vartable,$resultform = false){
  1045. $result = "false";
  1046. if($vartable[0]['patternResult']!=null){
  1047. // sort vars (ORDER BY, LIMIT, OFFSET)
  1048. $vartable = $this->sortVars($vartable[0]['patternResult']);
  1049. // CONSTRUCT, ASK, DESCRIBE, SELECT
  1050. if( strtolower($this->query->getResultForm()) == 'ask'){
  1051. if(count($vartable)>0)
  1052. $result = "true";
  1053. else
  1054. $result = "false";
  1055. }else if(strtolower($this->query->getResultForm()) == 'construct'){
  1056. $result = $this->constructGraph($vartable,$this->query->getConstructPattern());
  1057. }else if(strtolower($this->query->getResultForm()) == 'describe'){
  1058. $result = $this->describeGraph($vartable);
  1059. }else{
  1060. // get result vars
  1061. $vars = $this->query->getResultVars();
  1062. // select result vars and return a result table
  1063. $vartable = $this->selectVars($vartable,$vars);
  1064. if($this->query->getResultForm()=='select distinct')
  1065. $result = $this->distinct($vartable);
  1066. else
  1067. $result = $vartable;
  1068. }
  1069. }else if(strtolower($this->query->getResultForm()) == 'describe'){
  1070. $result = $this->describeGraph(null);
  1071. }else if(strtolower($this->query->getResultForm()) == 'construct'){
  1072. $result = $this->constructGraph(false,$this->query->getConstructPattern());
  1073. }
  1074. if($resultform == 'xml' && $this->query->getResultForm()!='construct' && $this->query->getResultForm()!='describe')
  1075. $result = $this->buildXmlResult($result);
  1076. return $result;
  1077. }
  1078. /**
  1079. * Generates an xml string from a given result table.
  1080. *
  1081. * @param $vartable The result table
  1082. * @return String The xml result string
  1083. */
  1084. protected function buildXmlResult($vartable){
  1085. if($vartable instanceof NamedGraphMem )
  1086. return $vartable->writeRdfToString();
  1087. $result = '<sparql xmlns="http://www.w3.org/2005/sparql-results#">';
  1088. $header = '<head>';
  1089. // build header
  1090. if(is_array($vartable)){
  1091. $vars = $this->query->getResultVars();
  1092. $header = '<head>';
  1093. foreach($vars as $value){
  1094. $header = $header.'<variable name="'.substr($value,1).'"/>';
  1095. }
  1096. $header = $header.'</head>';
  1097. // build results
  1098. $solm = $this->query->getSolutionModifier();
  1099. $sel = $this->query->getResultForm();
  1100. $distinct = 'false';
  1101. if($sel == 'select distinct')
  1102. $distinct = 'true';
  1103. $ordered = 'false';
  1104. if($solm['order by'] != 0)
  1105. $ordered = 'true';
  1106. $results = '<results ordered="'.$ordered.'" distinct="'.$distinct.'">';
  1107. foreach($vartable as $value){
  1108. $results = $results.'<result>';
  1109. foreach($value as $varname => $varvalue)
  1110. $results = $results.$this->_getBindingString(substr($varname,1),$varvalue);
  1111. $results = $results.'</result>';
  1112. }
  1113. $results = $results.'</results>';
  1114. }else{
  1115. $results = '</head><boolean>'.$vartable.'</boolean>';
  1116. }
  1117. $result = $result.$header.$results.'</sparql>';
  1118. $result = simplexml_load_string($result);
  1119. return $result->asXML();
  1120. }
  1121. /**
  1122. * Helper Function for function buildXmlResult($vartable). Generates
  1123. * an xml string for a single variable an their corresponding value.
  1124. *
  1125. * @param String $varname The variables name
  1126. * @param Node $varvalue The value of the variable
  1127. * @return String The xml string
  1128. */
  1129. protected function _getBindingString($varname,$varvalue){
  1130. $binding = '<binding name="'.$varname.'">';
  1131. $value = '<unbound/>';
  1132. if($varvalue instanceof BlankNode ){
  1133. $value = '<bnode>'.$varvalue->getLabel().'</bnode>';
  1134. }elseif ($varvalue instanceof Resource){
  1135. $value = '<uri>'.$varvalue->getUri().'</uri>';
  1136. }elseif ($varvalue instanceof Literal){
  1137. $label = htmlentities($varvalue->getLabel());
  1138. $value = '<literal>'.$label.'</literal>';
  1139. if($varvalue->getDatatype() != null)
  1140. $value = '<literal datatype="'.$varvalue->getDatatype().'">'.$label.'</literal>';
  1141. if($varvalue->getLanguage() != null)
  1142. $value = '<literal xml:lang="'.$varvalue->getLanguage().'">'.$label.'</literal>';
  1143. }
  1144. $binding = $binding.$value.'</binding>';
  1145. return $binding;
  1146. }
  1147. /**
  1148. * Prints a query result as HTML table.
  1149. * You can change the colors in the configuration file.
  1150. *
  1151. * @param array $queryResult [][?VARNAME] = object Node
  1152. * @return void
  1153. */
  1154. public function writeQueryResultAsHtmlTable($queryResult) {
  1155. // Import Package Utility
  1156. include_once(RDFAPI_INCLUDE_DIR.PACKAGE_UTILITY);
  1157. if ( $queryResult[0] == null) {
  1158. echo 'no match<br>';
  1159. return;
  1160. }
  1161. if ( $queryResult == 'false') {
  1162. echo 'boolean: false<br>';
  1163. return;
  1164. }
  1165. if ( $queryResult == 'true') {
  1166. echo 'boolean: true<br>';
  1167. return;
  1168. }
  1169. echo '<table border="1" cellpadding="3" cellspacing="0"><tr><td><b>No.</b></td>';
  1170. foreach ($queryResult[0] as $varName => $value)
  1171. echo "<td align='center'><b>$varName</b></td>";
  1172. echo '</tr>';
  1173. foreach ($queryResult as $n => $var) {
  1174. echo '<tr><td width="20" align="right">' .($n + 1) .'.</td>';
  1175. foreach ($var as $varName => $value) {
  1176. if($value !=''){
  1177. echo INDENTATION . INDENTATION . '<td bgcolor="';
  1178. echo RDFUtil::chooseColor($value);
  1179. echo '">';
  1180. echo '<p>';
  1181. $lang = NULL;
  1182. $dtype = NULL;
  1183. if (is_a($value, 'Literal')) {
  1184. if ($value->getLanguage() != NULL)
  1185. $lang = ' <b>(xml:lang="' . $value->getLanguage() . '") </b> ';
  1186. if ($value->getDatatype() != NULL)
  1187. $dtype = ' <b>(rdf:datatype="' . $value->getDatatype() . '") </b> ';
  1188. }
  1189. echo RDFUtil::getNodeTypeName($value) .$value->getLabel() . $lang . $dtype .'</p>';
  1190. }else{
  1191. echo "<td bgcolor='white'>unbound";
  1192. }
  1193. }
  1194. echo '</tr>';
  1195. }
  1196. echo '</table>';
  1197. }
  1198. } // end: Class SparqlEngine
  1199. ?>