Grammar.php 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. <?php
  2. namespace Adldap\Query;
  3. class Grammar
  4. {
  5. /**
  6. * Wraps a query string in brackets.
  7. *
  8. * Produces: (query)
  9. *
  10. * @param string $query
  11. * @param string $prefix
  12. * @param string $suffix
  13. *
  14. * @return string
  15. */
  16. public function wrap($query, $prefix = '(', $suffix = ')')
  17. {
  18. return $prefix.$query.$suffix;
  19. }
  20. /**
  21. * Compiles the Builder instance into an LDAP query string.
  22. *
  23. * @param Builder $builder
  24. *
  25. * @return string
  26. */
  27. public function compile(Builder $builder)
  28. {
  29. $ands = $builder->filters['and'];
  30. $ors = $builder->filters['or'];
  31. $raws = $builder->filters['raw'];
  32. $query = $this->concatenate($raws);
  33. $query = $this->compileWheres($ands, $query);
  34. $query = $this->compileOrWheres($ors, $query);
  35. // We need to check if the query is already nested, otherwise
  36. // we'll nest it here and return the result.
  37. if (!$builder->isNested()) {
  38. $total = count($ands) + count($raws);
  39. // Make sure we wrap the query in an 'and' if using
  40. // multiple filters. We also need to check if only
  41. // one where is used with multiple orWheres, that
  42. // we wrap it in an `and` query.
  43. if ($total > 1 || (count($ands) === 1 && count($ors) > 0)) {
  44. $query = $this->compileAnd($query);
  45. }
  46. }
  47. return $query;
  48. }
  49. /**
  50. * Concatenates filters into a single string.
  51. *
  52. * @param array $bindings
  53. *
  54. * @return string
  55. */
  56. public function concatenate(array $bindings = [])
  57. {
  58. // Filter out empty query segments.
  59. $bindings = array_filter($bindings, function ($value) {
  60. return (string) $value !== '';
  61. });
  62. return implode('', $bindings);
  63. }
  64. /**
  65. * Returns a query string for equals.
  66. *
  67. * Produces: (field=value)
  68. *
  69. * @param string $field
  70. * @param string $value
  71. *
  72. * @return string
  73. */
  74. public function compileEquals($field, $value)
  75. {
  76. return $this->wrap($field.Operator::$equals.$value);
  77. }
  78. /**
  79. * Returns a query string for does not equal.
  80. *
  81. * Produces: (!(field=value))
  82. *
  83. * @param string $field
  84. * @param string $value
  85. *
  86. * @return string
  87. */
  88. public function compileDoesNotEqual($field, $value)
  89. {
  90. return $this->compileNot($this->compileEquals($field, $value));
  91. }
  92. /**
  93. * Alias for does not equal operator (!=) operator.
  94. *
  95. * Produces: (!(field=value))
  96. *
  97. * @param string $field
  98. * @param string $value
  99. *
  100. * @return string
  101. */
  102. public function compileDoesNotEqualAlias($field, $value)
  103. {
  104. return $this->compileDoesNotEqual($field, $value);
  105. }
  106. /**
  107. * Returns a query string for greater than or equals.
  108. *
  109. * Produces: (field>=value)
  110. *
  111. * @param string $field
  112. * @param string $value
  113. *
  114. * @return string
  115. */
  116. public function compileGreaterThanOrEquals($field, $value)
  117. {
  118. return $this->wrap($field.Operator::$greaterThanOrEquals.$value);
  119. }
  120. /**
  121. * Returns a query string for less than or equals.
  122. *
  123. * Produces: (field<=value)
  124. *
  125. * @param string $field
  126. * @param string $value
  127. *
  128. * @return string
  129. */
  130. public function compileLessThanOrEquals($field, $value)
  131. {
  132. return $this->wrap($field.Operator::$lessThanOrEquals.$value);
  133. }
  134. /**
  135. * Returns a query string for approximately equals.
  136. *
  137. * Produces: (field~=value)
  138. *
  139. * @param string $field
  140. * @param string $value
  141. *
  142. * @return string
  143. */
  144. public function compileApproximatelyEquals($field, $value)
  145. {
  146. return $this->wrap($field.Operator::$approximatelyEquals.$value);
  147. }
  148. /**
  149. * Returns a query string for starts with.
  150. *
  151. * Produces: (field=value*)
  152. *
  153. * @param string $field
  154. * @param string $value
  155. *
  156. * @return string
  157. */
  158. public function compileStartsWith($field, $value)
  159. {
  160. return $this->wrap($field.Operator::$equals.$value.Operator::$has);
  161. }
  162. /**
  163. * Returns a query string for does not start with.
  164. *
  165. * Produces: (!(field=*value))
  166. *
  167. * @param string $field
  168. * @param string $value
  169. *
  170. * @return string
  171. */
  172. public function compileNotStartsWith($field, $value)
  173. {
  174. return $this->compileNot($this->compileStartsWith($field, $value));
  175. }
  176. /**
  177. * Returns a query string for ends with.
  178. *
  179. * Produces: (field=*value)
  180. *
  181. * @param string $field
  182. * @param string $value
  183. *
  184. * @return string
  185. */
  186. public function compileEndsWith($field, $value)
  187. {
  188. return $this->wrap($field.Operator::$equals.Operator::$has.$value);
  189. }
  190. /**
  191. * Returns a query string for does not end with.
  192. *
  193. * Produces: (!(field=value*))
  194. *
  195. * @param string $field
  196. * @param string $value
  197. *
  198. * @return string
  199. */
  200. public function compileNotEndsWith($field, $value)
  201. {
  202. return $this->compileNot($this->compileEndsWith($field, $value));
  203. }
  204. /**
  205. * Returns a query string for contains.
  206. *
  207. * Produces: (field=*value*)
  208. *
  209. * @param string $field
  210. * @param string $value
  211. *
  212. * @return string
  213. */
  214. public function compileContains($field, $value)
  215. {
  216. return $this->wrap($field.Operator::$equals.Operator::$has.$value.Operator::$has);
  217. }
  218. /**
  219. * Returns a query string for does not contain.
  220. *
  221. * Produces: (!(field=*value*))
  222. *
  223. * @param string $field
  224. * @param string $value
  225. *
  226. * @return string
  227. */
  228. public function compileNotContains($field, $value)
  229. {
  230. return $this->compileNot($this->compileContains($field, $value));
  231. }
  232. /**
  233. * Returns a query string for a where has.
  234. *
  235. * Produces: (field=*)
  236. *
  237. * @param string $field
  238. *
  239. * @return string
  240. */
  241. public function compileHas($field)
  242. {
  243. return $this->wrap($field.Operator::$equals.Operator::$has);
  244. }
  245. /**
  246. * Returns a query string for a where does not have.
  247. *
  248. * Produces: (!(field=*))
  249. *
  250. * @param string $field
  251. *
  252. * @return string
  253. */
  254. public function compileNotHas($field)
  255. {
  256. return $this->compileNot($this->compileHas($field));
  257. }
  258. /**
  259. * Wraps the inserted query inside an AND operator.
  260. *
  261. * Produces: (&query)
  262. *
  263. * @param string $query
  264. *
  265. * @return string
  266. */
  267. public function compileAnd($query)
  268. {
  269. return $query ? $this->wrap($query, '(&') : '';
  270. }
  271. /**
  272. * Wraps the inserted query inside an OR operator.
  273. *
  274. * Produces: (|query)
  275. *
  276. * @param string $query
  277. *
  278. * @return string
  279. */
  280. public function compileOr($query)
  281. {
  282. return $query ? $this->wrap($query, '(|') : '';
  283. }
  284. /**
  285. * Wraps the inserted query inside an NOT operator.
  286. *
  287. * @param string $query
  288. *
  289. * @return string
  290. */
  291. public function compileNot($query)
  292. {
  293. return $query ? $this->wrap($query, '(!') : '';
  294. }
  295. /**
  296. * Assembles all where clauses in the current wheres property.
  297. *
  298. * @param array $wheres
  299. * @param string $query
  300. *
  301. * @return string
  302. */
  303. protected function compileWheres(array $wheres = [], $query = '')
  304. {
  305. foreach ($wheres as $where) {
  306. $query .= $this->compileWhere($where);
  307. }
  308. return $query;
  309. }
  310. /**
  311. * Assembles all or where clauses in the current orWheres property.
  312. *
  313. * @param array $orWheres
  314. * @param string $query
  315. *
  316. * @return string
  317. */
  318. protected function compileOrWheres(array $orWheres = [], $query = '')
  319. {
  320. $or = '';
  321. foreach ($orWheres as $where) {
  322. $or .= $this->compileWhere($where);
  323. }
  324. // Make sure we wrap the query in an 'or' if using multiple
  325. // orWheres. For example (|(QUERY)(ORWHEREQUERY)).
  326. if (($query && count($orWheres) > 0) || count($orWheres) > 1) {
  327. $query .= $this->compileOr($or);
  328. } else {
  329. $query .= $or;
  330. }
  331. return $query;
  332. }
  333. /**
  334. * Assembles a single where query based
  335. * on its operator and returns it.
  336. *
  337. * @param array $where
  338. *
  339. * @return string|null
  340. */
  341. protected function compileWhere(array $where)
  342. {
  343. // Get the name of the operator.
  344. if ($name = array_search($where['operator'], Operator::all())) {
  345. // If the name was found we'll camel case it
  346. // to run it through the compile method.
  347. $method = 'compile'.ucfirst($name);
  348. // Make sure the compile method exists for the operator.
  349. if (method_exists($this, $method)) {
  350. return $this->{$method}($where['field'], $where['value']);
  351. }
  352. }
  353. }
  354. }