PageRenderTime 63ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/s3db3.5.10/pearlib/rdfapi-php/api/sparql/SparqlEngine.php

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