expression.class.inc.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. <?php
  2. // Copyright (C) 2010 Combodo SARL
  3. //
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation; version 3 of the License.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. /**
  17. * General definition of an expression tree (could be OQL, SQL or whatever)
  18. *
  19. * @author Erwan Taloc <erwan.taloc@combodo.com>
  20. * @author Romain Quetiez <romain.quetiez@combodo.com>
  21. * @author Denis Flaven <denis.flaven@combodo.com>
  22. * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
  23. */
  24. class MissingQueryArgument extends CoreException
  25. {
  26. }
  27. abstract class Expression
  28. {
  29. // recursive translation of identifiers
  30. abstract public function Translate($aTranslationData, $bMatchAll = true);
  31. // recursive rendering (aArgs used as input by default, or used as output if bRetrofitParams set to True
  32. abstract public function Render(&$aArgs = null, $bRetrofitParams = false);
  33. // recursively builds an array of class => fieldname
  34. abstract public function ListRequiredFields();
  35. abstract public function IsTrue();
  36. public function RequiresField($sClass, $sFieldName)
  37. {
  38. // #@# todo - optimize : this is called quite often when building a single query !
  39. $aRequired = $this->ListRequiredFields();
  40. if (!in_array($sClass.'.'.$sFieldName, $aRequired)) return false;
  41. return true;
  42. }
  43. public function serialize()
  44. {
  45. return base64_encode($this->Render());
  46. }
  47. static public function unserialize($sValue)
  48. {
  49. return self::FromOQL(base64_decode($sValue));
  50. }
  51. static public function FromOQL($sConditionExpr)
  52. {
  53. $oOql = new OqlInterpreter($sConditionExpr);
  54. $oExpression = $oOql->ParseExpression();
  55. return $oExpression;
  56. }
  57. public function LogAnd($oExpr)
  58. {
  59. if ($this->IsTrue()) return clone $oExpr;
  60. if ($oExpr->IsTrue()) return clone $this;
  61. return new BinaryExpression($this, 'AND', $oExpr);
  62. }
  63. public function LogOr($oExpr)
  64. {
  65. return new BinaryExpression($this, 'OR', $oExpr);
  66. }
  67. }
  68. class BinaryExpression extends Expression
  69. {
  70. protected $m_oLeftExpr; // filter code or an SQL expression (later?)
  71. protected $m_oRightExpr;
  72. protected $m_sOperator;
  73. public function __construct($oLeftExpr, $sOperator, $oRightExpr)
  74. {
  75. if (!is_object($oLeftExpr))
  76. {
  77. throw new CoreException('Expecting an Expression object on the left hand', array('found_type' => gettype($oLeftExpr)));
  78. }
  79. if (!is_object($oRightExpr))
  80. {
  81. throw new CoreException('Expecting an Expression object on the right hand', array('found_type' => gettype($oRightExpr)));
  82. }
  83. if (!$oLeftExpr instanceof Expression)
  84. {
  85. throw new CoreException('Expecting an Expression object on the left hand', array('found_class' => get_class($oLeftExpr)));
  86. }
  87. if (!$oRightExpr instanceof Expression)
  88. {
  89. throw new CoreException('Expecting an Expression object on the right hand', array('found_class' => get_class($oRightExpr)));
  90. }
  91. $this->m_oLeftExpr = $oLeftExpr;
  92. $this->m_oRightExpr = $oRightExpr;
  93. $this->m_sOperator = $sOperator;
  94. }
  95. public function IsTrue()
  96. {
  97. // return true if we are certain that it will be true
  98. if ($this->m_sOperator == 'AND')
  99. {
  100. if ($this->m_oLeftExpr->IsTrue() && $this->m_oLeftExpr->IsTrue()) return true;
  101. }
  102. return false;
  103. }
  104. public function GetLeftExpr()
  105. {
  106. return $this->m_oLeftExpr;
  107. }
  108. public function GetRightExpr()
  109. {
  110. return $this->m_oRightExpr;
  111. }
  112. public function GetOperator()
  113. {
  114. return $this->m_sOperator;
  115. }
  116. // recursive rendering
  117. public function Render(&$aArgs = null, $bRetrofitParams = false)
  118. {
  119. $sOperator = $this->GetOperator();
  120. $sLeft = $this->GetLeftExpr()->Render($aArgs, $bRetrofitParams);
  121. $sRight = $this->GetRightExpr()->Render($aArgs, $bRetrofitParams);
  122. return "($sLeft $sOperator $sRight)";
  123. }
  124. public function Translate($aTranslationData, $bMatchAll = true)
  125. {
  126. $oLeft = $this->GetLeftExpr()->Translate($aTranslationData, $bMatchAll);
  127. $oRight = $this->GetRightExpr()->Translate($aTranslationData, $bMatchAll);
  128. return new BinaryExpression($oLeft, $this->GetOperator(), $oRight);
  129. }
  130. public function ListRequiredFields()
  131. {
  132. $aLeft = $this->GetLeftExpr()->ListRequiredFields();
  133. $aRight = $this->GetRightExpr()->ListRequiredFields();
  134. return array_merge($aLeft, $aRight);
  135. }
  136. }
  137. class UnaryExpression extends Expression
  138. {
  139. protected $m_value;
  140. public function __construct($value)
  141. {
  142. $this->m_value = $value;
  143. }
  144. public function IsTrue()
  145. {
  146. // return true if we are certain that it will be true
  147. return ($this->m_value == 1);
  148. }
  149. public function GetValue()
  150. {
  151. return $this->m_value;
  152. }
  153. // recursive rendering
  154. public function Render(&$aArgs = null, $bRetrofitParams = false)
  155. {
  156. if ($bRetrofitParams)
  157. {
  158. $iParamIndex = count($aArgs) + 1; // 1-based indexation
  159. $aArgs['param'.$iParamIndex] = $this->m_value;
  160. return ':param'.$iParamIndex;
  161. }
  162. else
  163. {
  164. return CMDBSource::Quote($this->m_value);
  165. }
  166. }
  167. public function Translate($aTranslationData, $bMatchAll = true)
  168. {
  169. return clone $this;
  170. }
  171. public function ListRequiredFields()
  172. {
  173. return array();
  174. }
  175. }
  176. class ScalarExpression extends UnaryExpression
  177. {
  178. public function __construct($value)
  179. {
  180. if (!is_scalar($value))
  181. {
  182. throw new CoreException('Attempt to create a scalar expression from a non scalar', array('var_type'=>gettype($value)));
  183. }
  184. parent::__construct($value);
  185. }
  186. }
  187. class TrueExpression extends ScalarExpression
  188. {
  189. public function __construct()
  190. {
  191. parent::__construct(1);
  192. }
  193. public function IsTrue()
  194. {
  195. return true;
  196. }
  197. }
  198. class FalseExpression extends ScalarExpression
  199. {
  200. public function __construct()
  201. {
  202. parent::__construct(0);
  203. }
  204. public function IsTrue()
  205. {
  206. return false;
  207. }
  208. }
  209. class FieldExpression extends UnaryExpression
  210. {
  211. protected $m_sParent;
  212. protected $m_sName;
  213. public function __construct($sName, $sParent = '')
  214. {
  215. parent::__construct("$sParent.$sName");
  216. $this->m_sParent = $sParent;
  217. $this->m_sName = $sName;
  218. }
  219. public function IsTrue()
  220. {
  221. // return true if we are certain that it will be true
  222. return false;
  223. }
  224. public function GetParent() {return $this->m_sParent;}
  225. public function GetName() {return $this->m_sName;}
  226. // recursive rendering
  227. public function Render(&$aArgs = null, $bRetrofitParams = false)
  228. {
  229. if (empty($this->m_sParent))
  230. {
  231. return "`{$this->m_sName}`";
  232. }
  233. return "`{$this->m_sParent}`.`{$this->m_sName}`";
  234. }
  235. public function Translate($aTranslationData, $bMatchAll = true)
  236. {
  237. if (!array_key_exists($this->m_sParent, $aTranslationData))
  238. {
  239. if ($bMatchAll) throw new CoreException('Unknown parent id in translation table', array('parent_id' => $this->m_sParent, 'translation_table' => array_keys($aTranslationData)));
  240. return clone $this;
  241. }
  242. if (!array_key_exists($this->m_sName, $aTranslationData[$this->m_sParent]))
  243. {
  244. if (!array_key_exists('*', $aTranslationData[$this->m_sParent]))
  245. {
  246. // #@# debug - if ($bMatchAll) MyHelpers::var_dump_html($aTranslationData, true);
  247. if ($bMatchAll) throw new CoreException('Unknown name in translation table', array('name' => $this->m_sName, 'parent_id' => $this->m_sParent, 'translation_table' => array_keys($aTranslationData[$this->m_sParent])));
  248. return clone $this;
  249. }
  250. $sNewParent = $aTranslationData[$this->m_sParent]['*'];
  251. $sNewName = $this->m_sName;
  252. }
  253. else
  254. {
  255. $sNewParent = $aTranslationData[$this->m_sParent][$this->m_sName][0];
  256. $sNewName = $aTranslationData[$this->m_sParent][$this->m_sName][1];
  257. }
  258. return new FieldExpression($sNewName, $sNewParent);
  259. }
  260. public function ListRequiredFields()
  261. {
  262. return array($this->m_sParent.'.'.$this->m_sName);
  263. }
  264. }
  265. class VariableExpression extends UnaryExpression
  266. {
  267. protected $m_sName;
  268. public function __construct($sName)
  269. {
  270. parent::__construct($sName);
  271. $this->m_sName = $sName;
  272. }
  273. public function IsTrue()
  274. {
  275. // return true if we are certain that it will be true
  276. return false;
  277. }
  278. public function GetName() {return $this->m_sName;}
  279. // recursive rendering
  280. public function Render(&$aArgs = null, $bRetrofitParams = false)
  281. {
  282. if (is_null($aArgs))
  283. {
  284. return ':'.$this->m_sName;
  285. }
  286. elseif (array_key_exists($this->m_sName, $aArgs))
  287. {
  288. return CMDBSource::Quote($aArgs[$this->m_sName]);
  289. }
  290. elseif ($bRetrofitParams)
  291. {
  292. //$aArgs[$this->m_sName] = null;
  293. return ':'.$this->m_sName;
  294. }
  295. else
  296. {
  297. throw new MissingQueryArgument('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>$aArgs));
  298. }
  299. }
  300. }
  301. // Temporary, until we implement functions and expression casting!
  302. // ... or until we implement a real full text search based in the MATCH() expression
  303. class ListExpression extends Expression
  304. {
  305. protected $m_aExpressions;
  306. public function __construct($aExpressions)
  307. {
  308. $this->m_aExpressions = $aExpressions;
  309. }
  310. public static function FromScalars($aScalars)
  311. {
  312. $aExpressions = array();
  313. foreach($aScalars as $value)
  314. {
  315. $aExpressions[] = new ScalarExpression($value);
  316. }
  317. return new ListExpression($aExpressions);
  318. }
  319. public function IsTrue()
  320. {
  321. // return true if we are certain that it will be true
  322. return false;
  323. }
  324. public function GetItems()
  325. {
  326. return $this->m_aExpressions;
  327. }
  328. // recursive rendering
  329. public function Render(&$aArgs = null, $bRetrofitParams = false)
  330. {
  331. $aRes = array();
  332. foreach ($this->m_aExpressions as $oExpr)
  333. {
  334. $aRes[] = $oExpr->Render($aArgs, $bRetrofitParams);
  335. }
  336. return '('.implode(', ', $aRes).')';
  337. }
  338. public function Translate($aTranslationData, $bMatchAll = true)
  339. {
  340. $aRes = array();
  341. foreach ($this->m_aExpressions as $oExpr)
  342. {
  343. $aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll);
  344. }
  345. return new ListExpression($aRes);
  346. }
  347. public function ListRequiredFields()
  348. {
  349. $aRes = array();
  350. foreach ($this->m_aExpressions as $oExpr)
  351. {
  352. $aRes = array_merge($aRes, $oExpr->ListRequiredFields());
  353. }
  354. return $aRes;
  355. }
  356. }
  357. class FunctionExpression extends Expression
  358. {
  359. protected $m_sVerb;
  360. protected $m_aArgs; // array of expressions
  361. public function __construct($sVerb, $aArgExpressions)
  362. {
  363. $this->m_sVerb = $sVerb;
  364. $this->m_aArgs = $aArgExpressions;
  365. }
  366. public function IsTrue()
  367. {
  368. // return true if we are certain that it will be true
  369. return false;
  370. }
  371. public function GetVerb()
  372. {
  373. return $this->m_sVerb;
  374. }
  375. public function GetArgs()
  376. {
  377. return $this->m_aArgs;
  378. }
  379. // recursive rendering
  380. public function Render(&$aArgs = null, $bRetrofitParams = false)
  381. {
  382. $aRes = array();
  383. foreach ($this->m_aArgs as $oExpr)
  384. {
  385. $aRes[] = $oExpr->Render($aArgs, $bRetrofitParams);
  386. }
  387. return $this->m_sVerb.'('.implode(', ', $aRes).')';
  388. }
  389. public function Translate($aTranslationData, $bMatchAll = true)
  390. {
  391. $aRes = array();
  392. foreach ($this->m_aArgs as $oExpr)
  393. {
  394. $aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll);
  395. }
  396. return new FunctionExpression($this->m_sVerb, $aRes);
  397. }
  398. public function ListRequiredFields()
  399. {
  400. $aRes = array();
  401. foreach ($this->m_aArgs as $oExpr)
  402. {
  403. $aRes = array_merge($aRes, $oExpr->ListRequiredFields());
  404. }
  405. return $aRes;
  406. }
  407. }
  408. class IntervalExpression extends Expression
  409. {
  410. protected $m_oValue; // expression
  411. protected $m_sUnit;
  412. public function __construct($oValue, $sUnit)
  413. {
  414. $this->m_oValue = $oValue;
  415. $this->m_sUnit = $sUnit;
  416. }
  417. public function IsTrue()
  418. {
  419. // return true if we are certain that it will be true
  420. return false;
  421. }
  422. public function GetValue()
  423. {
  424. return $this->m_oValue;
  425. }
  426. public function GetUnit()
  427. {
  428. return $this->m_sUnit;
  429. }
  430. // recursive rendering
  431. public function Render(&$aArgs = null, $bRetrofitParams = false)
  432. {
  433. return 'INTERVAL '.$this->m_oValue->Render($aArgs, $bRetrofitParams).' '.$this->m_sUnit;
  434. }
  435. public function Translate($aTranslationData, $bMatchAll = true)
  436. {
  437. return new IntervalExpression($this->m_oValue->Translate($aTranslationData, $bMatchAll), $this->m_sUnit);
  438. }
  439. public function ListRequiredFields()
  440. {
  441. return array();
  442. }
  443. }
  444. class CharConcatExpression extends Expression
  445. {
  446. protected $m_aExpressions;
  447. public function __construct($aExpressions)
  448. {
  449. $this->m_aExpressions = $aExpressions;
  450. }
  451. public function IsTrue()
  452. {
  453. // return true if we are certain that it will be true
  454. return false;
  455. }
  456. public function GetItems()
  457. {
  458. return $this->m_aExpressions;
  459. }
  460. // recursive rendering
  461. public function Render(&$aArgs = null, $bRetrofitParams = false)
  462. {
  463. $aRes = array();
  464. foreach ($this->m_aExpressions as $oExpr)
  465. {
  466. $sCol = $oExpr->Render($aArgs, $bRetrofitParams);
  467. // Concat will be globally NULL if one single argument is null !
  468. $aRes[] = "COALESCE($sCol, '')";
  469. }
  470. return "CAST(CONCAT(".implode(', ', $aRes).") AS CHAR)";
  471. }
  472. public function Translate($aTranslationData, $bMatchAll = true)
  473. {
  474. $aRes = array();
  475. foreach ($this->m_aExpressions as $oExpr)
  476. {
  477. $aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll);
  478. }
  479. return new CharConcatExpression($aRes);
  480. }
  481. public function ListRequiredFields()
  482. {
  483. $aRes = array();
  484. foreach ($this->m_aExpressions as $oExpr)
  485. {
  486. $aRes = array_merge($aRes, $oExpr->ListRequiredFields());
  487. }
  488. return $aRes;
  489. }
  490. }
  491. ?>