PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/branches/v0.2.0/Classes/PHPLinq/LinqToObjects.php

#
PHP | 579 lines | 231 code | 67 blank | 281 comment | 39 complexity | c47bd8cc9b9665089fa68c952311ba73 MD5 | raw file
Possible License(s): LGPL-2.0
  1. <?php
  2. /**
  3. * PHPLinq
  4. *
  5. * Copyright (c) 2008 PHPLinq
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * @category PHPLinq
  22. * @package PHPLinq
  23. * @copyright Copyright (c) 2008 PHPLinq (http://www.codeplex.com/PHPLinq)
  24. * @license http://www.gnu.org/licenses/lgpl.txt LGPL
  25. * @version ##VERSION##, ##DATE##
  26. */
  27. /** PHPLinq */
  28. require_once('PHPLinq.php');
  29. /** PHPLinq_ILinqProvider */
  30. require_once('PHPLinq/ILinqProvider.php');
  31. /** PHPLinq_Expression */
  32. require_once('PHPLinq/Expression.php');
  33. /** PHPLinq_OrderByExpression */
  34. require_once('PHPLinq/OrderByExpression.php');
  35. /** PHPLinq_Initiator */
  36. require_once('PHPLinq/Initiator.php');
  37. /** Register ILinqProvider */
  38. PHPLinq_Initiator::registerProvider('PHPLinq_LinqToObjects');
  39. /**
  40. * PHPLinq_LinqToObjects
  41. *
  42. * @category PHPLinq
  43. * @package PHPLinq
  44. * @copyright Copyright (c) 2008 PHPLinq (http://www.codeplex.com/PHPLinq)
  45. */
  46. class PHPLinq_LinqToObjects implements PHPLinq_ILinqProvider {
  47. /**
  48. * Default variable name
  49. *
  50. * @var string
  51. */
  52. private $_from = '';
  53. /**
  54. * Data source
  55. *
  56. * @var mixed
  57. */
  58. private $_data = null;
  59. /**
  60. * Where expression
  61. *
  62. * @var PHPLinq_Expression
  63. */
  64. private $_where = null;
  65. /**
  66. * Take n elements
  67. *
  68. * @var int?
  69. */
  70. private $_take = null;
  71. /**
  72. * Skip n elements
  73. *
  74. * @var int?
  75. */
  76. private $_skip = null;
  77. /**
  78. * Take while expression is true
  79. *
  80. * @var PHPLinq_Expression
  81. */
  82. private $_takeWhile = null;
  83. /**
  84. * Skip while expression is true
  85. *
  86. * @var PHPLinq_Expression
  87. */
  88. private $_skipWhile = null;
  89. /**
  90. * OrderBy expressions
  91. *
  92. * @var PHPLinq_Expression[]
  93. */
  94. private $_orderBy = array();
  95. /**
  96. * Distinct expression
  97. *
  98. * @var PHPLinq_Expression
  99. */
  100. private $_distinct = null;
  101. /**
  102. * OfType expression
  103. *
  104. * @var PHPLinq_Expression
  105. */
  106. private $_ofType = null;
  107. /**
  108. * Can this provider type handle data in $source?
  109. *
  110. * @param mixed $source
  111. * @return bool
  112. */
  113. public static function handles($source) {
  114. return is_array($source);
  115. }
  116. /**
  117. * Create a new class instance
  118. *
  119. * @param string $name
  120. * @return PHPLinq_ILinqProvider
  121. */
  122. public function __construct($name) {
  123. $this->_from = $name;
  124. return $this;
  125. }
  126. /**
  127. * Set source of data
  128. *
  129. * @param mixed $source
  130. * @return PHPLinq_ILinqProvider
  131. */
  132. public function in($source) {
  133. $this->_data = $source;
  134. return $this;
  135. }
  136. /**
  137. * Select
  138. *
  139. * @param string $expression Expression which creates a resulting element
  140. * @return mixed
  141. */
  142. public function select($expression = null) {
  143. // Returnvalue
  144. $returnValue = array();
  145. // Expression set?
  146. if (is_null($expression) || $expression == '') {
  147. $expression = $this->_from . ' => ' . $this->_from;
  148. }
  149. // OrderBy set?
  150. if (is_array($this->_orderBy) && count($this->_orderBy) > 0) {
  151. // Create sorter
  152. $sorter = '';
  153. // Is there only one OrderBy expression?
  154. if (count($this->_orderBy) == 1) {
  155. // First sorter
  156. $sorter = $this->_orderBy[0]->getFunctionReference();
  157. } else {
  158. // Create OrderBy expression
  159. $compareCode = '';
  160. // Compile comparer function
  161. $compareCode = "
  162. \$result = null;
  163. ";
  164. for ($i = 0; $i < count($this->_orderBy); $i++) {
  165. $f = substr($this->_orderBy[$i]->getFunctionReference(), 1);
  166. $compareCode .= "
  167. \$result = call_user_func_array(chr(0).'$f', array({$this->_from}A, {$this->_from}B));
  168. if (\$result != 0) {
  169. return \$result;
  170. }
  171. ";
  172. }
  173. $compareCode .= "return \$result;";
  174. $sorter = create_function($this->_from . 'A, ' . $this->_from . 'B', $compareCode);
  175. }
  176. // Sort!
  177. usort($this->_data, $sorter);
  178. }
  179. // Distinct values storage
  180. $distinctValues = array();
  181. // Create selector expression
  182. $selector = new PHPLinq_Expression($expression, $this->_from);
  183. // Count elements
  184. $elementCount = 0;
  185. // Loop trough data source
  186. foreach ($this->_data as $value) {
  187. // Is it a valid element?
  188. $isValid = true;
  189. // OfType expresion set? Evaluate it!
  190. if ($isValid && !is_null($this->_ofType)) {
  191. $isValid = $this->_ofType->execute($value);
  192. }
  193. // Where expresion set? Evaluate it!
  194. if ($isValid && !is_null($this->_where)) {
  195. $isValid = $this->_where->execute($value);
  196. }
  197. // Distinct expression set? Evaluate it!
  198. if ($isValid && !is_null($this->_distinct)) {
  199. $distinctKey = $this->_distinct->execute($value);
  200. if (isset($distinctValues[$distinctKey])) {
  201. $isValid = false;
  202. } else {
  203. $distinctValues[$distinctKey] = 1;
  204. }
  205. }
  206. // The element is valid, check if it is our selection range
  207. if ($isValid) {
  208. // Skip element?
  209. if (!is_null($this->_skipWhile) && $this->_skipWhile->execute($value)) {
  210. $isValid = false;
  211. }
  212. if (!is_null($this->_skip) && $elementCount < $this->_skip) {
  213. $isValid = false;
  214. }
  215. // Take element?
  216. if (!is_null($this->_takeWhile) && !$this->_takeWhile->execute($value)) {
  217. $isValid = false;
  218. break;
  219. }
  220. if (!is_null($this->_take) && count($returnValue) >= $this->_take) {
  221. $isValid = false;
  222. break;
  223. }
  224. // Next element
  225. $elementCount++;
  226. // Add the element to the return value if it is a valid element
  227. if ($isValid) {
  228. $returnValue[] = $selector->execute($value);
  229. }
  230. }
  231. }
  232. // Return value
  233. return $returnValue;
  234. }
  235. /**
  236. * Where
  237. *
  238. * @param string $expression Expression checking if an element should be contained
  239. * @return PHPLinq_ILinqProvider
  240. */
  241. public function where($expression) {
  242. $this->_where = new PHPLinq_Expression($expression, $this->_from);
  243. return $this;
  244. }
  245. /**
  246. * Take $n elements
  247. *
  248. * @param int $n
  249. * @return PHPLinq_ILinqProvider
  250. */
  251. public function take($n) {
  252. $this->_take = $n;
  253. return $this;
  254. }
  255. /**
  256. * Skip $n elements
  257. *
  258. * @param int $n
  259. * @return PHPLinq_ILinqProvider
  260. */
  261. public function skip($n) {
  262. $this->_skip = $n;
  263. return $this;
  264. }
  265. /**
  266. * Take elements while $expression evaluates to true
  267. *
  268. * @param string $expression Expression to evaluate
  269. * @return PHPLinq_ILinqProvider
  270. */
  271. public function takeWhile($expression) {
  272. $this->_takeWhile = new PHPLinq_Expression($expression, $this->_from);
  273. return $this;
  274. }
  275. /**
  276. * Skip elements while $expression evaluates to true
  277. *
  278. * @param string $expression Expression to evaluate
  279. * @return PHPLinq_ILinqProvider
  280. */
  281. public function skipWhile($expression) {
  282. $this->_skipWhile = new PHPLinq_Expression($expression, $this->_from);
  283. return $this;
  284. }
  285. /**
  286. * OrderBy
  287. *
  288. * @param string $expression Expression to order elements by
  289. * @param string $comparer Comparer function (taking 2 arguments, returning -1, 0, 1)
  290. * @return PHPLinq_ILinqProvider
  291. */
  292. public function orderBy($expression, $comparer = null) {
  293. $this->_orderBy[0] = new PHPLinq_OrderByExpression($expression, $this->_from, false, $comparer);
  294. return $this;
  295. }
  296. /**
  297. * OrderByDescending
  298. *
  299. * @param string $expression Expression to order elements by
  300. * @param string $comparer Comparer function (taking 2 arguments, returning -1, 0, 1)
  301. * @return PHPLinq_ILinqProvider
  302. */
  303. public function orderByDescending($expression, $comparer = null) {
  304. $this->_orderBy[0] = new PHPLinq_OrderByExpression($expression, $this->_from, true, $comparer);
  305. return $this;
  306. }
  307. /**
  308. * ThenBy
  309. *
  310. * @param string $expression Expression to order elements by
  311. * @param string $comparer Comparer function (taking 2 arguments, returning -1, 0, 1)
  312. * @return PHPLinq_ILinqProvider
  313. */
  314. public function thenBy($expression, $comparer = null) {
  315. $this->_orderBy[] = new PHPLinq_OrderByExpression($expression, $this->_from, false, $comparer);
  316. return $this;
  317. }
  318. /**
  319. * ThenByDescending
  320. *
  321. * @param string $expression Expression to order elements by
  322. * @param string $comparer Comparer function (taking 2 arguments, returning -1, 0, 1)
  323. * @return PHPLinq_ILinqProvider
  324. */
  325. public function thenByDescending($expression, $comparer = null) {
  326. $this->_orderBy[] = new PHPLinq_OrderByExpression($expression, $this->_from, true, $comparer);
  327. return $this;
  328. }
  329. /**
  330. * Distinct
  331. *
  332. * @param string $expression Expression to retrieve the key value.
  333. * @return PHPLinq_ILinqProvider
  334. */
  335. public function distinct($expression) {
  336. $this->_distinct = new PHPLinq_Expression($expression, $this->_from);
  337. return $this;
  338. }
  339. /**
  340. * Select the elements of a certain type
  341. *
  342. * @param string $type Type name
  343. */
  344. public function ofType($type) {
  345. // Create a new expression
  346. $expression = $this->_from . ' => ';
  347. // Evaluate type
  348. switch (strtolower($type)) {
  349. case 'array':
  350. case 'bool':
  351. case 'double':
  352. case 'float':
  353. case 'int':
  354. case 'integer':
  355. case 'long':
  356. case 'null':
  357. case 'numeric':
  358. case 'object':
  359. case 'real':
  360. case 'scalar':
  361. case 'string':
  362. $expression .= 'is_' . strtolower($type) . '(' . $this->_from . ')';
  363. break;
  364. default:
  365. $expression .= 'is_a(' . $this->_from . ', "' . $type . '")';
  366. break;
  367. }
  368. // Assign expression
  369. $this->_ofType = new PHPLinq_Expression($expression, $this->_from);
  370. return $this;
  371. }
  372. /**
  373. * Any
  374. *
  375. * @param string $expression Expression checking if an element is contained
  376. * @return boolean
  377. */
  378. public function any($expression) {
  379. $result = $this->where($expression)->select($this->_from);
  380. return count($result) > 0;
  381. }
  382. /**
  383. * All
  384. *
  385. * @param string $expression Expression checking if an all elements are contained
  386. * @return boolean
  387. */
  388. public function all($expression) {
  389. $result = $this->where($expression)->select($this->_from);
  390. return count($result) == count($this->_data);
  391. }
  392. /**
  393. * Contains
  394. *
  395. * @param mixed $element Is the $element contained?
  396. * @return boolean
  397. */
  398. public function contains($element) {
  399. return in_array($element, $this->_data);
  400. }
  401. /**
  402. * Reverse elements
  403. *
  404. * @param bool $preserveKeys Preserve keys?
  405. * @return PHPLinq_ILinqProvider
  406. */
  407. public function reverse($preserveKeys = null) {
  408. $this->_data = array_reverse($this->_data, $preserveKeys);
  409. return $this;
  410. }
  411. /**
  412. * Element at index
  413. *
  414. * @param mixed $index Index
  415. * @return mixed Element at $index
  416. */
  417. public function elementAt($index = null) {
  418. return isset($this->_data[$index]) ? $this->_data[$index] : null;
  419. }
  420. /**
  421. * Element at index or default
  422. *
  423. * @param mixed $index Index
  424. * @param mixed $defaultValue Default value to return if nothing is found
  425. * @return mixed Element at $index
  426. */
  427. public function elementAtOrDefault($index = null, $defaultValue = null) {
  428. $returnValue = $this->elementAt($index);
  429. if (!is_null($returnValue)) {
  430. return $returnValue;
  431. } else {
  432. return $defaultValue;
  433. }
  434. }
  435. /**
  436. * Count elements
  437. *
  438. * @return int Element count
  439. */
  440. public function count() {
  441. return count($this->_data);
  442. }
  443. /**
  444. * Concatenate data
  445. *
  446. * @param mixed $source
  447. * @return PHPLinq_ILinqProvider
  448. */
  449. public function concat($source) {
  450. $temporaryData = array_merge($this->_data, $source);
  451. $this->_data = $temporaryData;
  452. return $this;
  453. }
  454. /**
  455. * First
  456. *
  457. * @param string $expression Expression which creates a resulting element
  458. * @return mixed
  459. */
  460. public function first($expression = null) {
  461. $linqCommand = clone $this;
  462. $result = $linqCommand->skip(0)->take(1)->select($expression);
  463. if (count($result) > 0) {
  464. return array_shift($result);
  465. }
  466. return null;
  467. }
  468. /**
  469. * FirstOrDefault
  470. *
  471. * @param string $expression Expression which creates a resulting element
  472. * @param mixed $defaultValue Default value to return if nothing is found
  473. * @return mixed
  474. */
  475. public function firstOrDefault ($expression = null, $defaultValue = null) {
  476. $returnValue = $this->first($expression);
  477. if (!is_null($returnValue)) {
  478. return $returnValue;
  479. } else {
  480. return $defaultValue;
  481. }
  482. }
  483. /**
  484. * Last
  485. *
  486. * @param string $expression Expression which creates a resulting element
  487. * @return mixed
  488. */
  489. public function last($expression = null) {
  490. $linqCommand = clone $this;
  491. $result = $linqCommand->reverse()->skip(0)->take(1)->select($expression);
  492. if (count($result) > 0) {
  493. return array_shift($result);
  494. }
  495. return null;
  496. }
  497. /**
  498. * LastOrDefault
  499. *
  500. * @param string $expression Expression which creates a resulting element
  501. * @param mixed $defaultValue Default value to return if nothing is found
  502. * @return mixed
  503. */
  504. public function lastOrDefault ($expression = null, $defaultValue = null) {
  505. $returnValue = $this->last($expression);
  506. if (!is_null($returnValue)) {
  507. return $returnValue;
  508. } else {
  509. return $defaultValue;
  510. }
  511. }
  512. }