PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/v2/mod_goodrelations/arc/sparqlscript/ARC2_SPARQLScriptProcessor.php

https://code.google.com/p/goodrelations-for-joomla/
PHP | 480 lines | 436 code | 21 blank | 23 comment | 35 complexity | bfc70930ad2b5ff0bc93b280ef9b172f MD5 | raw file
  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: 2008-10-08 (Tweak: GET/POST placeholders have to be uppercase now)
  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->env = array(
  22. 'endpoint' => '',
  23. 'vars' => array(),
  24. 'output' => '',
  25. 'operation_count' => 0,
  26. 'query_count' => 0,
  27. 'query_log' => array()
  28. );
  29. }
  30. function reset() {
  31. $this->__init();
  32. }
  33. /* */
  34. function processScript($s) {
  35. $r = array();
  36. $parser = $this->getParser();
  37. $parser->parse($s);
  38. $blocks = $parser->getScriptBlocks();
  39. if ($parser->getErrors()) return 0;
  40. foreach ($blocks as $block) {
  41. $sub_r = $this->processBlock($block);
  42. if ($this->getErrors()) return 0;
  43. }
  44. }
  45. /* */
  46. function getParser() {
  47. ARC2::inc('SPARQLScriptParser');
  48. return new ARC2_SPARQLScriptParser($this->a, $this);
  49. }
  50. /* */
  51. function setVar($name, $val, $type = 'literal') {
  52. $this->env['vars'][$name] = array('value_type' => $type, 'value' => $val);
  53. }
  54. /* */
  55. function replacePlaceholders($val, $context = '') {
  56. do {
  57. $old_val = $val;
  58. if (preg_match_all('/(\{(?:[^{}]+|(?R))*\})/', $val, $m)) {
  59. foreach ($m[1] as $match) {
  60. if (strpos($val, '$' . $match) === false) {/* just some container brackets, recurse */
  61. $val = str_replace($match, '{' . $this->replacePlaceholders(substr($match, 1, -1), $context) . '}', $val);
  62. }
  63. else {
  64. $ph = substr($match, 1, -1);
  65. $sub_val = $this->getPlaceholderValue($ph);
  66. if (is_array($sub_val)) $sub_val = $this->getArraySerialization($sub_val, $context);
  67. $val = str_replace('${' . $ph . '}', $sub_val, $val);
  68. }
  69. }
  70. }
  71. } while ($old_val != $val);
  72. return $val;
  73. }
  74. function getPlaceholderValue($ph) {
  75. /* simple vars */
  76. if (isset($this->env['vars'][$ph])) return $this->env['vars'][$ph]['value'];
  77. /* GET/POST */
  78. if (preg_match('/^(GET|POST)\.([^\.]+)(.*)$/', $ph, $m)) {
  79. $vals = strtoupper($m[1]) == 'GET' ? $_GET : $POST;
  80. $r = isset($vals[$m[2]]) ? $vals[$m[2]] : '';
  81. return $m[3] ? $this->getPropertyValue(array('value' => $r, 'value_type' => '?'), ltrim($m[3], '.')) : $r;
  82. }
  83. /* NOW */
  84. if (preg_match('/^NOW(.*)$/', $ph, $m)) {
  85. /* may have sub-phs */
  86. $m[1] = $this->replacePlaceholders($m[1]);
  87. $r = array(
  88. 'y' => date('Y'),
  89. 'mo' => date('m'),
  90. 'd' => date('d'),
  91. 'h' => date('H'),
  92. 'mi' => date('i'),
  93. 's' => date('s')
  94. );
  95. if (preg_match('/(\+|\-)\s*([0-9]+)(y|mo|d|h|mi|s)/is', trim($m[1]), $m2)) {
  96. eval('$r[$m2[3]] ' . $m2[1] . '= (int)' . $m2[2] . ';');
  97. }
  98. $uts = mktime($r['h'], $r['mi'], $r['s'], $r['mo'], $r['d'], $r['y']);
  99. $uts -= date('Z', $uts); /* timezone offset */
  100. return date('Y-m-d\TH:i:s\Z', $uts);
  101. }
  102. /* property */
  103. if (preg_match('/^([^\.]+)\.(.+)$/', $ph, $m)) {
  104. list($var, $path) = array($m[1], $m[2]);
  105. if (isset($this->env['vars'][$var])) {
  106. return $this->getPropertyValue($this->env['vars'][$var], $path);
  107. }
  108. }
  109. return '';
  110. }
  111. function getPropertyValue($obj, $path) {
  112. $val = $obj['value'];
  113. /* reserved */
  114. if ($path == 'size') {
  115. if ($obj['value_type'] == 'rows') return count($val);
  116. if ($obj['value_type'] == 'literal') return strlen($val);
  117. }
  118. if (preg_match('/^replace\([\'\"](\/.*\/[a-z]*)[\'\"],\s*[\'\"](.*)[\'\"]\)$/is', $path, $m)) {
  119. return @preg_replace($m[1], str_replace('$', '\\', $m[2]), $val);
  120. }
  121. if (preg_match('/^match\([\'\"](\/.*\/[a-z]*)[\'\"]\)$/is', $path, $m)) {
  122. return @preg_match($m[1], $val, $m) ? $m : '';
  123. }
  124. if (preg_match('/^urlencode\([\'\"]?(get|post|.*)[\'\"]?\)$/is', $path, $m)) {
  125. return (strtolower($m[1]) == 'post') ? rawurlencode($val) : urlencode($val);
  126. }
  127. if ($path == 'wiki2html') {
  128. $conf = array_merge($this->a, array('m4sh_item' => $val));
  129. $item = Trice::getObject('M4SH_Item', $conf, $this);
  130. return $item->getHTML();
  131. }
  132. /* struct */
  133. if (is_array($val)) {
  134. if (isset($val[$path])) return $val[$path];
  135. if (preg_match('/^([^\.]+)\.(.+)$/', $path, $m)) {
  136. list($var, $path) = array($m[1], $m[2]);
  137. if (isset($val[$var])) {
  138. return $this->getPropertyValue(array('value' => $val[$var]), $path);
  139. }
  140. return '';
  141. }
  142. }
  143. return '';
  144. }
  145. function getArraySerialization($v, $context) {
  146. $v_type = ARC2::getStructType($v);/* string|array|triples|index */
  147. $pf = ARC2::getPreferredFormat();
  148. /* string */
  149. if ($v_type == 'string') return $v;
  150. /* simple array (e.g. from SELECT) */
  151. if ($v_type == 'array') {
  152. $m = method_exists($this, 'toLegacy' . $pf) ? 'toLegacy' . $pf : 'toLegacyXML';
  153. }
  154. /* rdf */
  155. if (($v_type == 'triples') || ($v_type == 'index')) {
  156. $m = method_exists($this, 'to' . $pf) ? 'to' . $pf : ($context == 'query' ? 'toNTriples' : 'toRDFXML');
  157. }
  158. return $this->$m($v);
  159. }
  160. /* */
  161. function processBlock($block) {
  162. if ($this->max_operations && ($this->env['operation_count'] >= $this->max_operations)) return $this->addError('Number of ' . $this->max_operations . ' allowed operations exceeded.');
  163. $this->env['operation_count']++;
  164. $type = $block['type'];
  165. $m = 'process' . $this->camelCase($type) . 'Block';
  166. if (method_exists($this, $m)) {
  167. return $this->$m($block);
  168. }
  169. return $this->addError('Unsupported block type "' . $type . '"');
  170. }
  171. /* */
  172. function processEndpointDeclBlock($block) {
  173. $this->env['endpoint'] = $block['endpoint'];
  174. return $this->env;
  175. }
  176. /* */
  177. function processQueryBlock($block) {
  178. if ($this->max_queries && ($this->env['query_count'] >= $this->max_queries)) return $this->addError('Number of ' . $this->max_queries . ' allowed queries exceeded.');
  179. $this->env['query_count']++;
  180. $ep_uri = $this->replacePlaceholders($this->env['endpoint'], 'endpoint');
  181. /* q */
  182. $q = 'BASE <' . $block['base']. '>';
  183. $ns = isset($this->a['ns']) ? array_merge($this->a['ns'], $block['prefixes']) : $block['prefixes'];
  184. $added_prefixes = array();
  185. foreach ($ns as $k => $v) {
  186. $k = rtrim($k, ':');
  187. if (in_array($k, $added_prefixes)) continue;
  188. $q .= (strpos($block['query'], $k . ':') !== false) ? "\n" . 'PREFIX ' . $k . ': <' . $v . '>' : '';
  189. $added_prefixes[] = $k;
  190. }
  191. $q .= "\n" . $block['query'];
  192. /* placeholders */
  193. $q = $this->replacePlaceholders($q, 'query');
  194. $this->env['query_log'][] = '(' . $ep_uri . ') ' . $q;
  195. if ($store = $this->getStore($ep_uri)) {
  196. $sub_r = $this->v('is_remote', '', $store) ? $store->query($q, '', $ep_uri) : $store->query($q);
  197. /* ignore socket errors */
  198. if (($errs = $this->getErrors()) && preg_match('/socket/', $errs[0])) {
  199. $this->warnings[] = $errs[0];
  200. $this->errors = array();
  201. $sub_r = array();
  202. }
  203. return $sub_r;
  204. }
  205. else {
  206. return $this->addError("no store");
  207. }
  208. }
  209. function getStore($ep_uri) {
  210. /* local store */
  211. if ((!$ep_uri || $ep_uri == ARC2::getScriptURI()) && ($this->v('sparqlscript_default_endpoint', '', $this->a) == 'local')) {
  212. if (!isset($this->local_store)) $this->local_store = ARC2::getStore($this->a);/* @@todo error checking */
  213. return $this->local_store;
  214. }
  215. elseif ($ep_uri) {
  216. ARC2::inc('RemoteStore');
  217. $conf = array_merge($this->a, array('remote_store_endpoint' => $ep_uri));
  218. $store =& new ARC2_RemoteStore($conf, $this);
  219. return $store;
  220. }
  221. return 0;
  222. }
  223. /* */
  224. function processAssignmentBlock($block) {
  225. $sub_type = $block['sub_type'];
  226. $m = 'process' . $this->camelCase($sub_type) . 'AssignmentBlock';
  227. if (!method_exists($this, $m)) return $this->addError('Unknown method "' . $m . '"');
  228. return $this->$m($block);
  229. }
  230. function processQueryAssignmentBlock($block) {
  231. $qr = $this->processQueryBlock($block['query']);
  232. if ($this->getErrors() || !isset($qr['query_type'])) return 0;
  233. $qt = $qr['query_type'];
  234. $vts = array('ask' => 'bool', 'select' => 'rows', 'desribe' => 'doc', 'construct' => 'doc');
  235. $r = array(
  236. 'value_type' => isset($vts[$qt]) ? $vts[$qt] : $qt . ' result',
  237. 'value' => ($qt == 'select') ? $qr['result']['rows'] : $qr['result'],
  238. );
  239. $this->env['vars'][$block['var']['value']] = $r;
  240. }
  241. function processStringAssignmentBlock($block) {
  242. $r = array('value_type' => 'literal', 'value' => $this->replacePlaceholders($block['string']['value']));
  243. $this->env['vars'][$block['var']['value']] = $r;
  244. }
  245. function processVarAssignmentBlock($block) {
  246. if (isset($this->env['vars'][$block['var2']['value']])) {
  247. $this->env['vars'][$block['var']['value']] = $this->env['vars'][$block['var2']['value']];
  248. }
  249. else {
  250. $this->env['vars'][$block['var']['value']] = array('value_type' => 'undefined', 'value' => '');
  251. }
  252. }
  253. function processPlaceholderAssignmentBlock($block) {
  254. $ph_val = $this->getPlaceholderValue($block['placeholder']['value']);
  255. $this->env['vars'][$block['var']['value']] = array('value_type' => 'undefined', 'value' => $ph_val);
  256. }
  257. function processVarMergeAssignmentBlock($block) {
  258. $val1 = isset($this->env['vars'][$block['var2']['value']]) ? $this->env['vars'][$block['var2']['value']] : array('value_type' => 'undefined', 'value' => '');
  259. $val2 = isset($this->env['vars'][$block['var3']['value']]) ? $this->env['vars'][$block['var3']['value']] : array('value_type' => 'undefined', 'value' => '');
  260. if (is_array($val1) && is_array($val2)) {
  261. $this->env['vars'][$block['var']['value']] = array('value_type' => $val2['value_type'], 'value' => array_merge($val1['value'], $val2['value']));
  262. }
  263. elseif (is_numeric($val1) && is_numeric($val2)) {
  264. $this->env['vars'][$block['var']['value']] = $val1 + $val2;
  265. }
  266. }
  267. function processFunctionCallAssignmentBlock($block) {
  268. $sub_r = $this->processFunctionCallBlock($block['function_call']);
  269. if ($this->getErrors()) return 0;
  270. $this->env['vars'][$block['var']['value']] = $sub_r;
  271. }
  272. /* */
  273. function processIfblockBlock($block) {
  274. if ($this->testCondition($block['condition'])) {
  275. $blocks = $block['blocks'];
  276. }
  277. else {
  278. $blocks = $block['else_blocks'];
  279. }
  280. foreach ($blocks as $block) {
  281. $sub_r = $this->processBlock($block);
  282. if ($this->getErrors()) return 0;
  283. }
  284. }
  285. function testCondition($cond) {
  286. $m = 'test' . $this->camelCase($cond['type']) . 'Condition';
  287. if (!method_exists($this, $m)) return $this->addError('Unknown method "' . $m . '"');
  288. return $this->$m($cond);
  289. }
  290. function testVarCondition($cond) {
  291. $r = 0;
  292. $vn = $cond['value'];
  293. if (isset($this->env['vars'][$vn])) $r = $this->env['vars'][$vn]['value'];
  294. $op = $this->v('operator', '', $cond);
  295. if ($op == '!') $r = !$r;
  296. return $r ? true : false;
  297. }
  298. function testPlaceholderCondition($cond) {
  299. $val = $this->getPlaceholderValue($cond['value']);
  300. $r = $val ? true : false;
  301. $op = $this->v('operator', '', $cond);
  302. if ($op == '!') $r = !$r;
  303. return $r;
  304. }
  305. function testExpressionCondition($cond) {
  306. $m = 'test' . $this->camelCase($cond['sub_type']) . 'ExpressionCondition';
  307. if (!method_exists($this, $m)) return $this->addError('Unknown method "' . $m . '"');
  308. return $this->$m($cond);
  309. }
  310. function testRelationalExpressionCondition($cond) {
  311. $op = $cond['operator'];
  312. if ($op == '=') $op = '==';
  313. $val1 = $this->getPatternValue($cond['patterns'][0]);
  314. $val2 = $this->getPatternValue($cond['patterns'][1]);
  315. eval('$result = ($val1 ' . $op . ' $val2) ? 1 : 0;');
  316. return $result;
  317. }
  318. function testAndExpressionCondition($cond) {
  319. foreach ($cond['patterns'] as $pattern) {
  320. if (!$this->testCondition($pattern)) return false;
  321. }
  322. return true;
  323. }
  324. function getPatternValue($pattern) {
  325. $m = 'get' . $this->camelCase($pattern['type']) . 'PatternValue';
  326. if (!method_exists($this, $m)) return '';
  327. return $this->$m($pattern);
  328. }
  329. function getLiteralPatternValue($pattern) {
  330. return $pattern['value'];
  331. }
  332. function getPlaceholderPatternValue($pattern) {
  333. return $this->getPlaceholderValue($pattern['value']);
  334. }
  335. /* */
  336. function processForblockBlock($block) {
  337. $set = $this->v($block['set'], array('value' => array()), $this->env['vars']);
  338. $entries = $set['value'];
  339. $iterator = $block['iterator'];
  340. $blocks = $block['blocks'];
  341. if (!is_array($entries)) return 0;
  342. foreach ($entries as $entry) {
  343. $this->env['vars'][$iterator] = array('value' => $entry, 'value_type' => $set['value_type'] . ' entry');
  344. foreach ($blocks as $block) {
  345. $this->processBlock($block);
  346. if ($this->getErrors()) return 0;
  347. }
  348. }
  349. }
  350. /* */
  351. function processLiteralBlock($block) {
  352. $this->env['output'] .= $this->replacePlaceholders($block['value'], 'output');
  353. }
  354. /* */
  355. function processFunctionCallBlock($block) {
  356. $f_uri = $block['uri'];
  357. /* sparqlscript built-ins */
  358. if (strpos($f_uri, 'http://semsol.org/ns/sparqlscript#') === 0) {
  359. $fnc = str_replace('http://semsol.org/ns/sparqlscript#', '', $f_uri);
  360. if (preg_match('/^(get|post)$/i', $fnc, $m)) {
  361. return $this->processHTTPCall($block, strtoupper($m[1]));
  362. }
  363. }
  364. }
  365. function processHTTPCall($block, $mthd) {
  366. ARC2::inc('Reader');
  367. $reader =& new ARC2_Reader($this->a, $this);
  368. $url = $block['args'][0]['value'];
  369. $url = $this->replacePlaceholders($url, 'function_call');
  370. if ($mthd != 'GET') {
  371. $reader->setHTTPMethod($mthd);
  372. $reader->setCustomHeaders("Content-Type: application/x-www-form-urlencoded");
  373. }
  374. $to = $this->v('remote_call_timeout', 0, $this->a);
  375. $reader->activate($url, '', 0, $to);
  376. $format = $reader->getFormat();
  377. $resp = '';
  378. while ($d = $reader->readStream()) {
  379. $resp .= $d;
  380. }
  381. $reader->closeStream();
  382. return array('value_type' => 'http_response', 'value' => $resp);
  383. }
  384. /* */
  385. function extractVars($pattern, $input = '') {
  386. $vars = array();
  387. /* replace PHs, track ()s */
  388. $regex = $pattern;
  389. $vars = array();
  390. if (preg_match_all('/([\?\$]\{([^\}]+)\}|\([^\)]+\))/', $regex, $m)) {
  391. $matches = $m[1];
  392. $pre_vars = $m[2];
  393. foreach ($matches as $i => $match) {
  394. $vars[] = $pre_vars[$i];
  395. if ($pre_vars[$i]) {/* placeholder */
  396. $regex = str_replace($match, '(.+)', $regex);
  397. }
  398. else {/* parentheses, but may contain placeholders */
  399. $sub_regex = $match;
  400. while (preg_match('/([\?\$]\{([^\}]+)\})/', $sub_regex, $m)) {
  401. $sub_regex = str_replace($m[1], '(.+)', $sub_regex);
  402. $vars[] = $m[2];
  403. }
  404. $regex = str_replace($match, $sub_regex, $regex);
  405. }
  406. }
  407. /* eval regex */
  408. if (@preg_match('/' . $regex . '/is', $input, $m)) {
  409. $vals = $m;
  410. }
  411. else {
  412. return 0;
  413. }
  414. for ($i = 0; $i < count($vars); $i++) {
  415. if ($vars[$i]) {
  416. $this->setVar($vars[$i], isset($vals[$i + 1]) ? $vals[$i + 1] : '');
  417. }
  418. }
  419. return 1;
  420. }
  421. /* no placeholders */
  422. return ($pattern == $input) ? 1 : 0;
  423. }
  424. /* */
  425. }