expression.class.inc.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  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 FieldExpression extends UnaryExpression
  199. {
  200. protected $m_sParent;
  201. protected $m_sName;
  202. public function __construct($sName, $sParent = '')
  203. {
  204. parent::__construct("$sParent.$sName");
  205. $this->m_sParent = $sParent;
  206. $this->m_sName = $sName;
  207. }
  208. public function IsTrue()
  209. {
  210. // return true if we are certain that it will be true
  211. return false;
  212. }
  213. public function GetParent() {return $this->m_sParent;}
  214. public function GetName() {return $this->m_sName;}
  215. // recursive rendering
  216. public function Render(&$aArgs = null, $bRetrofitParams = false)
  217. {
  218. if (empty($this->m_sParent))
  219. {
  220. return "`{$this->m_sName}`";
  221. }
  222. return "`{$this->m_sParent}`.`{$this->m_sName}`";
  223. }
  224. public function Translate($aTranslationData, $bMatchAll = true)
  225. {
  226. if (!array_key_exists($this->m_sParent, $aTranslationData))
  227. {
  228. if ($bMatchAll) throw new CoreException('Unknown parent id in translation table', array('parent_id' => $this->m_sParent, 'translation_table' => array_keys($aTranslationData)));
  229. return clone $this;
  230. }
  231. if (!array_key_exists($this->m_sName, $aTranslationData[$this->m_sParent]))
  232. {
  233. if (!array_key_exists('*', $aTranslationData[$this->m_sParent]))
  234. {
  235. // #@# debug - if ($bMatchAll) MyHelpers::var_dump_html($aTranslationData, true);
  236. 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])));
  237. return clone $this;
  238. }
  239. $sNewParent = $aTranslationData[$this->m_sParent]['*'];
  240. $sNewName = $this->m_sName;
  241. }
  242. else
  243. {
  244. $sNewParent = $aTranslationData[$this->m_sParent][$this->m_sName][0];
  245. $sNewName = $aTranslationData[$this->m_sParent][$this->m_sName][1];
  246. }
  247. return new FieldExpression($sNewName, $sNewParent);
  248. }
  249. public function ListRequiredFields()
  250. {
  251. return array($this->m_sParent.'.'.$this->m_sName);
  252. }
  253. }
  254. class VariableExpression extends UnaryExpression
  255. {
  256. protected $m_sName;
  257. public function __construct($sName)
  258. {
  259. parent::__construct($sName);
  260. $this->m_sName = $sName;
  261. }
  262. public function IsTrue()
  263. {
  264. // return true if we are certain that it will be true
  265. return false;
  266. }
  267. public function GetName() {return $this->m_sName;}
  268. // recursive rendering
  269. public function Render(&$aArgs = null, $bRetrofitParams = false)
  270. {
  271. if (is_null($aArgs))
  272. {
  273. return ':'.$this->m_sName;
  274. }
  275. elseif (array_key_exists($this->m_sName, $aArgs))
  276. {
  277. return CMDBSource::Quote($aArgs[$this->m_sName]);
  278. }
  279. elseif ($bRetrofitParams)
  280. {
  281. //$aArgs[$this->m_sName] = null;
  282. return ':'.$this->m_sName;
  283. }
  284. else
  285. {
  286. throw new MissingQueryArgument('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>$aArgs));
  287. }
  288. }
  289. }
  290. // Temporary, until we implement functions and expression casting!
  291. // ... or until we implement a real full text search based in the MATCH() expression
  292. class ListExpression extends Expression
  293. {
  294. protected $m_aExpressions;
  295. public function __construct($aExpressions)
  296. {
  297. $this->m_aExpressions = $aExpressions;
  298. }
  299. public function IsTrue()
  300. {
  301. // return true if we are certain that it will be true
  302. return false;
  303. }
  304. public function GetItems()
  305. {
  306. return $this->m_aExpressions;
  307. }
  308. // recursive rendering
  309. public function Render(&$aArgs = null, $bRetrofitParams = false)
  310. {
  311. $aRes = array();
  312. foreach ($this->m_aExpressions as $oExpr)
  313. {
  314. $aRes[] = $oExpr->Render($aArgs, $bRetrofitParams);
  315. }
  316. return '('.implode(', ', $aRes).')';
  317. }
  318. public function Translate($aTranslationData, $bMatchAll = true)
  319. {
  320. $aRes = array();
  321. foreach ($this->m_aExpressions as $oExpr)
  322. {
  323. $aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll);
  324. }
  325. return new ListExpression($aRes);
  326. }
  327. public function ListRequiredFields()
  328. {
  329. $aRes = array();
  330. foreach ($this->m_aExpressions as $oExpr)
  331. {
  332. $aRes = array_merge($aRes, $oExpr->ListRequiredFields());
  333. }
  334. return $aRes;
  335. }
  336. }
  337. class FunctionExpression extends Expression
  338. {
  339. protected $m_sVerb;
  340. protected $m_aArgs; // array of expressions
  341. public function __construct($sVerb, $aArgExpressions)
  342. {
  343. $this->m_sVerb = $sVerb;
  344. $this->m_aArgs = $aArgExpressions;
  345. }
  346. public function IsTrue()
  347. {
  348. // return true if we are certain that it will be true
  349. return false;
  350. }
  351. public function GetVerb()
  352. {
  353. return $this->m_sVerb;
  354. }
  355. public function GetArgs()
  356. {
  357. return $this->m_aArgs;
  358. }
  359. // recursive rendering
  360. public function Render(&$aArgs = null, $bRetrofitParams = false)
  361. {
  362. $aRes = array();
  363. foreach ($this->m_aArgs as $oExpr)
  364. {
  365. $aRes[] = $oExpr->Render($aArgs, $bRetrofitParams);
  366. }
  367. return $this->m_sVerb.'('.implode(', ', $aRes).')';
  368. }
  369. public function Translate($aTranslationData, $bMatchAll = true)
  370. {
  371. $aRes = array();
  372. foreach ($this->m_aArgs as $oExpr)
  373. {
  374. $aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll);
  375. }
  376. return new FunctionExpression($this->m_sVerb, $aRes);
  377. }
  378. public function ListRequiredFields()
  379. {
  380. $aRes = array();
  381. foreach ($this->m_aArgs as $oExpr)
  382. {
  383. $aRes = array_merge($aRes, $oExpr->ListRequiredFields());
  384. }
  385. return $aRes;
  386. }
  387. }
  388. class IntervalExpression extends Expression
  389. {
  390. protected $m_oValue; // expression
  391. protected $m_sUnit;
  392. public function __construct($oValue, $sUnit)
  393. {
  394. $this->m_oValue = $oValue;
  395. $this->m_sUnit = $sUnit;
  396. }
  397. public function IsTrue()
  398. {
  399. // return true if we are certain that it will be true
  400. return false;
  401. }
  402. public function GetValue()
  403. {
  404. return $this->m_oValue;
  405. }
  406. public function GetUnit()
  407. {
  408. return $this->m_sUnit;
  409. }
  410. // recursive rendering
  411. public function Render(&$aArgs = null, $bRetrofitParams = false)
  412. {
  413. return 'INTERVAL '.$this->m_oValue->Render($aArgs, $bRetrofitParams).' '.$this->m_sUnit;
  414. }
  415. public function Translate($aTranslationData, $bMatchAll = true)
  416. {
  417. return new IntervalExpression($this->m_oValue->Translate($aTranslationData, $bMatchAll), $this->m_sUnit);
  418. }
  419. public function ListRequiredFields()
  420. {
  421. return array();
  422. }
  423. }
  424. class CharConcatExpression extends Expression
  425. {
  426. protected $m_aExpressions;
  427. public function __construct($aExpressions)
  428. {
  429. $this->m_aExpressions = $aExpressions;
  430. }
  431. public function IsTrue()
  432. {
  433. // return true if we are certain that it will be true
  434. return false;
  435. }
  436. public function GetItems()
  437. {
  438. return $this->m_aExpressions;
  439. }
  440. // recursive rendering
  441. public function Render(&$aArgs = null, $bRetrofitParams = false)
  442. {
  443. $aRes = array();
  444. foreach ($this->m_aExpressions as $oExpr)
  445. {
  446. $sCol = $oExpr->Render($aArgs, $bRetrofitParams);
  447. // Concat will be globally NULL if one single argument is null !
  448. $aRes[] = "COALESCE($sCol, '')";
  449. }
  450. return "CAST(CONCAT(".implode(', ', $aRes).") AS CHAR)";
  451. }
  452. public function Translate($aTranslationData, $bMatchAll = true)
  453. {
  454. $aRes = array();
  455. foreach ($this->m_aExpressions as $oExpr)
  456. {
  457. $aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll);
  458. }
  459. return new CharConcatExpression($aRes);
  460. }
  461. public function ListRequiredFields()
  462. {
  463. $aRes = array();
  464. foreach ($this->m_aExpressions as $oExpr)
  465. {
  466. $aRes = array_merge($aRes, $oExpr->ListRequiredFields());
  467. }
  468. return $aRes;
  469. }
  470. }
  471. ?>