/branches/v0.4.0/Classes/PHPLinq/LinqToObjects.php
PHP | 868 lines | 353 code | 114 blank | 401 comment | 54 complexity | a2b0bb7a48bc0f02dcd34c15835ae65b MD5 | raw file
Possible License(s): LGPL-2.0
- <?php
- /**
- * PHPLinq
- *
- * Copyright (c) 2008 - 2009 PHPLinq
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * @category PHPLinq
- * @package PHPLinq
- * @copyright Copyright (c) 2008 - 2009 PHPLinq (http://www.codeplex.com/PHPLinq)
- * @license http://www.gnu.org/licenses/lgpl.txt LGPL
- * @version ##VERSION##, ##DATE##
- */
-
-
- /** PHPLinq */
- require_once('PHPLinq.php');
-
- /** PHPLinq_ILinqProvider */
- require_once('PHPLinq/ILinqProvider.php');
-
- /** PHPLinq_Expression */
- require_once('PHPLinq/Expression.php');
-
- /** PHPLinq_OrderByExpression */
- require_once('PHPLinq/OrderByExpression.php');
-
- /** PHPLinq_Initiator */
- require_once('PHPLinq/Initiator.php');
-
- /** Register ILinqProvider */
- PHPLinq_Initiator::registerProvider('PHPLinq_LinqToObjects');
-
-
- /**
- * PHPLinq_LinqToObjects
- *
- * @category PHPLinq
- * @package PHPLinq
- * @copyright Copyright (c) 2008 - 2009 PHPLinq (http://www.codeplex.com/PHPLinq)
- */
- class PHPLinq_LinqToObjects implements PHPLinq_ILinqProvider {
- /**
- * Default variable name
- *
- * @var string
- */
- private $_from = '';
-
- /**
- * Data source
- *
- * @var mixed
- */
- private $_data = null;
-
- /**
- * Where expression
- *
- * @var PHPLinq_Expression
- */
- private $_where = null;
-
- /**
- * Take n elements
- *
- * @var int?
- */
- private $_take = null;
-
- /**
- * Skip n elements
- *
- * @var int?
- */
- private $_skip = null;
-
- /**
- * Take while expression is true
- *
- * @var PHPLinq_Expression
- */
- private $_takeWhile = null;
-
- /**
- * Skip while expression is true
- *
- * @var PHPLinq_Expression
- */
- private $_skipWhile = null;
-
- /**
- * OrderBy expressions
- *
- * @var PHPLinq_Expression[]
- */
- private $_orderBy = array();
-
- /**
- * Distinct expression
- *
- * @var PHPLinq_Expression
- */
- private $_distinct = null;
-
- /**
- * OfType expression
- *
- * @var PHPLinq_Expression
- */
- private $_ofType = null;
-
- /**
- * Parent PHPLinq_ILinqProvider instance, used with join conditions
- *
- * @var PHPLinq_ILinqProvider
- */
- private $_parentProvider = null;
-
- /**
- * Child PHPLinq_ILinqProvider instances, used with join conditions
- *
- * @var PHPLinq_ILinqProvider[]
- */
- private $_childProviders = array();
-
- /**
- * Join condition
- *
- * @var PHPLinq_Expression
- */
- private $_joinCondition = null;
-
- /**
- * Is object destructing?
- *
- * @var bool
- */
- private $_isDestructing;
-
- /**
- * Can this provider type handle data in $source?
- *
- * @param mixed $source
- * @return bool
- */
- public static function handles($source) {
- return is_array($source);
- }
-
- /**
- * Create a new class instance
- *
- * @param string $name
- * @param PHPLinq_ILinqProvider $parentProvider Optional parent PHPLinq_ILinqProvider instance, used with join conditions
- * @return PHPLinq_ILinqProvider
- */
- public function __construct($name, PHPLinq_ILinqProvider $parentProvider = null) {
- $this->_from = $name;
-
- if (!is_null($parentProvider)) {
- $this->_parentProvider = $parentProvider;
- $parentProvider->addChildProvider($this);
- }
-
- return $this;
- }
-
- /**
- * Class destructor
- */
- public function __destruct() {
- $this->_isDestructing = true;
-
- if (isset($this->_parentProvider) && !is_null($this->_parentProvider)) {
- if (!$this->_parentProvider->__isDestructing()) {
- $this->_parentProvider->__destruct();
- }
- $this->_parentProvider = null;
- unset($this->_parentProvider);
- }
-
- if (!is_null($this->_childProviders)) {
- foreach ($this->_childProviders as $provider) {
- $provider->__destruct();
- $provider = null;
- unset($provider);
- }
- }
- }
-
- /**
- * Is object destructing?
- *
- * @return bool
- */
- public function __isDestructing() {
- return $this->_isDestructing;
- }
-
- /**
- * Get join condition
- *
- * @return PHPLinq_Expression
- */
- public function getJoinCondition() {
- return $this->_joinCondition;
- }
-
- /**
- * Add child provider, used with joins
- *
- * @param PHPLinq_ILinqProvider $provider
- */
- public function addChildProvider(PHPLinq_ILinqProvider $provider) {
- $this->_childProviders[] = $provider;
- }
-
- /**
- * Retrieve "from" name
- *
- * @return string
- */
- public function getFromName() {
- return $this->_from;
- }
-
- /**
- * Retrieve data in data source
- *
- * @return mixed
- */
- public function getSource() {
- return $this->_data;
- }
-
- /**
- * Set source of data
- *
- * @param mixed $source
- * @return PHPLinq_ILinqProvider
- */
- public function in($source) {
- $this->_data = $source;
- return $this;
- }
-
- /**
- * Select
- *
- * @param string $expression Expression which creates a resulting element
- * @return mixed
- */
- public function select($expression = null) {
- // Returnvalue
- $returnValue = array();
-
- // Expression set?
- if (is_null($expression) || $expression == '') {
- $expression = $this->_from . ' => ' . $this->_from;
- }
-
- // OrderBy set?
- if (is_array($this->_orderBy) && count($this->_orderBy) > 0) {
- // Create sorter
- $sorter = '';
-
- // Is there only one OrderBy expression?
- if (count($this->_orderBy) == 1) {
- // First sorter
- $sorter = $this->_orderBy[0]->getFunctionReference();
- } else {
- // Create OrderBy expression
- $compareCode = '';
-
- // Compile comparer function
- $compareCode = "
- \$result = null;
- ";
- for ($i = 0; $i < count($this->_orderBy); $i++) {
-
- $f = substr($this->_orderBy[$i]->getFunctionReference(), 1);
- $compareCode .= "
- \$result = call_user_func_array(chr(0).'$f', array({$this->_from}A, {$this->_from}B));
- if (\$result != 0) {
- return \$result;
- }
- ";
-
- }
- $compareCode .= "return \$result;";
- $sorter = create_function($this->_from . 'A, ' . $this->_from . 'B', $compareCode);
- }
-
- // Sort!
- usort($this->_data, $sorter);
- }
-
- // Data source
- $dataSource = array();
-
- // Build possible join data
- foreach ($this->_data as $value) {
- // Child providers set?
- if (count($this->_childProviders) > 0) {
- // Data to be joined
- $joinedData = array();
- $joinedData[$this->_from][] = $value;
-
- // Join other values
- foreach ($this->_childProviders as $provider) {
- // Fetch possible join data
- foreach ($provider->select() as $record) {
- $joinValid = $provider->getJoinCondition()->execute( array( $this->_from => $value, $provider->getFromName() => $record ) );
- if ($joinValid) {
- $joinedData[$provider->getFromName()][] = $record;
- }
- }
- }
-
- /** BEGIN CARTESIAN JOIN */
-
- // Joinable array
- $joinable = array();
-
- // Join data keys
- $joinedDataKeys = array_keys($joinedData);
-
- // Calculate expected size of $joinable
- $size = (count($joinedData) > 0 ? 1 : 0);
- foreach ($joinedData as $values) {
- $size = $size * count($values);
- }
-
- // Create cartesian array
- for ($i = 0; $i < $size; $i++) {
- $joinable[$i] = array();
-
- for ($j = 0; $j < count($joinedData); $j++) {
- array_push($joinable[$i], current($joinedData[$joinedDataKeys[$j]]));
- }
-
- // Set cursor on next element in the $joinedData, beginning with the last array
- for ($j = (count($joinedData)-1); $j >= 0; $j--) {
- // If next returns true, then break
- if (next($joinedData[$joinedDataKeys[$j]])) {
- break;
- } else {
- // If next returns false, then reset and go on with previous $joinedData...
- reset($joinedData[$joinedDataKeys[$j]]);
- }
- }
- }
-
- /** END CARTESIAN JOIN */
-
- // Join values using selector expression
- foreach ($joinable as $values) {
- $dataSource[] = $values;
- }
- }
- }
-
- // Data source filled?
- if (count($dataSource) == 0) {
- $dataSource = $this->_data;
- }
-
- // Distinct values storage
- $distinctValues = array();
-
- // Create selector expression
- $selector = new PHPLinq_Expression($expression, $this->_from);
-
- // Count elements
- $elementCount = 0;
-
- // Loop trough data source
- foreach ($dataSource as $value) {
- // Is it a valid element?
- $isValid = true;
-
- // OfType expresion set? Evaluate it!
- if ($isValid && !is_null($this->_ofType)) {
- $isValid = $this->_ofType->execute($value);
- }
-
- // Where expresion set? Evaluate it!
- if ($isValid && !is_null($this->_where)) {
- $isValid = $this->_where->execute($value);
- }
-
- // Distinct expression set? Evaluate it!
- if ($isValid && !is_null($this->_distinct)) {
- $distinctKey = $this->_distinct->execute($value);
- if (isset($distinctValues[$distinctKey])) {
- $isValid = false;
- } else {
- $distinctValues[$distinctKey] = 1;
- }
- }
-
- // The element is valid, check if it is our selection range
- if ($isValid) {
-
- // Skip element?
- if (!is_null($this->_skipWhile) && $this->_skipWhile->execute($value)) {
- $isValid = false;
- }
- if (!is_null($this->_skip) && $elementCount < $this->_skip) {
- $isValid = false;
- }
-
- // Take element?
- if (!is_null($this->_takeWhile) && !$this->_takeWhile->execute($value)) {
- $isValid = false;
- break;
- }
- if (!is_null($this->_take) && count($returnValue) >= $this->_take) {
- $isValid = false;
- break;
- }
-
- // Next element
- $elementCount++;
-
- // Add the element to the return value if it is a valid element
- if ($isValid) {
- $returnValue[] = $selector->execute($value);
- }
- }
- }
-
- // Return value
- return $returnValue;
- }
-
- /**
- * Where
- *
- * @param string $expression Expression checking if an element should be contained
- * @return PHPLinq_ILinqProvider
- */
- public function where($expression) {
- $this->_where = !is_null($expression) ? new PHPLinq_Expression($expression, $this->_from) : null;
- return $this;
- }
-
- /**
- * Take $n elements
- *
- * @param int $n
- * @return PHPLinq_ILinqProvider
- */
- public function take($n) {
- $this->_take = $n;
- return $this;
- }
-
- /**
- * Skip $n elements
- *
- * @param int $n
- * @return PHPLinq_ILinqProvider
- */
- public function skip($n) {
- $this->_skip = $n;
- return $this;
- }
-
- /**
- * Take elements while $expression evaluates to true
- *
- * @param string $expression Expression to evaluate
- * @return PHPLinq_ILinqProvider
- */
- public function takeWhile($expression) {
- $this->_takeWhile = !is_null($expression) ? new PHPLinq_Expression($expression, $this->_from) : null;
- return $this;
- }
-
- /**
- * Skip elements while $expression evaluates to true
- *
- * @param string $expression Expression to evaluate
- * @return PHPLinq_ILinqProvider
- */
- public function skipWhile($expression) {
- $this->_skipWhile = !is_null($expression) ? new PHPLinq_Expression($expression, $this->_from) : null;
- return $this;
- }
-
- /**
- * OrderBy
- *
- * @param string $expression Expression to order elements by
- * @param string $comparer Comparer function (taking 2 arguments, returning -1, 0, 1)
- * @return PHPLinq_ILinqProvider
- */
- public function orderBy($expression, $comparer = null) {
- $this->_orderBy[0] = new PHPLinq_OrderByExpression($expression, $this->_from, false, $comparer);
- return $this;
- }
-
- /**
- * OrderByDescending
- *
- * @param string $expression Expression to order elements by
- * @param string $comparer Comparer function (taking 2 arguments, returning -1, 0, 1)
- * @return PHPLinq_ILinqProvider
- */
- public function orderByDescending($expression, $comparer = null) {
- $this->_orderBy[0] = new PHPLinq_OrderByExpression($expression, $this->_from, true, $comparer);
- return $this;
- }
-
- /**
- * ThenBy
- *
- * @param string $expression Expression to order elements by
- * @param string $comparer Comparer function (taking 2 arguments, returning -1, 0, 1)
- * @return PHPLinq_ILinqProvider
- */
- public function thenBy($expression, $comparer = null) {
- $this->_orderBy[] = new PHPLinq_OrderByExpression($expression, $this->_from, false, $comparer);
- return $this;
- }
-
- /**
- * ThenByDescending
- *
- * @param string $expression Expression to order elements by
- * @param string $comparer Comparer function (taking 2 arguments, returning -1, 0, 1)
- * @return PHPLinq_ILinqProvider
- */
- public function thenByDescending($expression, $comparer = null) {
- $this->_orderBy[] = new PHPLinq_OrderByExpression($expression, $this->_from, true, $comparer);
- return $this;
- }
-
- /**
- * Distinct
- *
- * @param string $expression Expression to retrieve the key value.
- * @return PHPLinq_ILinqProvider
- */
- public function distinct($expression) {
- $this->_distinct = !is_null($expression) ? new PHPLinq_Expression($expression, $this->_from) : null;
- return $this;
- }
-
- /**
- * Select the elements of a certain type
- *
- * @param string $type Type name
- */
- public function ofType($type) {
- // Create a new expression
- $expression = $this->_from . ' => ';
-
- // Evaluate type
- switch (strtolower($type)) {
- case 'array':
- case 'bool':
- case 'double':
- case 'float':
- case 'int':
- case 'integer':
- case 'long':
- case 'null':
- case 'numeric':
- case 'object':
- case 'real':
- case 'scalar':
- case 'string':
- $expression .= 'is_' . strtolower($type) . '(' . $this->_from . ')';
- break;
- default:
- $expression .= 'is_a(' . $this->_from . ', "' . $type . '")';
- break;
- }
-
- // Assign expression
- $this->_ofType = new PHPLinq_Expression($expression, $this->_from);
- return $this;
- }
-
- /**
- * Any
- *
- * @param string $expression Expression checking if an element is contained
- * @return boolean
- */
- public function any($expression) {
- $originalWhere = $this->_where;
-
- $result = $this->where($expression)->select($this->_from);
-
- $this->_where = $originalWhere;
-
- return count($result) > 0;
- }
-
- /**
- * All
- *
- * @param string $expression Expression checking if an all elements are contained
- * @return boolean
- */
- public function all($expression) {
- $originalWhere = $this->_where;
-
- $result = $this->where($expression)->select($this->_from);
-
- $this->_where = $originalWhere;
-
- return count($result) == count($this->_data);
- }
-
- /**
- * Contains
- *
- * @param mixed $element Is the $element contained?
- * @return boolean
- */
- public function contains($element) {
- return in_array($element, $this->_data);
- }
-
- /**
- * Reverse elements
- *
- * @param bool $preserveKeys Preserve keys?
- * @return PHPLinq_ILinqProvider
- */
- public function reverse($preserveKeys = null) {
- $data = array_reverse($this->select(), $preserveKeys);
- return linqfrom($this->_from)->in($data);
- }
-
- /**
- * Element at index
- *
- * @param mixed $index Index
- * @return mixed Element at $index
- */
- public function elementAt($index = null) {
- $originalWhere = $this->_where;
-
- $result = isset($this->_data[$index]) ? $this->_data[$index] : null;
-
- $this->_where = $originalWhere;
-
- if (count($result) > 0) {
- return array_shift($result);
- }
- return null;
- }
-
- /**
- * Element at index or default
- *
- * @param mixed $index Index
- * @param mixed $defaultValue Default value to return if nothing is found
- * @return mixed Element at $index
- */
- public function elementAtOrDefault($index = null, $defaultValue = null) {
- $returnValue = $this->elementAt($index);
- if (!is_null($returnValue)) {
- return $returnValue;
- } else {
- return $defaultValue;
- }
- }
-
- /**
- * Concatenate data
- *
- * @param mixed $source
- * @return PHPLinq_ILinqProvider
- */
- public function concat($source) {
- $data = array_merge($this->select(), $source);
- return linqfrom($this->_from)->in($data);
- }
-
- /**
- * First
- *
- * @param string $expression Expression which creates a resulting element
- * @return mixed
- */
- public function first($expression = null) {
- $linqCommand = clone $this;
- $result = $linqCommand->skip(0)->take(1)->select($expression);
- if (count($result) > 0) {
- return array_shift($result);
- }
- return null;
- }
-
- /**
- * FirstOrDefault
- *
- * @param string $expression Expression which creates a resulting element
- * @param mixed $defaultValue Default value to return if nothing is found
- * @return mixed
- */
- public function firstOrDefault ($expression = null, $defaultValue = null) {
- $returnValue = $this->first($expression);
- if (!is_null($returnValue)) {
- return $returnValue;
- } else {
- return $defaultValue;
- }
- }
-
- /**
- * Last
- *
- * @param string $expression Expression which creates a resulting element
- * @return mixed
- */
- public function last($expression = null) {
- $linqCommand = clone $this;
- $result = $linqCommand->reverse()->skip(0)->take(1)->select($expression);
- if (count($result) > 0) {
- return array_shift($result);
- }
- return null;
- }
-
- /**
- * LastOrDefault
- *
- * @param string $expression Expression which creates a resulting element
- * @param mixed $defaultValue Default value to return if nothing is found
- * @return mixed
- */
- public function lastOrDefault ($expression = null, $defaultValue = null) {
- $returnValue = $this->last($expression);
- if (!is_null($returnValue)) {
- return $returnValue;
- } else {
- return $defaultValue;
- }
- }
-
- /**
- * Single
- *
- * @param string $expression Expression which creates a resulting element
- * @return mixed
- */
- public function single($expression = null) {
- return $this->first($expression);
- }
-
- /**
- * SingleOrDefault
- *
- * @param string $expression Expression which creates a resulting element
- * @param mixed $defaultValue Default value to return if nothing is found
- * @return mixed
- */
- public function singleOrDefault ($expression = null, $defaultValue = null) {
- return $this->firstOrDefault($expression, $defaultValue);
- }
-
- /**
- * Join
- *
- * @param string $name
- * @return PHPLinq_Initiator
- */
- public function join($name) {
- return new PHPLinq_Initiator($name, $this);
- }
-
- /**
- * On
- *
- * @param string $expression Expression representing join condition
- * @return PHPLinq_ILinqProvider
- */
- public function on($expression) {
- $this->_joinCondition = new PHPLinq_Expression($expression, $this->_from);
- return $this->_parentProvider;
- }
-
- /**
- * Count elements
- *
- * @return int Element count
- */
- public function count() {
- return count($this->_data);
- }
-
- /**
- * Sum elements
- *
- * @return mixed Sum of elements
- */
- public function sum() {
- return array_sum($this->_data); // $this->aggregate(0, '$s, $t => $s + $t');
- }
-
- /**
- * Minimum of elements
- *
- * @return mixed Minimum of elements
- */
- public function min(){
- return min($this->_data);
- }
-
- /**
- * Maximum of elements
- *
- * @return mixed Maximum of elements
- */
- public function max(){
- return max($this->_data);
- }
-
- /**
- * Average of elements
- *
- * @return mixed Average of elements
- */
- public function average(){
- return $this->sum() / $this->count();
- }
-
- /**
- * Aggregate
- *
- * Example: Equivalent of count(): $this->aggregate(0, '$s, $t => $s + 1');
- *
- * @param int $seed Seed
- * @param string $expression Expression defining the aggregate
- * @return mixed aggregate
- */
- public function aggregate($seed = 0, $expression) {
- $codeExpression = new PHPLinq_Expression($expression);
-
- $runningValue = $seed;
- foreach ($this->_data as $value) {
- $runningValue = $codeExpression->execute( array($runningValue, $value) );
- }
-
- return $runningValue;
- }
- }