PageRenderTime 44ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/elgg/mod/foafssl/arc/sparqlscript/ARC2_SPARQLScriptProcessor.php

https://bitbucket.org/rhizomatik/lorea_production/
PHP | 581 lines | 543 code | 19 blank | 19 comment | 28 complexity | 9c510cd3443a7bbf5d200a1684fdaa03 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /*
  3. homepage: ARC or plugin homepage
  4. license: http://arc.semsol.org/license
  5. class: ARC2 SPARQLScript Processor
  6. author:
  7. version: 2009-07-09 (Tweak: improved PREFIX injection)
  8. */
  9. ARC2::inc('Class');
  10. class ARC2_SPARQLScriptProcessor extends ARC2_Class {
  11. function __construct($a = '', &$caller) {
  12. parent::__construct($a, $caller);
  13. }
  14. function ARC2_SPARQLScriptProcessor ($a = '', &$caller) {
  15. $this->__construct($a, $caller);
  16. }
  17. function __init() {
  18. parent::__init();
  19. $this->max_operations = $this->v('sparqlscript_max_operations', 0, $this->a);
  20. $this->max_queries = $this->v('sparqlscript_max_queries', 0, $this->a);
  21. $this->return = 0;
  22. $this->script_hash = '';
  23. $this->env = array(
  24. 'endpoint' => '',
  25. 'vars' => array(),
  26. 'output' => '',
  27. 'operation_count' => 0,
  28. 'query_count' => 0,
  29. 'query_log' => array()
  30. );
  31. }
  32. function reset() {
  33. $this->__init();
  34. }
  35. /* */
  36. function processScript($s) {
  37. $this->script_hash = abs(crc32($s));
  38. $parser = $this->getParser();
  39. $parser->parse($s);
  40. $blocks = $parser->getScriptBlocks();
  41. if ($parser->getErrors()) return 0;
  42. foreach ($blocks as $block) {
  43. $this->processBlock($block);
  44. if ($this->return) return 0;
  45. if ($this->getErrors()) return 0;
  46. }
  47. }
  48. function getResult() {
  49. if ($this->return) {
  50. return $this->getVarValue('__return_value__');
  51. }
  52. else {
  53. return $this->env['output'];
  54. }
  55. }
  56. /* */
  57. function getParser() {
  58. ARC2::inc('SPARQLScriptParser');
  59. return new ARC2_SPARQLScriptParser($this->a, $this);
  60. }
  61. /* */
  62. function setVar($name, $val, $type = 'literal', $meta = '') {
  63. /* types: literal, var, rows, bool, doc, http_response, undefined, ? */
  64. $this->env['vars'][$name] = array(
  65. 'value_type' => $type,
  66. 'value' => $val,
  67. 'meta' => $meta ? $meta : array()
  68. );
  69. }
  70. function getVar($name) {
  71. return isset($this->env['vars'][$name]) ? $this->env['vars'][$name] : '';
  72. }
  73. function getVarValue($name) {
  74. return ($v = $this->getVar($name)) ? (isset($v['value']) ? $v['value'] : $v ) : '';
  75. }
  76. /* */
  77. function replacePlaceholders($val, $context = '', $return_string = 1, $loop = 0) {
  78. do {
  79. $old_val = $val;
  80. if (preg_match_all('/(\{(?:[^{}]+|(?R))*\})/', $val, $m)) {
  81. foreach ($m[1] as $match) {
  82. if (strpos($val, '$' . $match) === false) {/* just some container brackets, recurse */
  83. $val = str_replace($match, '{' . $this->replacePlaceholders(substr($match, 1, -1), $context, $return_string, $loop + 1) . '}', $val);
  84. }
  85. else {
  86. $ph = substr($match, 1, -1);
  87. $sub_val = $this->getPlaceholderValue($ph);
  88. if (is_array($sub_val)) {
  89. $sub_val = $this->getArraySerialization($sub_val, $context);
  90. }
  91. $val = str_replace('${' . $ph . '}', $sub_val, $val);
  92. }
  93. }
  94. }
  95. } while (($old_val != $val) && ($loop < 10));
  96. return $val;
  97. }
  98. function getPlaceholderValue($ph) {
  99. /* simple vars */
  100. if (isset($this->env['vars'][$ph])) {
  101. return $this->v('value', $this->env['vars'][$ph], $this->env['vars'][$ph]);
  102. }
  103. /* GET/POST */
  104. if (preg_match('/^(GET|POST)\.([^\.]+)(.*)$/', $ph, $m)) {
  105. $vals = strtoupper($m[1]) == 'GET' ? $_GET : $POST;
  106. $r = isset($vals[$m[2]]) ? $vals[$m[2]] : '';
  107. return $m[3] ? $this->getPropertyValue(array('value' => $r, 'value_type' => '?'), ltrim($m[3], '.')) : $r;
  108. }
  109. /* NOW */
  110. if (preg_match('/^NOW(.*)$/', $ph, $m)) {
  111. $rest = $m[1];
  112. /* may have sub-phs */
  113. $rest = $this->replacePlaceholders($rest);
  114. $r_struct = array(
  115. 'y' => date('Y'),
  116. 'mo' => date('m'),
  117. 'd' => date('d'),
  118. 'h' => date('H'),
  119. 'mi' => date('i'),
  120. 's' => date('s')
  121. );
  122. if (preg_match('/(\+|\-)\s*([0-9]+)(y|mo|d|h|mi|s)[a-z]*(.*)/is', trim($rest), $m2)) {
  123. eval('$r_struct[$m2[3]] ' . $m2[1] . '= (int)' . $m2[2] . ';');
  124. $rest = $m2[4];
  125. }
  126. $uts = mktime($r_struct['h'], $r_struct['mi'], $r_struct['s'], $r_struct['mo'], $r_struct['d'], $r_struct['y']);
  127. $uts -= date('Z', $uts); /* timezone offset */
  128. $r = date('Y-m-d\TH:i:s\Z', $uts);
  129. if (preg_match('/^\.(.+)$/', $rest, $m)) {
  130. return $this->getPropertyValue(array('value' => $r), $m[1]);
  131. }
  132. return $r;
  133. }
  134. /* property */
  135. if (preg_match('/^([^\.]+)\.(.+)$/', $ph, $m)) {
  136. list($var, $path) = array($m[1], $m[2]);
  137. if (isset($this->env['vars'][$var])) {
  138. return $this->getPropertyValue($this->env['vars'][$var], $path);
  139. }
  140. }
  141. return '';
  142. }
  143. function getPropertyValue($obj, $path) {
  144. $val = isset($obj['value']) ? $obj['value'] : $obj;
  145. $path = $this->replacePlaceholders($path, 'property_value', 0);
  146. /* reserved */
  147. if ($path == 'size') {
  148. if ($obj['value_type'] == 'rows') return count($val);
  149. if ($obj['value_type'] == 'literal') return strlen($val);
  150. }
  151. if (preg_match('/^replace\([\'\"](\/.*\/[a-z]*)[\'\"],\s*[\'\"](.*)[\'\"]\)$/is', $path, $m)) {
  152. return @preg_replace($m[1], str_replace('$', '\\', $m[2]), $val);
  153. }
  154. if (preg_match('/^match\([\'\"](\/.*\/[a-z]*)[\'\"]\)$/is', $path, $m)) {
  155. return @preg_match($m[1], $val, $m) ? $m : '';
  156. }
  157. if (preg_match('/^urlencode\([\'\"]?(get|post|.*)[\'\"]?\)$/is', $path, $m)) {
  158. return (strtolower($m[1]) == 'post') ? rawurlencode($val) : urlencode($val);
  159. }
  160. if (preg_match('/^toDataURI\([^\)]*\)$/is', $path, $m)) {
  161. return 'data:text/plain;charset=utf-8,' . rawurlencode($val);
  162. }
  163. if (preg_match('/^fromDataURI\([^\)]*\)$/is', $path, $m)) {
  164. return rawurldecode(str_replace('data:text/plain;charset=utf-8,', '', $val));
  165. }
  166. if (preg_match('/^toPrettyDate\([^\)]*\)$/is', $path, $m)) {
  167. $uts = strtotime(preg_replace('/(T|\+00\:00)/', ' ', $val));
  168. return date('D j M H:i', $uts);
  169. }
  170. if (preg_match('/^render\(([^\)]*)\)$/is', $path, $m)) {
  171. $src_format = trim($m[1], '"\'');
  172. return $this->render($val, $src_format);
  173. }
  174. /* struct */
  175. if (is_array($val)) {
  176. if (isset($val[$path])) return $val[$path];
  177. $exp_path = $this->expandPName($path);
  178. if (isset($val[$exp_path])) return $val[$exp_path];
  179. if (preg_match('/^([^\.]+)\.(.+)$/', $path, $m)) {
  180. list($var, $path) = array($m[1], $m[2]);
  181. if (isset($val[$var])) {
  182. return $this->getPropertyValue(array('value' => $val[$var]), $path);
  183. }
  184. /* qname */
  185. $exp_var = $this->expandPName($var);
  186. if (isset($val[$exp_var])) {
  187. return $this->getPropertyValue(array('value' => $val[$exp_var]), $path);
  188. }
  189. return '';
  190. }
  191. }
  192. /* meta */
  193. if (preg_match('/^\_/', $path) && isset($obj['meta']) && isset($obj['meta'][substr($path, 1)])) {
  194. return $obj['meta'][substr($path, 1)];
  195. }
  196. return '';
  197. }
  198. function render($val, $src_format = '') {
  199. if ($src_format) {
  200. $mthd = 'render' . $this->camelCase($src_format);
  201. if (method_exists($this, $mthd)) {
  202. return $this->$mthd($val);
  203. }
  204. else {
  205. return 'No rendering method found for "' . $src_format. '"';
  206. }
  207. }
  208. /* try RDF */
  209. return $this->getArraySerialization($val);
  210. }
  211. function renderObjects($os) {
  212. $r = '';
  213. foreach ($os as $o) {
  214. $r .= $r ? ', ' : '';
  215. $r .= $o['value'];
  216. }
  217. return $r;
  218. }
  219. /* */
  220. function getArraySerialization($v, $context) {
  221. $v_type = ARC2::getStructType($v);/* string|array|triples|index */
  222. $pf = ARC2::getPreferredFormat();
  223. /* string */
  224. if ($v_type == 'string') return $v;
  225. /* simple array (e.g. from SELECT) */
  226. if ($v_type == 'array') {
  227. $m = method_exists($this, 'toLegacy' . $pf) ? 'toLegacy' . $pf : 'toLegacyXML';
  228. }
  229. /* rdf */
  230. if (($v_type == 'triples') || ($v_type == 'index')) {
  231. $m = method_exists($this, 'to' . $pf) ? 'to' . $pf : ($context == 'query' ? 'toNTriples' : 'toRDFXML');
  232. }
  233. /* else */
  234. return $this->$m($v);
  235. }
  236. /* */
  237. function processBlock($block) {
  238. if ($this->max_operations && ($this->env['operation_count'] >= $this->max_operations)) return $this->addError('Number of ' . $this->max_operations . ' allowed operations exceeded.');
  239. if ($this->return) return 0;
  240. $this->env['operation_count']++;
  241. $type = $block['type'];
  242. $m = 'process' . $this->camelCase($type) . 'Block';
  243. if (method_exists($this, $m)) {
  244. return $this->$m($block);
  245. }
  246. return $this->addError('Unsupported block type "' . $type . '"');
  247. }
  248. /* */
  249. function processEndpointDeclBlock($block) {
  250. $this->env['endpoint'] = $block['endpoint'];
  251. return $this->env;
  252. }
  253. /* */
  254. function processQueryBlock($block) {
  255. if ($this->max_queries && ($this->env['query_count'] >= $this->max_queries)) return $this->addError('Number of ' . $this->max_queries . ' allowed queries exceeded.');
  256. $this->env['query_count']++;
  257. $ep_uri = $this->replacePlaceholders($this->env['endpoint'], 'endpoint');
  258. /* q */
  259. $prologue = 'BASE <' . $block['base']. '>';
  260. $q = $this->replacePlaceholders($block['query'], 'query');
  261. /* prefixes */
  262. $ns = isset($this->a['ns']) ? array_merge($this->a['ns'], $block['prefixes']) : $block['prefixes'];
  263. $q = $prologue . "\n" . $this->completeQuery($q, $ns);
  264. $this->env['query_log'][] = '(' . $ep_uri . ') ' . $q;
  265. if ($store = $this->getStore($ep_uri)) {
  266. $sub_r = $this->v('is_remote', '', $store) ? $store->query($q, '', $ep_uri) : $store->query($q);
  267. /* ignore socket errors */
  268. if (($errs = $this->getErrors()) && preg_match('/socket/', $errs[0])) {
  269. $this->warnings[] = $errs[0];
  270. $this->errors = array();
  271. $sub_r = array();
  272. }
  273. return $sub_r;
  274. }
  275. else {
  276. return $this->addError("no store (" . $ep_uri . ")");
  277. }
  278. }
  279. function getStore($ep_uri) {
  280. /* local store */
  281. if ((!$ep_uri || $ep_uri == ARC2::getScriptURI()) && ($this->v('sparqlscript_default_endpoint', '', $this->a) == 'local')) {
  282. if (!isset($this->local_store)) $this->local_store = ARC2::getStore($this->a);/* @@todo error checking */
  283. return $this->local_store;
  284. }
  285. elseif ($ep_uri) {
  286. ARC2::inc('RemoteStore');
  287. $conf = array_merge($this->a, array('remote_store_endpoint' => $ep_uri, 'reader_timeout' => 10));
  288. return new ARC2_RemoteStore($conf, $this);
  289. }
  290. return 0;
  291. }
  292. /* */
  293. function processAssignmentBlock($block) {
  294. $sub_type = $block['sub_type'];
  295. $m = 'process' . $this->camelCase($sub_type) . 'AssignmentBlock';
  296. if (!method_exists($this, $m)) return $this->addError('Unknown method "' . $m . '"');
  297. return $this->$m($block);
  298. }
  299. function processQueryAssignmentBlock($block) {
  300. $qr = $this->processQueryBlock($block['query']);
  301. if ($this->getErrors() || !isset($qr['query_type'])) return 0;
  302. $qt = $qr['query_type'];
  303. $vts = array('ask' => 'bool', 'select' => 'rows', 'desribe' => 'doc', 'construct' => 'doc');
  304. $r = array(
  305. 'value_type' => isset($vts[$qt]) ? $vts[$qt] : $qt . ' result',
  306. 'value' => ($qt == 'select') ? $this->v('rows', array(), $qr['result']) : $qr['result'],
  307. );
  308. $this->env['vars'][$block['var']['value']] = $r;
  309. }
  310. function processStringAssignmentBlock($block) {
  311. $r = array('value_type' => 'literal', 'value' => $this->replacePlaceholders($block['string']['value']));
  312. $this->env['vars'][$block['var']['value']] = $r;
  313. }
  314. function processVarAssignmentBlock($block) {
  315. if (isset($this->env['vars'][$block['var2']['value']])) {
  316. $this->env['vars'][$block['var']['value']] = $this->env['vars'][$block['var2']['value']];
  317. }
  318. else {
  319. $this->env['vars'][$block['var']['value']] = array('value_type' => 'undefined', 'value' => '');
  320. }
  321. }
  322. function processPlaceholderAssignmentBlock($block) {
  323. $ph_val = $this->getPlaceholderValue($block['placeholder']['value']);
  324. $this->env['vars'][$block['var']['value']] = array('value_type' => 'undefined', 'value' => $ph_val);
  325. }
  326. function processVarMergeAssignmentBlock($block) {
  327. $val1 = isset($this->env['vars'][$block['var2']['value']]) ? $this->env['vars'][$block['var2']['value']] : array('value_type' => 'undefined', 'value' => '');
  328. $val2 = isset($this->env['vars'][$block['var3']['value']]) ? $this->env['vars'][$block['var3']['value']] : array('value_type' => 'undefined', 'value' => '');
  329. if (is_array($val1) && is_array($val2)) {
  330. $this->env['vars'][$block['var']['value']] = array('value_type' => $val2['value_type'], 'value' => array_merge($val1['value'], $val2['value']));
  331. }
  332. elseif (is_numeric($val1) && is_numeric($val2)) {
  333. $this->env['vars'][$block['var']['value']] = $val1 + $val2;
  334. }
  335. }
  336. function processFunctionCallAssignmentBlock($block) {
  337. $sub_r = $this->processFunctionCallBlock($block['function_call']);
  338. if ($this->getErrors()) return 0;
  339. $this->env['vars'][$block['var']['value']] = $sub_r;
  340. }
  341. /* */
  342. function processReturnBlock($block) {
  343. $sub_type = $block['sub_type'];
  344. $m = 'process' . $this->camelCase($sub_type) . 'AssignmentBlock';
  345. if (!method_exists($this, $m)) return $this->addError('Unknown method "' . $m . '"');
  346. $sub_r = $this->$m($block);
  347. $this->return = 1;
  348. return $sub_r;
  349. }
  350. /* */
  351. function processIfblockBlock($block) {
  352. if ($this->testCondition($block['condition'])) {
  353. $blocks = $block['blocks'];
  354. }
  355. else {
  356. $blocks = $block['else_blocks'];
  357. }
  358. foreach ($blocks as $block) {
  359. $sub_r = $this->processBlock($block);
  360. if ($this->getErrors()) return 0;
  361. }
  362. }
  363. function testCondition($cond) {
  364. $m = 'test' . $this->camelCase($cond['type']) . 'Condition';
  365. if (!method_exists($this, $m)) return $this->addError('Unknown method "' . $m . '"');
  366. return $this->$m($cond);
  367. }
  368. function testVarCondition($cond) {
  369. $r = 0;
  370. $vn = $cond['value'];
  371. if (isset($this->env['vars'][$vn])) $r = $this->env['vars'][$vn]['value'];
  372. $op = $this->v('operator', '', $cond);
  373. if ($op == '!') $r = !$r;
  374. return $r ? true : false;
  375. }
  376. function testPlaceholderCondition($cond) {
  377. $val = $this->getPlaceholderValue($cond['value']);
  378. $r = $val ? true : false;
  379. $op = $this->v('operator', '', $cond);
  380. if ($op == '!') $r = !$r;
  381. return $r;
  382. }
  383. function testExpressionCondition($cond) {
  384. $m = 'test' . $this->camelCase($cond['sub_type']) . 'ExpressionCondition';
  385. if (!method_exists($this, $m)) return $this->addError('Unknown method "' . $m . '"');
  386. return $this->$m($cond);
  387. }
  388. function testRelationalExpressionCondition($cond) {
  389. $op = $cond['operator'];
  390. if ($op == '=') $op = '==';
  391. $val1 = $this->getPatternValue($cond['patterns'][0]);
  392. $val2 = $this->getPatternValue($cond['patterns'][1]);
  393. eval('$result = ($val1 ' . $op . ' $val2) ? 1 : 0;');
  394. return $result;
  395. }
  396. function testAndExpressionCondition($cond) {
  397. foreach ($cond['patterns'] as $pattern) {
  398. if (!$this->testCondition($pattern)) return false;
  399. }
  400. return true;
  401. }
  402. function getPatternValue($pattern) {
  403. $m = 'get' . $this->camelCase($pattern['type']) . 'PatternValue';
  404. if (!method_exists($this, $m)) return '';
  405. return $this->$m($pattern);
  406. }
  407. function getLiteralPatternValue($pattern) {
  408. return $pattern['value'];
  409. }
  410. function getPlaceholderPatternValue($pattern) {
  411. return $this->getPlaceholderValue($pattern['value']);
  412. }
  413. /* */
  414. function processForblockBlock($block) {
  415. $set = $this->v($block['set'], array('value' => array()), $this->env['vars']);
  416. $entries = isset($set['value']) ? $set['value'] : $set;
  417. $iterator = $block['iterator'];
  418. $blocks = $block['blocks'];
  419. if (!is_array($entries)) return 0;
  420. $rc = count($entries);
  421. foreach ($entries as $i => $entry) {
  422. $val_type = $this->v('value_type', 'set', $set) . ' entry';
  423. $this->env['vars'][$iterator] = array(
  424. 'value' => $entry,
  425. 'value_type' => $val_type,
  426. 'meta' => array(
  427. 'pos' => $i,
  428. 'odd_even' => ($i % 2) ? 'even' : 'odd'
  429. )
  430. );
  431. foreach ($blocks as $block) {
  432. $this->processBlock($block);
  433. if ($this->getErrors()) return 0;
  434. }
  435. }
  436. }
  437. /* */
  438. function processLiteralBlock($block) {
  439. $this->env['output'] .= $this->replacePlaceholders($block['value'], 'output');
  440. }
  441. /* */
  442. function processFunctionCallBlock($block) {
  443. $uri = $this->replacePlaceholders($block['uri'], 'function_call');
  444. /* built-ins */
  445. if (strpos($uri, $this->a['ns']['sps']) === 0) {
  446. return $this->processBuiltinFunctionCallBlock($block);
  447. }
  448. /* remote functions */
  449. }
  450. function processBuiltinFunctionCallBlock($block) {
  451. $fnc_uri = $this->replacePlaceholders($block['uri'], 'function_call');
  452. $fnc_name = substr($fnc_uri, strlen($this->a['ns']['sps']));
  453. if (preg_match('/^(get|post)$/i', $fnc_name, $m)) {
  454. return $this->processHTTPCall($block, strtoupper($m[1]));
  455. }
  456. }
  457. function processHTTPCall($block, $mthd = 'GET') {
  458. ARC2::inc('Reader');
  459. $reader =& new ARC2_Reader($this->a, $this);
  460. $url = $this->replacePlaceholders($block['args'][0]['value'], 'function_call');
  461. if ($mthd != 'GET') {
  462. $reader->setHTTPMethod($mthd);
  463. $reader->setCustomHeaders("Content-Type: application/x-www-form-urlencoded");
  464. }
  465. $to = $this->v('remote_call_timeout', 0, $this->a);
  466. $reader->activate($url, '', 0, $to);
  467. $format = $reader->getFormat();
  468. $resp = '';
  469. while ($d = $reader->readStream()) {
  470. $resp .= $d;
  471. }
  472. $reader->closeStream();
  473. unset($this->reader);
  474. return array('value_type' => 'http_response', 'value' => $resp);
  475. }
  476. /* */
  477. function extractVars($pattern, $input = '') {
  478. $vars = array();
  479. /* replace PHs, track ()s */
  480. $regex = $pattern;
  481. $vars = array();
  482. if (preg_match_all('/([\?\$]\{([^\}]+)\}|\([^\)]+\))/', $regex, $m)) {
  483. $matches = $m[1];
  484. $pre_vars = $m[2];
  485. foreach ($matches as $i => $match) {
  486. $vars[] = $pre_vars[$i];
  487. if ($pre_vars[$i]) {/* placeholder */
  488. $regex = str_replace($match, '(.+)', $regex);
  489. }
  490. else {/* parentheses, but may contain placeholders */
  491. $sub_regex = $match;
  492. while (preg_match('/([\?\$]\{([^\}]+)\})/', $sub_regex, $m)) {
  493. $sub_regex = str_replace($m[1], '(.+)', $sub_regex);
  494. $vars[] = $m[2];
  495. }
  496. $regex = str_replace($match, $sub_regex, $regex);
  497. }
  498. }
  499. /* eval regex */
  500. if (@preg_match('/' . $regex . '/is', $input, $m)) {
  501. $vals = $m;
  502. }
  503. else {
  504. return 0;
  505. }
  506. for ($i = 0; $i < count($vars); $i++) {
  507. if ($vars[$i]) {
  508. $this->setVar($vars[$i], isset($vals[$i + 1]) ? $vals[$i + 1] : '');
  509. }
  510. }
  511. return 1;
  512. }
  513. /* no placeholders */
  514. return ($pattern == $input) ? 1 : 0;
  515. }
  516. /* */
  517. }