scratch

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 86  →  ?path2? @ 87
/vendor/fusonic/linq/.gitignore
@@ -0,0 +1,4 @@
.idea/
vendor/
composer.lock
composer.phar
/vendor/fusonic/linq/.travis.yml
@@ -0,0 +1,8 @@
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
install: composer install
script: phpunit --stderr --bootstrap tests/bootstrap.php tests
/vendor/fusonic/linq/LICENSE
@@ -0,0 +1,20 @@
Copyright (c) 2014 Fusonic GmbH (http://www.fusonic.net)
 
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
 
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/vendor/fusonic/linq/README.md
@@ -0,0 +1,207 @@
# fusonic/linq
 
[![Build Status](https://travis-ci.org/fusonic/linq.png)](https://travis-ci.org/fusonic/linq)
[![Total Downloads](https://poser.pugx.org/fusonic/linq/downloads.png)](https://packagist.org/packages/fusonic/linq)
 
fusonic/linq is a lightweight PHP library inspired by the LINQ 2 Objects extension methods in .NET.
 
For a full introduction read my blog-post: http://www.fusonic.net/en/blog/2013/08/14/fusonic-linq-write-less-do-more/
 
LINQ queries offer three main advantages over traditional foreach loops:
 
* They are more concise and readable, especially when filtering multiple conditions.
 
* They provide powerful filtering, ordering, and grouping capabilities with a minimum of application code.
 
* In general, the more complex the operation you want to perform on the data, the more benefit you will realize by using LINQ instead of traditional iteration techniques.
 
## Requirements
 
fusonic/linq is supported on PHP 5.3 and up.
 
 
## Installation & Usage
 
The most flexible installation method is using Composer: Simply create a composer.json file in the root of your project:
``` json
{
"require": {
"fusonic/linq": "@dev"
}
}
```
 
Install composer and run install command:
``` bash
curl -s http://getcomposer.org/installer | php
php composer.phar install
```
 
Once installed, include vendor/autoload.php in your script to autoload fusonic/linq.
 
``` php
require 'vendor/autoload.php';
use Fusonic\Linq\Linq;
 
Linq::from(array())->count();
```
 
## Examples
 
### Calculate the average file size of files in a directory:
``` php
$source = glob("files/*");
Linq::from($source)
->select(function($i) { return filesize($i); })
->average();
```
 
### Find all files bigger than 1024 bytes and return the fileinfo object:
``` php
$source = glob("files/*");
Linq::from($source)
->where(function($i) { return filesize($i) > 1024; })
->select(function($i) { return pathinfo($i); });
```
 
### Search for all users containing "Max 1", Skip 5 items, Take 2 items and select the property ID of each user:
```php
$result = Linq::from($users)
->where(function (User $u) { return strstr($u->surname, "Max 1"); })
->skip(5)
->take(2)
->select(function (User $u) { return $u->usrId; });
```
 
### Flatten multiple sequences into one sequence:
```php
$array1 = array("key" => "a", "data" => array("a1", "a2"));
$array2 = array("key" => "b", "data" => array("b1", "b2"));
$array3 = array("key" => "c", "data" => array("c1", "c2"));
 
$allArrays = array($array1, $array2, $array3);
 
$result = Linq::from($allArrays)
->selectMany(function($x) { return $x["data"]; })
->toArray();
// $result is now: array("a1", "a2", "b1", "b2", "c1", "c2");
 
```
### Map sequence to array with key/value selectors:
```php
$category1 = new stdClass(); $category1->key = 1; $category1->value = "Cars";
$category2 = new stdClass(); $category2->key = 2; $category2->value = "Ships";
 
$result = Linq::from(array($category1, $category2))
->toArray(
function($x) { return $x->key; }, // key-selector
function($x) { return $x->value; } // value-selector
);
// $result is now: array(1 => "Cars", 2 => "Ships");
```
 
### The aggregate method makes it simple to perform a calculation over a sequence of values:
```php
$numbers = Linq::from(array(1,2,3,4));
$sum = $numbers->aggregate(function($a, $b) { return $a + $b; });
// echo $sum; // output: 10 (1+2+3+4)
 
$chars = Linq::from(array("a", "b", "c"));
$csv = $chars->aggregate(function($a, $b) { return $a . "," . $b; });
// echo $csv; // output: "a,b,c"
 
$chars = Linq::from(array("a", "b", "c"));
$csv = $chars->aggregate(function($a, $b) { return $a . "," . $b; }, "seed");
// echo $csv; // output: "seed,a,b,c"
 
```
 
 
### The chunk method makes it simple to split a sequence into chunks of a given size:
```php
$chunks = Linq::from(array("a","b","c","d","e"))->chunk(2);
$i = 0;
foreach($chunk in $chunks) {
$i++;
echo "Row $i <br>";
foreach($char in $chunk) {
echo $char . "|";
}
}
// Result:
// Row 1
// a|b
// Row 2
// c|d
// Row 3
// e|
 
```
 
## List of methods provided by fusonic/linq:
 
```php
aggregate($func, $seed = null) // Applies an accumulator function over a sequence.
all($func) // Determines wheter all elements satisfy a condition.
any($func) // Determines wheter any element satisfies a condition.
average($func = null) // Computes the average of all numeric values.
concat($second) // Concatenates 2 sequences
contains($value) // Determines whether a sequence contains a specified element.
count() // Counts the elements of the sequence.
chunk($chunksize) // Splits the sequence in chunks according to $chunksize.
except($second) // Returns all items except the ones of the given sequence.
distinct($func = null) // Returns all distinct items of a sequence using the optional selector.
each($func) // Performs the specified action on each element of the sequence.
elementAt($index) // Returns the element at a specified index or throws an exception.
elementAtOrNull($index) // Returns the element at a specified index or returns null
first($func = null) // Returns the first element that satisfies a specified condition or throws an exception.
firstOrNull($func = null) // Returns the first element, or NULL if the sequence contains no elements.
groupBy($keySelector) // Groups the object according to the $keySelector generated key.
intersect($second) // Intersects the Linq sequence with second Iterable sequence.
last($func = null) // Returns the last element that satisfies a specified condition or throws an exception.
lastOrNull($func = null) // Returns the last element that satisfies a condition or NULL if no such element is found.
max($func = null) // Returns the maximum item value according to $func.
min($func = null) // Returns the minimum item value according to $func
orderBy($func) // Sorts the elements in ascending order according to a key provided by $func.
orderByDescending($func) // Sorts the elements in descending order according to a key provided by $func.
select($func) // Projects each element into a new form by invoking the selector function.
selectMany($func) // Projects each element of a sequence to a new Linq and flattens the resulting sequences into one sequence.
single($func = null) // Returns the only element that satisfies a specified condition or throws an exception.
singleOrDefault($func = null) // Returns the only element that satisfies a specified condition or returns Null.
skip($count) // Bypasses a specified number of elements and then returns the remaining elements.
sum($func = null) // Gets the sum of all items or by invoking a transform function on each item to get a numeric value.
take($count) // Returns a specified number of contiguous elements from the start of a sequence.
toArray($keySelector=null, $valueSelector=null) // Creates an Array from this Linq object with an optional key selector.
where($func) // Filters the Linq object according to func return result.
```
 
## Simple, Consistent and Predictable
 
One important design goal was the principle of the least surprise. As PHP is a fully dynamic language with nearly no type-safety, it is common to shoot yourself into the foot because of accidentally mixing up incompatible types.
 
We protect you from these programing errors by asserting that every callback functions you supply to the library must return a correctly typed value. In addition, every supported aggregate function will throw an exception if you are accidentally mixing up incompatible types.
 
This means that we made this library totally predictable in what it does, and verified that every function has its defined exceptions which are thrown when certain operations fail, or if certain types are not correct.
 
```php
/* Throws an UnexpectedValueException if the
provided callback function does not return a boolean */
Linq::from(array("1", "1"))
->where(function($x) { return "NOT A BOOLEAN"; });
 
/* Throws an UnexpectedValueException if one of the values
is not convertible to a numeric value:*/
Linq::from(array(1, 2, "Not a numeric value"))
->sum();
```
 
## Running tests
 
You can run the test suite with the following command:
 
```bash
phpunit --bootstrap tests/bootstrap.php .
```
 
/vendor/fusonic/linq/composer.json
@@ -0,0 +1,22 @@
{
"name": "fusonic/linq",
"description": "LINQ 2 objects class for PHP",
"keywords": ["linq", "linq2objects"],
"type": "library",
"homepage": "http://fusonic.github.io/fusonic-linq/",
"license": "MIT",
"authors": [
{
"name": "Fusonic",
"homepage": "http://www.fusonic.net"
}
],
"require": {
"php": ">=5.3.2"
},
"autoload": {
"psr-0": {
"Fusonic\\Linq": "src/"
}
}
}
/vendor/fusonic/linq/examples/1-average.php
@@ -0,0 +1,28 @@
<?php
 
require_once __DIR__ . '/../vendor/autoload.php'; // Autoload files using Composer autoload
 
use Fusonic\Linq\Linq;
$files = glob("/tmp/*");
 
// Calculate the average filesize of all files in a directory.
 
### Plain PHP: ###
 
$sum = 0;
$i = 0;
foreach($files as $file) {
$sum += filesize($file);
$i++;
}
$avg = $i == 0 ? 0 : $sum / $i;
 
echo "Average: " . $avg;
 
### Linq: ###
 
$avgL = Linq::from($files)
->select(function($f) { return filesize($f); })
->average();
 
echo "<br/><br>Average Linq: " . $avgL;
/vendor/fusonic/linq/examples/2-filter.php
@@ -0,0 +1,32 @@
<?php
 
require_once __DIR__ . '/../vendor/autoload.php'; // Autoload files using Composer autoload
 
use Fusonic\Linq\Linq;
$files = glob("/tmp/*");
 
// Calculate the average filesize of all files greater than 1024 bytes in a directory.
 
### Plain PHP: ###
 
$sum = 0;
$i = 0;
foreach($files as $file) {
$currentSize = filesize($file);
if($currentSize > 1024) {
$sum += $currentSize;
$i++;
}
}
$avg = $sum / $i;
 
echo "Average: " . $avg;
 
### Linq: ###
 
$avgL = Linq::from($files)
->select(function($f) { return filesize($f); })
->where(function($fs) { return $fs > 1024; })
->average();
 
echo "<br/><br>Average Linq: " . $avgL;
/vendor/fusonic/linq/examples/3-skiptake.php
@@ -0,0 +1,44 @@
<?php
 
require_once __DIR__ . '/../vendor/autoload.php'; // Autoload files using Composer autoload
 
use Fusonic\Linq\Linq;
$files = glob("/tmp/*");
 
// Calculate the average filesize of all files greater than 1024 bytes in a directory
// but skip the very first 2 files and then take only 4 files.
 
### Plain PHP: ###
 
$sum = 0;
$i = 0;
$y = 0;
foreach($files as $file) {
$currentSize = filesize($file);
if($currentSize > 1024) {
if($y < 2) {
$y++;
continue;
}
else if ($y > 5) {
break;
}
$y++;
$sum += $currentSize;
$i++;
}
}
$avg = $sum / $i;
 
echo "Average: " . $avg;
 
### Linq: ###
 
$avgL = Linq::from($files)
->select(function($x) { return filesize($x); })
->where(function($x) { return $x > 1024; })
->skip(2)
->take(4)
->average();
 
echo "<br/><br>Average Linq: " . $avgL;
/vendor/fusonic/linq/examples/4-order.php
@@ -0,0 +1,38 @@
<?php
 
require_once __DIR__ . '/../vendor/autoload.php'; // Autoload files using Composer autoload
 
use Fusonic\Linq\Linq;
$files = glob("/tmp/*");
 
// Sort all files in a directory by filsize in descending order
 
### Plain PHP: ###
$data = array();
foreach($files as $file) {
$currentSize = filesize($file);
$data[] = array("name" => $file, "size" => $currentSize);
}
 
uasort($data, function($a, $b) {
$as = $a['size'];
$bs = $b['size'];
if($as == $bs) { return 0; }
else return $as < $bs ? 1 : -1;
});
 
foreach($data as $x)
{
echo $x['name'] . " " . $x['size'] . "<br>";
}
 
### Linq: ###
 
echo "<br/><br> Linq: <br /><br>";
 
$linq = Linq::from($files)
->select(function($x) { return array("name" => $x, "size" => filesize($x)); })
->orderByDescending(function($x) { return $x['size']; })
->each(function($x) {
echo $x['name'] . " " . $x['size'] . "<br>";
});
/vendor/fusonic/linq/examples/5-grouping.php
@@ -0,0 +1,54 @@
<?php
 
require_once __DIR__ . '/../vendor/autoload.php'; // Autoload files using Composer autoload
 
use Fusonic\Linq\Linq;
$files = glob("/tmp/*");
 
// Group all files by its filesize.
 
### Plain PHP: ###
$data = array();
foreach($files as $file) {
$currentSize = filesize($file);
$data[] = array("name" => $file, "size" => $currentSize);
}
 
uasort($data, function($a, $b) {
$as = $a['size'];
$bs = $b['size'];
if($as == $bs) { return 0; }
else return $as < $bs ? 1 : -1;
});
 
$grouped = array();
foreach($data as $x)
{
if(isset($grouped[$x['size']])) {
$grouped[$x['size']][] = $x;
}
else {
$grouped[$x['size']] = array($x);
}
}
 
foreach($grouped as $key => $value) {
echo $key . " (" . count($value) . ")" . "<br />";
foreach($value as $file) {
echo " -" . $file["name"] . "<br>";
}
};
 
### Linq: ###
 
echo "<br/><br> Linq: <br /><br>";
 
$linq = Linq::from($files)
->select(function($x) { return array("name" => $x, "size" => filesize($x)); })
->orderByDescending(function($x) { return $x['size']; })
->groupBy(function($x) { return $x['size']; })
->orderByDescending(function($x) { return $x->count(); })
->each(function($x) {
echo $x->key() . " (" . $x->count() . ")" . "<br />";
$x->each(function($y) { echo " -" . $y["name"] . "<br>"; });
});
/vendor/fusonic/linq/examples/6-typesafe.php
@@ -0,0 +1,27 @@
<?php
/*
* One important design goal was the principle of the least surprise.
* As PHP is a fully dynamic language with nearly no type-safety, it is common to shoot yourself into the foot because of accidentally mixing up incompatible types.
* We protect you from these programing errors by asserting that every callback functions you supply to the library must return a correctly typed value.
* In addition, every supported aggregate function will throw an exception if you are accidentally mixing up incompatible types.
*
* This means that we made this library totally predictable in what it does, and verified that every function has its defined exceptions
* which are thrown when certain operations fail, or if certain types are not correct.
*
*/
echo "<pre>";
 
require_once __DIR__ . '/../vendor/autoload.php'; // Autoload files using Composer autoload
 
use Fusonic\Linq\Linq;
 
/* Throws an UnexpectedValueException if the
provided callback function does not return a boolean */
Linq::from(array("1", "1"))
->where(function($x) { return "NOT A BOOLEAN"; });
 
 
/* Throws an UnexpectedValueException if one of the values
is not convertible to a numeric value:*/
Linq::from(array(1, 2, "Not a numeric value"))
->sum();
/vendor/fusonic/linq/src/Fusonic/Linq/GroupedLinq.php
@@ -0,0 +1,35 @@
<?php
 
/*
* This file is part of Fusonic-linq.
*
* (c) Fusonic GmbH
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
 
namespace Fusonic\Linq;
 
use Fusonic\Linq\Linq;
 
/**
* Class GroupedLinq
* Represents a Linq object that groups together other elements with a group key().
* @package Fusonic\Linq
*/
class GroupedLinq extends Linq
{
private $groupKey;
 
public function __construct($groupKey, $dataSource)
{
parent::__construct($dataSource);
$this->groupKey = $groupKey;
}
 
public function key()
{
return $this->groupKey;
}
}
/vendor/fusonic/linq/src/Fusonic/Linq/Helper/LinqHelper.php
@@ -0,0 +1,67 @@
<?php
 
/*
* This file is part of Fusonic-linq.
* https://github.com/fusonic/fusonic-linq
*
* (c) Fusonic GmbH
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
 
namespace Fusonic\Linq\Helper;
 
use ArrayIterator;
use InvalidArgumentException;
use UnexpectedValueException;
 
class LinqHelper
{
const MODE_ASSERT = 'MODE_ASSERT';
const MODE_NULL = 'MODE_NULL';
 
const LINQ_ORDER_ASC = 'asc';
const LINQ_ORDER_DESC = 'desc';
 
const LINQ_ORDER_TYPE_NUMERIC = 1;
const LINQ_ORDER_TYPE_ALPHANUMERIC = 2;
const LINQ_ORDER_TYPE_DATETIME = 3;
 
public static function getBoolOrThrowException($returned)
{
if (!is_bool($returned)) {
throw new UnexpectedValueException("Return type of filter func must be boolean.");
}
return $returned;
}
 
public static function assertArgumentIsIterable($param, $argumentName)
{
if (!self::isIterable($param)) {
throw new InvalidArgumentException("Argument must be an array, or implement either the \IteratorAggregate or \Iterator interface. ArgumentName = " . $argumentName);
}
}
 
public static function getIteratorOrThrow($value)
{
if (is_array($value)) {
return new ArrayIterator($value);
}
else if($value instanceof \IteratorAggregate) {
return $value->getIterator();
}
else if($value instanceof \Iterator) {
return $value;
}
 
throw new \UnexpectedValueException("Value must be an array, or implement either the \IteratorAggregate or \Iterator interface");
}
 
public static function isIterable($param)
{
return is_array($param)
|| $param instanceof \IteratorAggregate
|| $param instanceof \Iterator;
}
}
/vendor/fusonic/linq/src/Fusonic/Linq/Iterator/DistinctIterator.php
@@ -0,0 +1,62 @@
<?php
 
/*
* This file is part of Fusonic-linq.
* https://github.com/fusonic/fusonic-linq
*
* (c) Fusonic GmbH
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
 
namespace Fusonic\Linq\Iterator;
 
use Iterator;
 
class DistinctIterator extends \IteratorIterator
{
private $iterator;
private $distinct;
 
public function __construct(Iterator $iterator)
{
$this->iterator = $iterator;
}
 
public function current()
{
return $this->distinct->current();
}
 
public function next()
{
$this->distinct->next();
}
 
public function key()
{
return $this->distinct->key();
}
 
public function valid()
{
return $this->distinct->valid();
}
 
public function rewind()
{
if ($this->distinct === null) {
$this->getDistincts();
}
 
$this->distinct->rewind();
}
 
private function getDistincts()
{
$data = iterator_to_array($this->iterator);
$distinct = array_unique($data);
$this->distinct = new \ArrayIterator($distinct);
}
}
/vendor/fusonic/linq/src/Fusonic/Linq/Iterator/ExceptIterator.php
@@ -0,0 +1,65 @@
<?php
 
/*
* This file is part of Fusonic-linq.
* https://github.com/fusonic/fusonic-linq
*
* (c) Fusonic GmbH
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
 
namespace Fusonic\Linq\Iterator;
 
use ArrayIterator;
use Iterator;
 
class ExceptIterator implements Iterator
{
private $first;
private $second;
private $result;
 
public function __construct(Iterator $first, Iterator $second)
{
$this->first = $first;
$this->second = $second;
}
 
public function current()
{
return $this->result->current();
}
 
public function next()
{
$this->result->next();
}
 
public function key()
{
return $this->result->key();
}
 
public function valid()
{
return $this->result->valid();
}
 
public function rewind()
{
if ($this->result === null) {
$this->getResult();
}
 
$this->result->rewind();
}
 
private function getResult()
{
$firstArray = iterator_to_array($this->first);
$secondArray = iterator_to_array($this->second);
$this->result = new ArrayIterator(array_diff($firstArray, $secondArray));
}
}
/vendor/fusonic/linq/src/Fusonic/Linq/Iterator/GroupIterator.php
@@ -0,0 +1,74 @@
<?php
 
/*
* This file is part of Fusonic-linq.
* https://github.com/fusonic/fusonic-linq
*
* (c) Fusonic GmbH
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
 
namespace Fusonic\Linq\Iterator;
 
use Iterator;
use ArrayIterator;
use Fusonic\Linq\GroupedLinq;
 
class GroupIterator implements Iterator
{
private $iterator;
private $grouped;
private $keySelector;
 
public function __construct($iterator, $keySelector)
{
$this->iterator = $iterator;
$this->keySelector = $keySelector;
}
 
public function current()
{
$current = $this->grouped->current();
return new GroupedLinq($current['key'], new \ArrayIterator($current['values']));
}
 
public function next()
{
$this->grouped->next();
}
 
public function key()
{
return $this->grouped->key();
}
 
public function valid()
{
return $this->grouped->valid();
}
 
public function rewind()
{
if ($this->grouped === null) {
$this->doGroup();
}
 
$this->grouped->rewind();
}
 
private function doGroup()
{
$keySelector = $this->keySelector;
$this->grouped = new \ArrayIterator(array());
foreach ($this->iterator as $value) {
$key = $keySelector($value);
if (!isset($this->grouped[$key])) {
$this->grouped[$key] = array('key' => $key, 'values'=> array());
}
 
$this->grouped[$key]['values'][] = $value;
}
}
}
/vendor/fusonic/linq/src/Fusonic/Linq/Iterator/IntersectIterator.php
@@ -0,0 +1,65 @@
<?php
 
/*
* This file is part of Fusonic-linq.
* https://github.com/fusonic/fusonic-linq
*
* (c) Fusonic GmbH
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
 
namespace Fusonic\Linq\Iterator;
 
use ArrayIterator;
use Iterator;
 
class IntersectIterator implements Iterator
{
private $first;
private $second;
private $intersections;
 
public function __construct(Iterator $first, Iterator $second)
{
$this->first = $first;
$this->second = $second;
}
 
public function current()
{
return $this->intersections->current();
}
 
public function next()
{
$this->intersections->next();
}
 
public function key()
{
return $this->intersections->key();
}
 
public function valid()
{
return $this->intersections->valid();
}
 
public function rewind()
{
if ($this->intersections === null) {
$this->calcIntersections();
}
 
$this->intersections->rewind();
}
 
private function calcIntersections()
{
$firstArray = iterator_to_array($this->first);
$secondArray = iterator_to_array($this->second);
$this->intersections = new ArrayIterator(array_intersect($firstArray, $secondArray));
}
}
/vendor/fusonic/linq/src/Fusonic/Linq/Iterator/OfTypeIterator.php
@@ -0,0 +1,94 @@
<?php
/*
* This file is part of Fusonic-linq.
* https://github.com/fusonic/fusonic-linq
*
* (c) Burgy Benjamin <benjamin.burgy@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
 
namespace Fusonic\Linq\Iterator;
 
use FilterIterator;
use Fusonic\Linq\Helper;
use Iterator;
 
/**
* Iterator for filtering the Linq query with a specified <b>type</b>.
* @package Fusonic\Linq\Iterator
*/
final class OfTypeIterator
extends
\FilterIterator
{
/**
* @var callable $acceptCallback
*/
private $acceptCallback;
 
/**
* Initializes an instance of <b>OfTypeIterator</b>.
*
* @param Iterator $iterator
* @param string $type
*/
public function __construct(Iterator $iterator, $type)
{
parent::__construct($iterator);
 
switch (strtolower($type))
{
case 'int':
case 'integer':
$this->acceptCallback = function ($current)
{
return is_int($current);
};
break;
case 'float':
case 'double':
$this->acceptCallback = function ($current)
{
return is_float($current);
};
break;
case 'string':
$this->acceptCallback = function ($current)
{
return is_string($current);
};
break;
case 'bool':
case 'boolean':
$this->acceptCallback = function ($current)
{
return is_bool($current);
};
break;
 
default:
$this->acceptCallback = function ($current) use ($type)
{
return $current instanceof $type;
};
}
}
 
/**
* (PHP 5 &gt;= 5.1.0)<br/>
* Check whether the current element of the iterator is acceptable
* @link http://php.net/manual/en/filteriterator.accept.php
* @return bool true if the current element is acceptable, otherwise false.
*/
public function accept()
{
/** @var mixed $current */
$current = $this->current();
/** @var callable $func */
$func = $this->acceptCallback;
 
return $func($current);
}
}
/vendor/fusonic/linq/src/Fusonic/Linq/Iterator/OrderIterator.php
@@ -0,0 +1,113 @@
<?php
 
/*
* This file is part of Fusonic-linq.
* https://github.com/fusonic/fusonic-linq
*
* (c) Fusonic GmbH
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
 
namespace Fusonic\Linq\Iterator;
 
use Iterator;
use ArrayIterator;
use Fusonic\Linq;
use Fusonic\Linq\Helper;
 
class OrderIterator implements Iterator
{
private $iterator;
private $direction;
private $orderedIterator;
private $orderKeyFunc;
 
public function __construct(Iterator $items, $orderKeyFunc, $direction)
{
$this->iterator = $items;
$this->direction = $direction;
$this->orderKeyFunc = $orderKeyFunc;
}
 
public function current()
{
return $this->orderedIterator->current();
}
 
public function next()
{
$this->orderedIterator->next();
}
 
public function key()
{
return $this->orderedIterator->key();
}
 
public function valid()
{
return $this->orderedIterator->valid();
}
 
public function rewind()
{
if ($this->orderedIterator == null) {
$this->orderItems();
}
$this->orderedIterator->rewind();
}
 
public function orderItems()
{
$orderKeyFunc = $this->orderKeyFunc;
$direction = $this->direction;
 
$itemIterator = $this->iterator;
$itemIterator->rewind();
if (!$itemIterator->valid()) {
$this->orderedIterator = new ArrayIterator();
return;
}
 
$firstOrderKey = $orderKeyFunc($itemIterator->current());
 
$sortType = Helper\LinqHelper::LINQ_ORDER_TYPE_NUMERIC;
 
if ($firstOrderKey instanceof \DateTime) {
$sortType = Helper\LinqHelper::LINQ_ORDER_TYPE_DATETIME;
} elseif (!is_numeric($firstOrderKey)) {
$sortType = Helper\LinqHelper::LINQ_ORDER_TYPE_ALPHANUMERIC;
}
 
$keyMap = array();
$valueMap = array();
 
foreach ($itemIterator as $value) {
$orderKey = $orderKeyFunc != null ? $orderKeyFunc($value) : $value;
if ($sortType == Helper\LinqHelper::LINQ_ORDER_TYPE_DATETIME) {
$orderKey = $orderKey->getTimeStamp();
}
$keyMap[] = $orderKey;
$valueMap[] = $value;
}
 
if ($sortType == Helper\LinqHelper::LINQ_ORDER_TYPE_DATETIME) {
$sortType = Helper\LinqHelper::LINQ_ORDER_TYPE_NUMERIC;
}
 
if ($direction == Helper\LinqHelper::LINQ_ORDER_ASC) {
asort($keyMap, $sortType == Helper\LinqHelper::LINQ_ORDER_TYPE_NUMERIC ? SORT_NUMERIC : SORT_LOCALE_STRING);
} else {
arsort($keyMap, $sortType == Helper\LinqHelper::LINQ_ORDER_TYPE_NUMERIC ? SORT_NUMERIC : SORT_LOCALE_STRING);
}
 
$sorted = new ArrayIterator(array());
foreach ($keyMap as $key => $value) {
$sorted[] = $valueMap[$key];
}
 
$this->orderedIterator = $sorted;
}
}
/vendor/fusonic/linq/src/Fusonic/Linq/Iterator/SelectIterator.php
@@ -0,0 +1,37 @@
<?php
 
/*
* This file is part of Fusonic-linq.
* https://github.com/fusonic/fusonic-linq
*
* (c) Fusonic GmbH
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
 
namespace Fusonic\Linq\Iterator;
 
use InvalidArgumentException;
use Iterator;
 
class SelectIterator extends \IteratorIterator
{
private $selector;
 
public function __construct(Iterator $iterator, $selector)
{
parent::__construct($iterator);
if ($selector === null) {
throw new InvalidArgumentException("Selector must not be null.");
}
 
$this->selector = $selector;
}
 
public function current()
{
$selector = $this->selector;
return $selector(parent::current());
}
}
/vendor/fusonic/linq/src/Fusonic/Linq/Iterator/SelectManyIterator.php
@@ -0,0 +1,84 @@
<?php
 
/*
* This file is part of Fusonic-linq.
* https://github.com/fusonic/fusonic-linq
*
* (c) Fusonic GmbH
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
 
namespace Fusonic\Linq\Iterator;
 
use Iterator;
use Fusonic\Linq\Helper;
 
class SelectManyIterator implements Iterator
{
private $iterator;
private $currentIterator;
private $key = 0;
 
public function __construct(Iterator $iterator)
{
$this->iterator = $iterator;
}
 
public function current()
{
if ($this->currentIterator != null) {
return $this->currentIterator->current();
}
 
return null;
}
 
public function next()
{
if ($this->currentIterator != null) {
$this->currentIterator->next();
 
if (!$this->currentIterator->valid()) {
$this->iterator->next();
if ($this->iterator->valid()) {
$this->currentIterator = Helper\LinqHelper::getIteratorOrThrow($this->iterator->current());
if ($this->currentIterator != null) {
$this->currentIterator->rewind();
$this->key++;
}
}
} else {
$this->key++;
}
}
}
 
public function key()
{
return $this->key;
}
 
public function valid()
{
$current = $this->currentIterator;
return $current != null && $current->valid();
}
 
public function rewind()
{
$this->iterator->rewind();
if ($this->iterator->valid()) {
$current = $this->iterator->current();
$this->currentIterator = Helper\LinqHelper::getIteratorOrThrow($current);
if ($this->currentIterator != null) {
$this->currentIterator->rewind();
}
} else {
$this->currentIterator = null;
}
 
$this->key = 0;
}
}
/vendor/fusonic/linq/src/Fusonic/Linq/Iterator/WhereIterator.php
@@ -0,0 +1,34 @@
<?php
 
/*
* This file is part of Fusonic-linq.
* https://github.com/fusonic/fusonic-linq
*
* (c) Fusonic GmbH
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
 
namespace Fusonic\Linq\Iterator;
 
use Iterator;
use Fusonic\Linq\Helper;
 
class WhereIterator extends \FilterIterator
{
private $func;
 
public function __construct(Iterator $iterator, $func)
{
parent::__construct($iterator);
$this->func = $func;
}
 
public function accept()
{
$func = $this->func;
$current = $this->current();
return Helper\LinqHelper::getBoolOrThrowException($func($current));
}
}
/vendor/fusonic/linq/src/Fusonic/Linq/Linq.php
@@ -0,0 +1,732 @@
<?php
 
/*
* This file is part of Fusonic-linq.
*
* (c) Fusonic GmbH
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
 
namespace Fusonic\Linq;
 
use Countable;
use Fusonic\Linq\Iterator\ExceptIterator;
use Fusonic\Linq\Iterator\DistinctIterator;
use Fusonic\Linq\Iterator\GroupIterator;
use Fusonic\Linq\Iterator\IntersectIterator;
use Fusonic\Linq\Iterator\OfTypeIterator;
use Fusonic\Linq\Iterator\OrderIterator;
use Fusonic\Linq\Iterator\SelectIterator;
use Fusonic\Linq\Iterator\SelectManyIterator;
use Fusonic\Linq\Iterator\WhereIterator;
use Fusonic\Linq\Helper\LinqHelper;
use IteratorAggregate;
use Traversable;
use UnexpectedValueException;
use InvalidArgumentException;
use OutOfRangeException;
 
/**
* Linq is a simple, powerful and consistent library for querying, projecting and aggregating data in php.
*
* @author David Roth <david.roth@fusonic.net>.
*/
class Linq implements IteratorAggregate, Countable
{
private $iterator;
 
/**
* Creates a new Linq object using the provided dataSource.
*
* @param array|\Iterator|IteratorAggregate $dataSource A Traversable sequence as data source.
*/
public function __construct($dataSource)
{
LinqHelper::assertArgumentIsIterable($dataSource, "dataSource");
$dataSource = LinqHelper::getIteratorOrThrow($dataSource);
 
$this->iterator = $dataSource;
}
 
/**
* Creates a new Linq object using the provided dataDataSource.
* This is the recommended way for getting a new Linq instance.
*
* @param array|\Iterator|IteratorAggregate $dataSource A Traversable sequence as data source.
* @return Linq
*/
public static function from($dataSource)
{
return new Linq($dataSource);
}
 
/**
* Generates a sequence of integral numbers within a specified range.
*
* @param $start The value of the first integer in the sequence.
* @param $count The number of sequential integers to generate.
* @return Linq An sequence that contains a range of sequential int numbers.
* @throws \OutOfRangeException
*/
public static function range($start, $count)
{
if ($count < 0) {
throw new OutOfRangeException('$count must be not be negative.');
}
 
return new Linq(range($start, $start + $count - 1));
}
 
/**
* Filters the Linq object according to func return result.
*
* @param callback $func A func that returns boolean
* @return Linq Filtered results according to $func
*/
public function where($func)
{
return new Linq(new WhereIterator($this->iterator, $func));
}
 
/**
* Filters the Linq object according to type.
*
* @param string $type
*
* @return Linq Filtered results according to $func
*/
public function ofType($type)
{
return new Linq(new OfTypeIterator($this->iterator, $type));
}
 
/**
* Bypasses a specified number of elements and then returns the remaining elements.
*
* @param int $count The number of elements to skip before returning the remaining elements.
* @return Linq A Linq object that contains the elements that occur after the specified index.
*/
public function skip($count)
{
// If its an array iterator we must check the arrays bounds are greater than the skip count.
// This is because the LimitIterator will use the seek() method which will throw an exception if $count > array.bounds.
$innerIterator = $this->iterator;
if ($innerIterator instanceof \ArrayIterator) {
if ($count >= $innerIterator->count()) {
return new Linq(array());
}
}
 
return new Linq(new \LimitIterator($innerIterator, $count, -1));
}
 
/**
* Returns a specified number of contiguous elements from the start of a sequence
*
* @param int $count The number of elements to return.
* @return Linq A Linq object that contains the specified number of elements from the start.
*/
public function take($count)
{
if ($count == 0) {
return new Linq(array());
}
 
return new Linq(new \LimitIterator($this->iterator, 0, $count));
}
 
/**
* Applies an accumulator function over a sequence.
* The aggregate method makes it simple to perform a calculation over a sequence of values.
* This method works by calling $func one time for each element.
* The first element of source is used as the initial aggregate value if $seed parameter is not specified.
* If $seed is specified, this value will be used as the first value.
*
* @param callback $func An accumulator function to be invoked on each element.
* @param mixed $seed
* @throws \RuntimeException if the input sequence contains no elements.
* @return mixed Returns the final result of $func.
*/
public function aggregate($func, $seed = null)
{
$result = null;
$first = true;
 
if ($seed !== null) {
$result = $seed;
$first = false;
}
 
foreach ($this->iterator as $current) {
if (!$first) {
$result = $func($result, $current);
} else {
$result = $current;
$first = false;
}
}
if ($first) {
throw new \RuntimeException("The input sequence contains no elements.");
}
return $result;
}
 
/**
* Splits the sequence in chunks according to $chunksize.
*
* @param $chunksize Specifies how many elements are grouped together per chunk.
* @throws \InvalidArgumentException
* @return Linq
*/
public function chunk($chunksize)
{
if ($chunksize < 1) {
throw new \InvalidArgumentException("chunksize", $chunksize);
}
 
$i = -1;
return $this->select(
function ($x) use (&$i) {
$i++;
return array("index" => $i, "value" => $x);
}
)
->groupBy(
function ($pair) use ($chunksize) {
return $pair["index"] / $chunksize;
}
)
->select(
function (GroupedLinq $group) {
return $group->select(
function ($v) {
return $v["value"];
}
);
}
);
}
 
/**
* Determines whether all elements satisfy a condition.
*
* @param callback $func A function to test each element for a condition.
* @return bool True if every element passes the test in the specified func, or if the sequence is empty; otherwise, false.
*/
public function all($func)
{
foreach ($this->iterator as $current) {
$match = LinqHelper::getBoolOrThrowException($func($current));
if (!$match) {
return false;
}
}
return true;
}
 
/**
* Determines whether any element exists or satisfies a condition by invoking $func.
*
* @param callback $func A function to test each element for a condition or NULL to determine if any element exists.
* @return bool True if no $func given and the source sequence contains any elements or True if any elements passed the test in the specified func; otherwise, false.
*/
public function any($func = null)
{
foreach ($this->iterator as $current) {
if ($func === null) {
return true;
}
 
$match = LinqHelper::getBoolOrThrowException($func($current));
if ($match) {
return true;
}
}
return false;
}
 
/**
* Counts the elements of this Linq sequence.
* @return int
*/
public function count()
{
if ($this->iterator instanceof Countable) {
return $this->iterator->count();
}
 
return iterator_count($this->iterator);
}
 
/**
* Computes the average of all numeric values. Uses $func to obtain the value on each element.
*
* @param callback $func A func that returns any numeric type (int, float etc.)
* @throws \UnexpectedValueException if an item of the sequence is not a numeric value.
* @return double Average of items
*/
public function average($func = null)
{
$resultTotal = 0;
$itemCount = 0;
 
$source = $this->getSelectIteratorOrInnerIterator($func);
 
foreach ($source as $item) {
if (!is_numeric($item)) {
throw new UnexpectedValueException("Cannot calculate an average on a none numeric value");
}
 
$resultTotal += $item;
$itemCount++;
}
return $itemCount == 0 ? 0 : ($resultTotal / $itemCount);
}
 
/**
* Sorts the elements in ascending order according to a key provided by $func.
*
* @param callback $func A function to extract a key from an element.
* @return Linq A new Linq instance whose elements are sorted ascending according to a key.
*/
public function orderBy($func)
{
return $this->order($func, LinqHelper::LINQ_ORDER_ASC);
}
 
/**
* Sorts the elements in descending order according to a key provided by $func.
*
* @param callback $func A function to extract a key from an element.
* @return Linq A new Linq instance whose elements are sorted descending according to a key.
*/
public function orderByDescending($func)
{
return $this->order($func, LinqHelper::LINQ_ORDER_DESC);
}
 
private function order($func, $direction = LinqHelper::LINQ_ORDER_ASC)
{
return new Linq(new OrderIterator($this->iterator, $func, $direction));
}
 
/**
* Gets the sum of all items or by invoking a transform function on each item to get a numeric value.
*
* @param callback $func A func that returns any numeric type (int, float etc.) from the given element, or NULL to use the element itself.
* @throws \UnexpectedValueException if any element is not a numeric value.
* @return double The sum of all items.
*/
public function sum($func = null)
{
$sum = 0;
$iterator = $this->getSelectIteratorOrInnerIterator($func);
foreach ($iterator as $value) {
if (!is_numeric($value)) {
throw new UnexpectedValueException("sum() only works on numeric values.");
}
 
$sum += $value;
}
return $sum;
}
 
/**
* Gets the minimum item value of all items or by invoking a transform function on each item to get a numeric value.
*
* @param callback $func A func that returns any numeric type (int, float etc.) from the given element, or NULL to use the element itself.
* @throws \RuntimeException if the sequence contains no elements
* @throws \UnexpectedValueException
* @return double Minimum item value
*/
public function min($func = null)
{
$min = null;
$iterator = $this->getSelectIteratorOrInnerIterator($func);
foreach ($iterator as $value) {
if (!is_numeric($value) && !is_string($value) && !($value instanceof \DateTime)) {
throw new UnexpectedValueException("min() only works on numeric values, strings and DateTime objects.");
}
 
if (is_null($min)) {
$min = $value;
} elseif ($min > $value) {
$min = $value;
}
}
 
if ($min === null) {
throw new \RuntimeException("Cannot calculate min() as the Linq sequence contains no elements.");
}
 
return $min;
}
 
/**
* Returns the maximum item value according to $func
*
* @param callback $func A func that returns any numeric type (int, float etc.)
* @throws \RuntimeException if the sequence contains no elements
* @throws \UnexpectedValueException if any element is not a numeric value or a string.
* @return double Maximum item value
*/
public function max($func = null)
{
$max = null;
$iterator = $this->getSelectIteratorOrInnerIterator($func);
foreach ($iterator as $value) {
if (!is_numeric($value) && !is_string($value) && !($value instanceof \DateTime)) {
throw new UnexpectedValueException("max() only works on numeric values, strings and DateTime objects.");
}
 
if (is_null($max)) {
$max = $value;
} elseif ($max < $value) {
$max = $value;
}
}
 
if ($max === null) {
throw new \RuntimeException("Cannot calculate max() as the Linq sequence contains no elements.");
}
 
return $max;
}
 
/**
* Projects each element into a new form by invoking the selector function.
*
* @param callback $func A transform function to apply to each element.
* @return Linq A new Linq object whose elements are the result of invoking the transform function on each element of the original Linq object.
*/
public function select($func)
{
return new Linq(new SelectIterator($this->iterator, $func));
}
 
/**
* Projects each element of a sequence to a new Linq and flattens the resulting sequences into one sequence.
*
* @param callback $func A func that returns a sequence (array, Linq, Iterator).
* @throws \UnexpectedValueException if an element is not a traversable sequence.
* @return Linq A new Linq object whose elements are the result of invoking the one-to-many transform function on each element of the input sequence.
*/
public function selectMany($func)
{
return new Linq(new SelectManyIterator(new SelectIterator($this->iterator, $func)));
}
 
/**
* Performs the specified action on each element of the Linq sequence and returns the Linq sequence.
* @param callback $func A func that will be evaluated for each item in the linq sequence.
* @return Linq The original Linq sequence that was used to perform the foreach.
*/
public function each($func)
{
foreach ($this->iterator as $item) {
$func($item);
}
return $this;
}
 
/**
* Determines whether a sequence contains a specified element.
* This function will use php strict comparison (===). If you need custom comparison use the Linq::any($func) method.
*
* @param mixed $value The value to locate in the sequence.
* @return bool True if $value is found within the sequence; otherwise false.
*/
public function contains($value)
{
return $this->any(
function ($x) use ($value) {
return $x === $value;
}
);
}
 
/**
* Concatenates this Linq object with the given sequence.
*
* @param array|\Iterator $second A sequence which will be concatenated with this Linq object.
* @throws InvalidArgumentException if the given sequence is not traversable.
* @return Linq A new Linq object that contains the concatenated elements of the input sequence and the original Linq sequence.
*/
public function concat($second)
{
LinqHelper::assertArgumentIsIterable($second, "second");
 
$allItems = new \ArrayIterator(array($this->iterator, $second));
 
return new Linq(new SelectManyIterator($allItems));
}
 
/**
* Returns distinct item values of this
*
* @param callback $func
* @return Linq Distinct item values of this
*/
public function distinct($func = null)
{
return new Linq(new DistinctIterator($this->getSelectIteratorOrInnerIterator($func)));
}
 
/**
* Intersects the Linq sequence with second Iterable sequence.
*
* @param \Iterator|array An iterator to intersect with:
* @return Linq intersected items
*/
public function intersect($second)
{
LinqHelper::assertArgumentIsIterable($second, "second");
return new Linq(new IntersectIterator($this->iterator, LinqHelper::getIteratorOrThrow($second)));
}
 
/**
* Returns all elements except the ones of the given sequence.
*
* @param array|\Iterator $second
* @return Linq Returns all items of this not occuring in $second
*/
public function except($second)
{
LinqHelper::assertArgumentIsIterable($second, "second");
return new Linq(new ExceptIterator($this->iterator, LinqHelper::getIteratorOrThrow($second)));
}
 
/**
* Returns the element at a specified index.
* This method throws an exception if index is out of range.
* To instead return NULL when the specified index is out of range, use the elementAtOrNull method.
*
* @throws \OutOfRangeException if index is less than 0 or greater than or equal to the number of elements in the sequence.
* @param int $index
* @return mixed Item at $index
*/
public function elementAt($index)
{
return $this->getValueAt($index, true);
}
 
/**
* Returns the element at a specified index or NULL if the index is out of range.
*
* @param $index
* @return mixed Item at $index
*/
public function elementAtOrNull($index)
{
return $this->getValueAt($index, false);
}
 
private function getValueAt($index, $throwEx)
{
$i = 0;
foreach ($this->iterator as $value) {
if ($i == $index) {
return $value;
}
$i++;
}
 
if ($throwEx) {
throw new OutOfRangeException("Index is less than 0 or greater than or equal to the number of elements in the sequence.");
}
 
return null;
}
 
/**
* Groups the object according to the $func generated key
*
* @param callback $keySelector a func that returns an item as key, item can be any type.
* @return GroupedLinq
*/
public function groupBy($keySelector)
{
return new Linq(new GroupIterator($this->iterator, $keySelector));
}
 
/**
* Returns the last element that satisfies a specified condition.
* @throws \RuntimeException if no element satisfies the condition in predicate or the source sequence is empty.
*
* @param callback $func a func that returns boolean.
* @return Object Last item in this
*/
public function last($func = null)
{
return $this->getLast($func, true);
}
 
/**
* Returns the last element that satisfies a condition or NULL if no such element is found.
*
* @param callback $func a func that returns boolean.
* @return mixed
*/
public function lastOrNull($func = null)
{
return $this->getLast($func, false);
}
 
/**
* Returns the first element that satisfies a specified condition
* @throws \RuntimeException if no element satisfies the condition in predicate -or- the source sequence is empty / does not match any elements.
*
* @param callback $func a func that returns boolean.
* @return mixed
*/
public function first($func = null)
{
return $this->getFirst($func, true);
}
 
/**
* Returns the first element, or NULL if the sequence contains no elements.
*
* @param callback $func a func that returns boolean.
* @return mixed
*/
public function firstOrNull($func = null)
{
return $this->getFirst($func, false);
}
 
/**
* Returns the only element that satisfies a specified condition.
*
* @throws \RuntimeException if no element exists or if more than one element exists.
* @param callback $func a func that returns boolean.
* @return mixed
*/
public function single($func = null)
{
return $this->getSingle($func, true);
}
 
/**
* Returns the only element that satisfies a specified condition or NULL if no such element exists.
*
* @throws \RuntimeException if more than one element satisfies the condition.
* @param callback $func a func that returns boolean.
* @return mixed
*/
public function singleOrNull($func = null)
{
return $this->getSingle($func, false);
}
 
 
private function getWhereIteratorOrInnerIterator($func)
{
return $func === null ? $this->iterator : new WhereIterator($this->iterator, $func);
}
 
private function getSelectIteratorOrInnerIterator($func)
{
return $func === null ? $this->iterator : new SelectIterator($this->iterator, $func);
}
 
private function getSingle($func, $throw)
{
$source = $this->getWhereIteratorOrInnerIterator($func);
 
$count = 0;
$single = null;
 
foreach ($source as $stored) {
$count++;
 
if ($count > 1) {
throw new \RuntimeException("The input sequence contains more than 1 elements.");
}
 
$single = $stored;
}
 
if ($count == 0 && $throw) {
throw new \RuntimeException("The input sequence contains no matching element.");
}
 
return $single;
}
 
private function getFirst($func, $throw)
{
$source = $this->getWhereIteratorOrInnerIterator($func);
 
$count = 0;
$first = null;
 
foreach ($source as $stored) {
$count++;
$first = $stored;
break;
}
 
if ($count == 0 && $throw) {
throw new \RuntimeException("The input sequence contains no matching element.");
}
 
return $first;
}
 
private function getLast($func, $throw)
{
$source = $this->getWhereIteratorOrInnerIterator($func);
 
$count = 0;
$last = null;
 
foreach ($source as $stored) {
$count++;
$last = $stored;
}
 
if ($count == 0 && $throw) {
throw new \RuntimeException("The input sequence contains no matching element.");
}
 
return $last;
}
 
/**
* Creates an Array from this Linq object with key/value selector(s).
*
* @param callback $keySelector a func that returns the array-key for each element.
* @param callback $valueSelector a func that returns the array-value for each element.
*
* @return Array An array with all values.
*/
public function toArray($keySelector = null, $valueSelector = null)
{
if ($keySelector === null && $valueSelector === null) {
return iterator_to_array($this, false);
} elseif ($keySelector == null) {
return iterator_to_array(new SelectIterator($this->getIterator(), $valueSelector), false);
} else {
$array = array();
foreach ($this as $value) {
$key = $keySelector($value);
$array[$key] = $valueSelector == null ? $value : $valueSelector($value);
}
return $array;
}
}
 
/**
* Retrieves the iterator of this Linq class.
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php
* @return Traversable An instance of an object implementing <b>Iterator</b> or
* <b>Traversable</b>
*/
public function getIterator()
{
return $this->iterator;
}
}
/vendor/fusonic/linq/tests/Fusonic/Linq/Test/LinqTest.php
@@ -0,0 +1,1962 @@
<?php
/** Copyright (C) Fusonic GmbH (http://www.fusonic.net) 2013, All rights reserved. */
 
use Fusonic\Linq\Linq;
 
/**
* Interface for test purposes only.
*/
interface StubInterface
{
}
 
/**
* Class for test purposes only.
*/
final class Stub
implements
StubInterface
{
}
 
/**
* Class for test purposes only.
*/
final class StubWithoutInterface
{
}
 
class LinqTest extends PHPUnit_Framework_TestCase
{
const ExceptionName_UnexpectedValue = "UnexpectedValueException";
const ExceptionName_InvalidArgument = "InvalidArgumentException";
const ExceptionName_OutOfRange = "OutOfRangeException";
const ExceptionName_Runtime = "RuntimeException";
 
/** Returns the only element of a sequence that satisfies a specified condition, and throws an exception if more than one such element exists.
*/
public function testSingle_TestBehaviour()
{
// more than one
$items = array(1, 2);
$this->assertException(function() use ($items) {
Linq::from($items)->single();
});
 
// no matching elements
$items = array();
$this->assertException(function() use ($items) {
Linq::from($items)->single();
});
 
// OK
$items = array(77);
$this->assertSame(77, Linq::from($items)->single());
 
// With closure
 
// more than one
$items = array(1, 2);
$this->assertException(function() use ($items) {
Linq::from($items)->single(function($x) { return true; });
});
 
// no matching elements
// because of false closure
$this->assertException(function() use ($items) {
Linq::from($items)->single(function($x) { return false; });
});
 
// because of empty array
$items = array();
$this->assertException(function() use ($items) {
Linq::from($items)->single(function($x) { return true; });
});
 
// OK
$items = array(77);
$this->assertSame(77, Linq::from($items)->single(function($x) { return true; }));
}
 
public function testCount_ReturnsCorrectAmounts()
{
$items = array(1, 2);
$this->assertEquals(2, Linq::from($items)->count());
 
$items = array(1, 2);
$this->assertEquals(1, Linq::from($items)->where(function($x) {
return $x == 2;
})->count());
 
$items = array(1, 2);
$this->assertEquals(0, Linq::from($items)->where(function($x) {
return false;
})->count());
 
$items = array();
$this->assertEquals(0, Linq::from($items)->count());
}
 
/** Returns the only element of a sequence that satisfies a specified condition or a default value if no such element exists;
* this method throws an exception if more than one element satisfies the condition.
*/
public function testSingleOrNull_TestBehaviour()
{
// more than one
$items = array(1, 2);
$this->assertException(function() use ($items) {
Linq::from($items)->singleOrNull();
});
 
// no matching elements
$items = array();
$this->assertNull(Linq::from($items)->singleOrNull());
 
// OK
$items = array(77);
$this->assertSame(77, Linq::from($items)->singleOrNull());
 
// With closure
 
// more than one
$items = array(1, 2);
$this->assertException(function() use ($items) {
Linq::from($items)->singleOrNull(function($x) { return true; });
});
 
// no matching elements
// because of false closure
$this->assertNull(Linq::from($items)->singleOrNull(function($x) { return false; }));
 
// because of empty array
$items = array();
$this->assertNull(Linq::from($items)->singleOrNull());
 
// OK
$items = array(77);
$this->assertSame(77, Linq::from($items)->singleOrNull(function($x) { return true; }));
}
 
/** Returns the first element in a sequence that satisfies a specified condition.
*
* Exceptions:
* No element satisfies the condition in predicate.
-or-
The source sequence is empty.
*/
public function testFirst_TestBehaviour()
{
$a = new stdClass();
$a->value = "a";
 
$b1 = new stdClass();
$b1->value = "b";
 
$b2 = new stdClass();
$b2->value = "b";
 
$c = new stdClass();
$c->value = "c";
 
// more than one
$items = array($a, $b1, $b2, $c);
$this->assertSame($a, Linq::from($items)->first());
 
// no matching elements
$items = array();
$this->assertException(function() use ($items) {
Linq::from($items)->first();
});
 
$items = array($a);
$this->assertSame($a, Linq::from($items)->first());
 
// #### With closures ###
 
// more than one
$items = array($a, $b1, $b2, $c);
$this->assertSame($b1, Linq::from($items)->first(function($x) { return $x->value == "b"; }));
 
// no matching elements
// because of false closure
$this->assertException(function() use ($items) {
Linq::from($items)->first(function($x) { return false; });
});
 
// because of empty array
$items = array();
$this->assertException(function() use ($items) {
Linq::from($items)->first(function($x) { return true; });
});
 
// OK
$items = array($a);
$this->assertSame($a, Linq::from($items)->first(function($x) { return true; }));
}
 
/**
* Returns the first element of a sequence, or a default value if the sequence contains no elements.
*/
public function testFirstOrNull_DoesReturnTheFirstElement_OrNull_DoesNotThrowExceptions()
{
$a = new stdClass();
$a->value = "a";
 
$b1 = new stdClass();
$b1->value = "b";
 
$b2 = new stdClass();
$b2->value = "b";
 
$c = new stdClass();
$c->value = "c";
 
$items = array($a, $b1, $b2, $c);
$this->assertSame($a, Linq::from($items)->firstOrNull());
 
$items = array();
$this->assertNull(Linq::from($items)->firstOrNull());
 
// #### With closures ###
 
$items = array($a, $b1, $b2, $c);
$this->assertSame($b1, Linq::from($items)->firstOrNull(function($x) { return $x->value == "b"; }));
 
$items = array($a, $b1, $b2, $c);
$this->assertSame($c, Linq::from($items)->firstOrNull(function($x) { return $x->value == "c"; }));
 
$items = array();
$this->assertNull(Linq::from($items)->firstOrNull(function($x) { return true; }));
}
 
 
public function testLast_DoesReturnTheLastElement_OrThrowsExceptions()
{
$a = new stdClass();
$a->value = "a";
 
$b1 = new stdClass();
$b1->value = "b";
 
$b2 = new stdClass();
$b2->value = "b";
 
$c = new stdClass();
$c->value = "c";
 
// more than one
$items = array($a, $b1, $b2, $c);
$last = Linq::from($items)->last();
$this->assertSame($c, $last);
 
// no matching elements
$items = array();
$this->assertException(function() use ($items) {
Linq::from($items)->last();
});
 
$items = array($a);
$this->assertSame($a, Linq::from($items)->last());
 
// #### With closures ###
 
// more than one
$items = array($a, $b1, $b2, $c);
$this->assertSame($b2, Linq::from($items)->last(function($x) { return $x->value == "b"; }));
 
// no matching elements
// because of false closure
$this->assertException(function() use ($items) {
Linq::from($items)->last(function($x) { return false; });
});
 
// because of empty array
$items = array();
$this->assertException(function() use ($items) {
Linq::from($items)->last(function($x) { return true; });
});
 
// OK
$items = array($a);
$this->assertSame($a, Linq::from($items)->last(function($x) { return true; }));
}
 
public function testLastOrDefault_DoesReturnTheLastElement_OrNull_DoesNotThrowExceptions()
{
$a = new stdClass();
$a->value = "a";
 
$b1 = new stdClass();
$b1->value = "b";
 
$b2 = new stdClass();
$b2->value = "b";
 
$c = new stdClass();
$c->value = "c";
 
$items = array($a, $b1, $b2, $c);
$this->assertSame($c, Linq::from($items)->lastOrNull());
 
$items = array();
$this->assertNull(Linq::from($items)->lastOrNull());
 
// #### With closures ###
 
$items = array($a, $b1, $b2, $c);
$this->assertSame($c, Linq::from($items)->lastOrNull(function($x) { return true; }));
 
$items = array($a, $b1, $b2, $c);
$this->assertSame($b2, Linq::from($items)->lastOrNull(function($x) { return $x->value == "b"; }));
 
$items = array();
$this->assertNull(Linq::from($items)->lastOrNull(function($x) { return true; }));
}
 
public function testWhere_ReturnsOnlyValuesMatching()
{
$a = new stdClass();
$a->value = "a";
 
$b1 = new stdClass();
$b1->value = "b";
 
$b2 = new stdClass();
$b2->value = "b";
 
$c = new stdClass();
$c->value = "c";
 
$items = array($a, $b1, $b2, $c);
$matching = Linq::from($items)->where(function ($v) { return false; });
$this->assertTrue($matching instanceof Linq);
 
$matching = $matching->toArray();
 
$this->assertEquals(0, count($matching));
 
$matching = Linq::from($items)->where(function ($v) { return true; })->toArray();
$this->assertEquals(4, count($matching));
$this->assertTrue(in_array($a, (array)$matching));
$this->assertTrue(in_array($b1, (array)$matching));
$this->assertTrue(in_array($b2, (array)$matching));
$this->assertTrue(in_array($c, (array)$matching));
 
$matching = Linq::from($items)->where(function ($v) { return $v->value == "b"; })->toArray();
$this->assertEquals(2, count($matching));
$this->assertFalse(in_array($a, (array)$matching));
$this->assertTrue(in_array($b1, (array)$matching));
$this->assertTrue(in_array($b2, (array)$matching));
$this->assertFalse(in_array($c, (array)$matching));
}
 
public function testWhere_ThrowsExceptionIfPredicateDoesNotReturnABoolean()
{
$this->assertException(function()
{
$items = array("1", "2", "3");
$matching = Linq::from($items)->where(function ($v) { return "NOT A BOOLEAN"; });
$matching->toArray();
}, self::ExceptionName_UnexpectedValue);
}
 
public function testWhere_DoesLazyEvaluation()
{
$eval = false;
$items = array("1", "2", "3");
$matching = Linq::from($items)->where(function ($v) use(&$eval)
{
$eval = true;
return true;
});
 
$this->assertFalse($eval, "SelectMany did execute before iterating!");
$matching->toArray();
$this->assertTrue($eval);
}
 
public function testWhere_EmptySequence_ReturnsEmptySequence()
{
$items = array();
$matching = Linq::from($items)->where(function ($v)
{
return true;
});
 
$this->assertEquals(0, $matching->count());
$array = $matching->toArray();
$this->assertEquals(0, count($array));
}
 
public function testCountable_implementedSqlInterface()
{
$items = array(1,2,3);
 
$matching = Linq::from($items);
 
$this->assertEquals(3, count($matching));
$this->assertEquals(3, $matching->count());
}
 
public function testSkipWithTake_Combined_SkipAndTakeValuesByAmount()
{
$items = array("a", "b", "c", "d", "e", "f");
$matching = Linq::from($items)->skip(2)->take(0);
$this->assertEquals(0, $matching->count());
 
$matching = Linq::from($items)->skip(0)->take(0);
$this->assertEquals(0, $matching->count());
 
$matching = Linq::from($items)->skip(0)->take(2);
$this->assertEquals(2, $matching->count());
$array = $matching->toArray();
$this->assertEquals("a", $array[0]);
$this->assertEquals("b", $array[1]);
 
$matching = Linq::from($items)->skip(2)->take(2);
$this->assertEquals(2, $matching->count());
$array = $matching->toArray();
$this->assertEquals("c", $array[0]);
$this->assertEquals("d", $array[1]);
 
$matching = Linq::from($items)->skip(4)->take(99);
$this->assertEquals(2, $matching->count());
$array = $matching->toArray();
$this->assertEquals("e", $array[0]);
$this->assertEquals("f", $array[1]);
}
 
public function testTakeSkip_Combined_TakeAndSkipValuesByAmount()
{
$items = array("a", "b", "c", "d", "e", "f");
$matching = Linq::from($items)->take(0)->skip(0);
$this->assertEquals(0, $matching->count());
 
$matching = Linq::from($items)->take(2)->skip(2);
$this->assertEquals(0, $matching->count());
 
$matching = Linq::from($items)->take(2)->skip(0);
$this->assertEquals(2, $matching->count());
$array = $matching->toArray();
$this->assertEquals("a", $array[0]);
$this->assertEquals("b", $array[1]);
}
 
public function testSkip_SkipValuesByAmount()
{
$items = array("a", "b", "c", "d", "e", "f");
$matching = Linq::from($items)->skip(2);
$this->assertTrue($matching instanceof Linq);
 
$this->assertEquals(4, $matching->count());
$matching = $matching->toArray();
 
$this->assertTrue(in_array("c", $matching));
$this->assertTrue(in_array("d", $matching));
$this->assertTrue(in_array("e", $matching));
$this->assertTrue(in_array("f", $matching));
 
$items = array("a", "b", "c", "d", "e", "f");
 
$matching = Linq::from($items)->skip(0);
$this->assertEquals(6, $matching->count());
$array = $matching->toArray();
$this->assertEquals("a", $array[0]);
$this->assertEquals("b", $array[1]);
$this->assertEquals("c", $array[2]);
$this->assertEquals("d", $array[3]);
$this->assertEquals("e", $array[4]);
$this->assertEquals("f", $array[5]);
 
$matching = Linq::from($items)->skip(5);
$this->assertEquals(1, $matching->count());
$array = $matching->toArray();
$this->assertEquals("f", $array[0]);
 
$matching = Linq::from($items)->skip(6);
$this->assertEquals(0, $matching->count());
 
$matching = Linq::from($items)->skip(7);
$this->assertEquals(0, $matching->count());
 
// Test against empty sequence:
 
$matching = Linq::from(array())->skip(0);
$this->assertEquals(0, $matching->count());
 
$matching = Linq::from(array())->skip(6);
$this->assertEquals(0, $matching->count());
}
 
public function testTake_TakeValuesByAmount()
{
$items = array("a", "b", "c", "d", "e", "f");
$matching = Linq::from($items)->take(4);
$this->assertTrue($matching instanceof Linq);
 
$this->assertEquals(4, $matching->count());
$matching = $matching->toArray();
$this->assertTrue(in_array("a", $matching));
$this->assertTrue(in_array("b", $matching));
$this->assertTrue(in_array("c", $matching));
$this->assertTrue(in_array("d", $matching));
 
$matching = Linq::from($items)->take(0);
$this->assertEquals(0, $matching->count());
}
 
public function testAll_WorksCorrectly()
{
// All must always return true on empty sequences:
$items = array();
$all = Linq::from($items)->all(function($v) { return true; });
$this->assertTrue($all);
 
$all = Linq::from($items)->all(function($v) { return false; });
$this->assertTrue($all);
 
// Test with values:
$items = array("a", "b");
$all = Linq::from($items)->all(function($v) { return $v == "a"; });
$this->assertFalse($all);
 
$all = Linq::from($items)->all(function($v) { return $v == "a" || $v == "b"; });
$this->assertTrue($all);
}
 
public function testAny_WithFunc_CorrectResults()
{
// Any must always return false on empty sequences:
$items = array();
$any = Linq::from($items)->any(function($v) { return true; });
$this->assertFalse($any);
 
$any = Linq::from($items)->any(function($v) { return false; });
$this->assertFalse($any);
 
// Test with values:
$items = array("a", "b");
$any = Linq::from($items)->any(function($v) { return $v == "not existing"; });
$this->assertFalse($any);
 
$any = Linq::from($items)->any(function($v) { return $v == "a"; });
$this->assertTrue($any);
}
 
public function testAny_WithoutFunc_CorrectResults()
{
$items = array();
$this->assertFalse(Linq::from($items)->any());
 
$items = array("a");
$this->assertTrue(Linq::from($items)->any());
 
$items = array("a", "b", "c");
$this->assertTrue(Linq::from($items)->any());
}
 
public function testAverage_throwsExceptionIfClosureReturnsNotNumericValue()
{
$this->assertException(function() {
$items = array(2, new stdClass());
Linq::from($items)->average();
}, self::ExceptionName_UnexpectedValue);
 
$this->assertException(function() {
$items = array(2, "no numeric value");
Linq::from($items)->average();
}, self::ExceptionName_UnexpectedValue);
 
$this->assertException(function() {
$cls = new stdClass();
$cls->value = "no numeric value";
$items = array($cls);
Linq::from($items)->average(function($x) { return $x->value; });
}, self::ExceptionName_UnexpectedValue);
}
 
public function testAverage_CalculatesCorrectAverage()
{
$items = array(2, 4, 6);
$avg = Linq::from($items)->average();
$this->assertEquals(4, $avg);
 
$items = array(2.5, 2.5);
$avg = Linq::from($items)->average();
$this->assertEquals(2.5, $avg);
 
$items = array(2, "4", "6");
$avg = Linq::from($items)->average();
$this->assertEquals(4, $avg);
 
$items = array(2, 4, 6);
$avg = Linq::from($items)->average(function($v) { return 1; });
$this->assertEquals(1, $avg);
 
$items = array(2.5, 2.5);
$avg = Linq::from($items)->average(function($v) { return $v; });
$this->assertEquals(2.5, $avg);
 
$a = new stdClass();
$a->value = 2;
 
$b = new stdClass();
$b->value = "4";
 
$c = new stdClass();
$c->value = "6";
 
$items = array($a, $b, $c);
$avg = Linq::from($items)->average(function($v) { return $v->value; });
$this->assertEquals(4, $avg);
}
 
public function testOrderBy_NumericValues_WithSelector_ReturnsOrderedObjects()
{
$a = new stdClass(); $a->value = 77;
$b = new stdClass(); $b->value = 10;
$c = new stdClass(); $c->value = 20;
$items = array($a, $b, $c);
 
$ascending = Linq::from($items)->orderBy(function($x)
{
return $x->value;
});
$this->assertEquals(3, $ascending->count());
$items = $ascending->toArray();
 
$this->assertSame($b, $items[0]);
$this->assertSame($c, $items[1]);
$this->assertSame($a, $items[2]);
 
$a = new stdClass(); $a->value = 77;
$b = new stdClass(); $b->value = 10.44;
$c = new stdClass(); $c->value = 20;
$d = new stdClass(); $d->value = 20;
$items = array($a, $b, $c, $d);
 
$ascending = Linq::from($items)->orderBy(function($x) { return $x->value; });
$this->assertEquals(4, $ascending->count());
$items = $ascending->toArray();
 
$this->assertSame($b, $items[0]);
 
// It is not predictable in which order objects with the same order key are ordered, both positions are valid:
$pos1 = $items[1];
$pos2 = $items[2];
$this->assertTrue($pos1 === $d || $pos1 === $c);
$this->assertTrue($pos1 === $d || $pos1 === $c);
$this->assertNotsame($pos1, $pos2);
 
$this->assertSame($a, $items[3]);
}
 
public function testOrderBy_NumericValues_ReturnsCorrectOrders()
{
$items = array(77, 10, 20);
$ascending = Linq::from($items)->orderBy(function($x) { return $x; });
$this->assertTrue($ascending instanceof Linq);
 
$ascending = $ascending->toArray();
 
$this->assertEquals(10, $ascending[0]);
$this->assertEquals(20, $ascending[1]);
$this->assertEquals(77, $ascending[2]);
 
// Verify that original collection is unmodified:
$this->assertEquals(77, $items[0]);
$this->assertEquals(10, $items[1]);
$this->assertEquals(20, $items[2]);
 
$items = array(12.33, 8.21, 11.3, 8.21, 33);
$ascending = Linq::from($items)->orderBy(function($x) { return $x; });
 
$ascending = $ascending->toArray();
$this->assertEquals(8.21, $ascending[0]);
$this->assertEquals(8.21, $ascending[1]);
$this->assertEquals(11.3, $ascending[2]);
$this->assertEquals(12.33, $ascending[3]);
$this->assertEquals(33, $ascending[4]);
}
 
public function testOrderBy_StringValues_ReturnsCorrectOrders()
{
$items = array("e", "a", "c");
$ascending = Linq::from($items)->orderBy(function($x) { return $x; });
$this->assertTrue($ascending instanceof Linq);
 
$ascending = $ascending->toArray();
$this->assertEquals("a", $ascending[0]);
$this->assertEquals("c", $ascending[1]);
$this->assertEquals("e", $ascending[2]);
 
// Verify that original collection is unmodified:
$this->assertEquals("e", $items[0]);
$this->assertEquals("a", $items[1]);
$this->assertEquals("c", $items[2]);
}
 
public function testOrderBy_DateTimeValues_ReturnsCorrectOrders()
{
$items = array(new DateTime("27.10.2011"), new DateTime("03.04.2012"), new DateTime("01.01.2005"));
$ascending = Linq::from($items)->orderBy(function($x) { return $x; });
$this->assertTrue($ascending instanceof Linq);
$ascending = $ascending->toArray();
 
$this->assertEquals(new DateTime("01.01.2005"), $ascending[0]);
$this->assertEquals(new DateTime("27.10.2011"), $ascending[1]);
$this->assertEquals(new DateTime("03.04.2012"), $ascending[2]);
 
// Verify that original collection is unmodified:
$this->assertEquals(new DateTime("27.10.2011"), $items[0]);
$this->assertEquals(new DateTime("03.04.2012"), $items[1]);
$this->assertEquals(new DateTime("01.01.2005"), $items[2]);
}
 
public function testOrderByDescending_NumericValues_ReturnsCorrectOrders()
{
$items = array(77, 10, 20);
$desc = Linq::from($items)->orderByDescending(function($x) { return $x; });
$this->assertTrue($desc instanceof Linq);
$desc = $desc->toArray();
 
$this->assertEquals(77, $desc[0]);
$this->assertEquals(20, $desc[1]);
$this->assertEquals(10, $desc[2]);
 
// Verify that original collection is unmodified:
$this->assertEquals(77, $items[0]);
$this->assertEquals(10, $items[1]);
$this->assertEquals(20, $items[2]);
 
$items = array(12.33, 8.21, 11.3, 8.21, 33);
$desc = Linq::from($items)->orderByDescending(function($x) { return $x; });
$desc = $desc->toArray();
$this->assertEquals(33, $desc[0]);
$this->assertEquals(12.33, $desc[1]);
$this->assertEquals(11.3, $desc[2]);
$this->assertEquals(8.21, $desc[3]);
$this->assertEquals(8.21, $desc[4]);
}
 
public function testOrderByDescending_NumericValues_WithSelector_ReturnsOrderedObjects()
{
$a = new stdClass(); $a->value = 77;
$b = new stdClass(); $b->value = 10;
$c = new stdClass(); $c->value = 20;
$items = array($a, $b, $c);
 
$ascending = Linq::from($items)->orderByDescending(function($x) { return $x->value; });
$this->assertEquals(3, $ascending->count());
$items = $ascending->toArray();
 
$this->assertSame($a, $items[0]);
$this->assertSame($c, $items[1]);
$this->assertSame($b, $items[2]);
 
$a = new stdClass(); $a->value = 77;
$b = new stdClass(); $b->value = 10.44;
$c = new stdClass(); $c->value = 20;
$d = new stdClass(); $d->value = 20;
$items = array($a, $b, $c, $d);
 
$ascending = Linq::from($items)->orderByDescending(function($x) { return $x->value; });
$this->assertEquals(4, $ascending->count());
$items = $ascending->toArray();
 
$this->assertSame($a, $items[0]);
$this->assertSame($c, $items[1]);
 
// It is not predictable in which order objects with the same order key are ordered, both positions are valid:
$pos1 = $items[2];
$pos2 = $items[3];
$this->assertTrue($pos1 === $d || $pos1 === $c);
$this->assertTrue($pos1 === $d || $pos1 === $c);
$this->assertNotsame($pos1, $pos2);
}
 
public function testOrderByDescending_DateTimeValues_ReturnsCorrectOrders()
{
$items = array(new DateTime("27.10.2011"), new DateTime("03.04.2012"), new DateTime("01.01.2005"));
$desc = Linq::from($items)->orderByDescending(function($x) { return $x; });
$this->assertTrue($desc instanceof Linq);
$desc = $desc->toArray();
 
$this->assertEquals(new DateTime("03.04.2012"), $desc[0]);
$this->assertEquals(new DateTime("27.10.2011"), $desc[1]);
$this->assertEquals(new DateTime("01.01.2005"), $desc[2]);
 
// Verify that original collection is unmodified:
$this->assertEquals(new DateTime("27.10.2011"), $items[0]);
$this->assertEquals(new DateTime("03.04.2012"), $items[1]);
$this->assertEquals(new DateTime("01.01.2005"), $items[2]);
}
 
public function testOrderBy_Descending_StringValues_ReturnsCorrectOrders()
{
$items = array("e", "a", "c");
$desc = Linq::from($items)->orderByDescending(function($x) { return $x; });
$this->assertTrue($desc instanceof Linq);
$desc = $desc->toArray();
 
$this->assertEquals("e", $desc[0]);
$this->assertEquals("c", $desc[1]);
$this->assertEquals("a", $desc[2]);
 
// Verify that original collection is unmodified:
$this->assertEquals("e", $items[0]);
$this->assertEquals("a", $items[1]);
$this->assertEquals("c", $items[2]);
}
 
public function testOrderBy_DoesMakeLazyEvalution()
{
$items = array("e", "a", "c");
$eval = false;
$linq = Linq::from($items)->orderByDescending(function($x) use(&$eval)
{
$eval = true;
return $x;
});
 
$this->assertFalse($eval, "OrderBy did execute before iterating!");
$result = $linq->toArray();
$this->assertTrue($eval);
}
 
public function testGroupBy()
{
$a1 = new stdClass(); $a1->id = 1; $a1->value = "a";
$a2 = new stdClass(); $a2->id = 2; $a2->value = "a";
$b1 = new stdClass(); $b1->id = 3; $b1->value = "b";
 
$items = array ($a1, $a2, $b1);
$grouped = Linq::from($items)->groupBy(function($x) {
return $x->value;
});
 
$this->assertTrue($grouped instanceof Linq);
 
$this->assertEquals(2, $grouped->count());
$aGroup = $grouped->elementAt(0);
$this->assertTrue($aGroup instanceof Fusonic\Linq\GroupedLinq);
 
$this->assertEquals("a", $aGroup->key());
$this->assertEquals(2, $aGroup->count());
$this->assertSame($a1, $aGroup->elementAt(0));
$this->assertSame($a2, $aGroup->elementAt(1));
 
$bGroup = $grouped->elementAt(1);
$this->assertEquals("b", $bGroup->key());
$this->assertEquals(1, $bGroup->count());
$this->assertSame($b1, $bGroup->elementAt(0));
}
 
public function testElementAt_ReturnsElementAtPositionOrThrowsException()
{
$items = array ("a", "b", "c");
$this->assertEquals("a", Linq::from($items)->elementAt(0));
$this->assertEquals("b", Linq::from($items)->elementAt(1));
$this->assertEquals("c", Linq::from($items)->elementAt(2));
 
$items = array("a" => "aValue", "b" => "bValue");
$this->assertEquals("aValue", Linq::from($items)->elementAt(0));
$this->assertEquals("bValue", Linq::from($items)->elementAt(1));
 
$this->assertException(function() {
$items = array();
Linq::from($items)->elementAt(0);
}, self::ExceptionName_OutOfRange);
 
$this->assertException(function() {
$items = array();
Linq::from($items)->elementAt(1);
}, self::ExceptionName_OutOfRange);
 
$this->assertException(function() {
$items = array();
Linq::from($items)->elementAt(-1);
}, self::ExceptionName_OutOfRange);
 
$this->assertException(function() {
$items = array("a");
Linq::from($items)->elementAt(1);
}, self::ExceptionName_OutOfRange);
 
$this->assertException(function() {
$items = array("a", "b");
Linq::from($items)->elementAt(2);
}, self::ExceptionName_OutOfRange);
 
$this->assertException(function() {
$items = array("a", "b");
Linq::from($items)->elementAt(-1);
}, self::ExceptionName_OutOfRange);
 
$this->assertException(function() {
$items = array("a" => "value", "b" => "bValue");
Linq::from($items)->elementAt(2);
}, self::ExceptionName_OutOfRange);
}
 
public function testElementAtOrNull_ReturnsElementAtPositionOrNull()
{
$items = array ("a", "b", "c");
$this->assertEquals("a", Linq::from($items)->elementAtOrNull(0));
$this->assertEquals("b", Linq::from($items)->elementAtOrNull(1));
$this->assertEquals("c", Linq::from($items)->elementAtOrNull(2));
 
$this->assertNull(Linq::from($items)->elementAtOrNull(3));
$this->assertNull(Linq::from($items)->elementAtOrNull(4));
$this->assertNull(Linq::from($items)->elementAtOrNull(-1));
 
$items = array();
$this->assertNull(Linq::from($items)->elementAtOrNull(3));
$this->assertNull(Linq::from($items)->elementAtOrNull(4));
$this->assertNull(Linq::from($items)->elementAtOrNull(-1));
 
$items = array("a" => "value", "b" => "bValue");
$this->assertEquals("value", Linq::from($items)->elementAtOrNull(0));
$this->assertNull(Linq::from($items)->elementAtOrNull(2));
}
 
public function testSelect_ReturnsProjectedSequence()
{
$a1 = new stdClass(); $a1->value = "a1";
$a2 = new stdClass(); $a2->value = "a2";
$a3 = new stdClass(); $a3->value = "a3";
$a4 = new stdClass(); $a4->value = "a4";
 
// more than one
$items = array($a1, $a2, $a3, $a4);
 
$projected = Linq::from($items)->select(function($v){
return $v->value;
});
 
$this->assertTrue($projected instanceof Linq);
$this->assertEquals(4, $projected->count());
 
$projected = $projected->toArray();
$this->assertEquals("a1", $projected[0]);
$this->assertEquals("a2", $projected[1]);
$this->assertEquals("a3", $projected[2]);
$this->assertEquals("a4", $projected[3]);
 
$items = array();
 
$projected = Linq::from($items)->select(function($v){
return $v->value;
});
 
$this->assertEquals(0, $projected->count());
}
 
public function testSelectMany_throwsExceptionIfElementIsNotIterable()
{
$a1 = new stdClass(); $a1->value = "a1";
$items = array($a1);
 
$this->assertException(function() use($items) {
Linq::from($items)->selectMany(function($v) {
return $v->value;
})->toArray();
 
}, self::ExceptionName_UnexpectedValue);
 
$this->assertException(function() use($items) {
Linq::from($items)->selectMany(function($v) {
return null;
})->toArray();
}, self::ExceptionName_UnexpectedValue);
}
 
public function testSelectMany_ReturnsFlattenedSequence()
{
$a1 = new stdClass(); $a1->value = array("a", "b");
$a2 = new stdClass(); $a2->value = array("c", "d");
$items = array($a1, $a2);
 
$linq = Linq::from($items)->selectMany(function($x)
{
return $x->value;
});
 
$this->assertTrue($linq instanceof Linq);
 
$this->assertEquals(4, $linq->count());
 
$array = $linq->toArray();
$this->assertEquals("a", $array[0]);
$this->assertEquals("b", $array[1]);
$this->assertEquals("c", $array[2]);
$this->assertEquals("d", $array[3]);
 
// Try once again to see if rewinding the iterator works:
$this->assertEquals(4, $linq->count());
 
$array = $linq->toArray();
$this->assertEquals("a", $array[0]);
$this->assertEquals("b", $array[1]);
$this->assertEquals("c", $array[2]);
$this->assertEquals("d", $array[3]);
}
 
public function testSelectMany_EmptySequence_ReturnsEmptySequence()
{
$linq = Linq::from(array())->selectMany(function($x)
{
return $x->value;
});
 
$this->assertEquals(0, $linq->count());
$array = $linq->toArray();
$this->assertEquals(0, count($array));
 
// Try once again to see if rewinding the iterator works:
$this->assertEquals(0, $linq->count());
$array = $linq->toArray();
$this->assertEquals(0, count($array));
}
 
public function testSelectMany_DoesLazyEvaluation()
{
$a1 = new stdClass(); $a1->value = array("a", "b");
$a2 = new stdClass(); $a2->value = array("c", "d");
$items = array($a1, $a2);
 
$eval = false;
$flattened = Linq::from($items)->selectMany(function($x) use(&$eval)
{
$eval = true;
return $x->value;
});
 
$this->assertFalse($eval, "SelectMany did execute before iterating!");
$result = $flattened->toArray();
$this->assertTrue($eval);
}
 
public function testConcat_ReturnsConcatenatedElements()
{
$first = array("a", "b");
$second = array("c", "d");
 
$all = Linq::from($first)->concat($second);
$this->assertTrue($all instanceof Linq);
 
$this->assertEquals(4, $all->count());
 
$all = $all->toArray();
$this->assertEquals("a", $all[0]);
$this->assertEquals("b", $all[1]);
$this->assertEquals("c", $all[2]);
$this->assertEquals("d", $all[3]);
}
 
public function testConcat_ThrowsArgumentExceptionIfNoTraversableArgument()
{
$this->assertException(function() {
$input = array();
$linq = Linq::from($input);
$linq->concat(null);
},self::ExceptionName_InvalidArgument);
 
$this->assertException(function() {
$input = array();
$second = new stdClass();
Linq::from($input)->concat($second);
},self::ExceptionName_InvalidArgument);
}
 
public function testLinqFrom_WorksWith_Arrays_Iterators_And_IteratorAggregates()
{
$linq = Linq::from(array(1, 2));
$linq = Linq::from($linq);
$linq = Linq::from($linq->getIterator());
}
 
public function testMethodsWithSequencesAsArguments_WorkWith_Arrays_Iterators_And_IteratorAggregates()
{
$first = Linq::from(array("a", "b"));
$secondArray = array("c", "d");
$secondLinq = Linq::from(array("c", "d"));
$secondIterator = $secondLinq->getIterator();
 
$res = $first->concat($secondLinq)->toArray();
$res = $first->intersect($secondLinq)->toArray();
$res = $first->except($secondLinq)->toArray();
 
$res = $first->concat($secondArray)->toArray();
$res = $first->intersect($secondArray)->toArray();
$res = $first->except($secondArray)->toArray();
 
$res = $first->concat($secondIterator)->toArray();
$res = $first->intersect($secondIterator)->toArray();
$res = $first->except($secondIterator)->toArray();
}
 
public function testIntersect_ReturnsIntersectedElements()
{
$first = array("a", "b", "c", "d");
$second = array("b", "c");
 
$linq = Linq::from($first)->intersect($second);
$this->assertEquals(2, $linq->count());
 
$array = $linq->toArray();
$this->assertEquals("b", $array[0]);
$this->assertEquals("c", $array[1]);
}
 
public function testIntersect_ThrowsArgumentExceptionIfSecondSequenceIsNotTraversable()
{
$this->assertException(function() {
$input = array();
$linq = Linq::from($input);
$linq->intersect(null);
},self::ExceptionName_InvalidArgument);
 
$this->assertException(function() {
$input = array();
$linq = Linq::from($input);
$linq->intersect("Not a sequence");
},self::ExceptionName_InvalidArgument);
}
 
public function testIntersect_EmptySequence_ReturnsEmptySequence()
{
$first = array("a", "b", "c", "d");
$second = array();
 
$linq = Linq::from($first)->intersect($second);
$this->assertEquals(0, $linq->count());
$array = $linq->toArray();
$this->assertEquals(0, count($array));
}
 
public function testExcept_ReturnsAllElementsExceptTheGivenOnes()
{
$first = array("a", "b", "c", "d");
$second = array("b", "c");
 
$linq = Linq::from($first)->except($second);
$this->assertEquals(2, $linq->count());
 
$array = $linq->toArray();
$this->assertEquals("a", $array[0]);
$this->assertEquals("d", $array[1]);
}
 
public function testExcept_ThrowsArgumentExceptionIfSecondSequenceIsNotTraversable()
{
$this->assertException(function() {
$input = array();
$linq = Linq::from($input);
$linq->except(null);
},self::ExceptionName_InvalidArgument);
 
$this->assertException(function() {
$input = array();
$linq = Linq::from($input);
$linq->except("Not a sequence");
},self::ExceptionName_InvalidArgument);
}
 
public function testExcept_EmptySequence_ReturnsAllElementsFromFirst()
{
$first = array("a", "b", "c", "d");
$second = array();
 
$linq = Linq::from($first)->except($second);
$this->assertEquals(4, $linq->count());
 
$array = $linq->toArray();
$this->assertEquals("a", $array[0]);
$this->assertEquals("b", $array[1]);
$this->assertEquals("c", $array[2]);
$this->assertEquals("d", $array[3]);
}
 
public function testDistinct_ReturnsDistinctElements()
{
$items = array("a", "b", "a", "b");
 
$distinct = Linq::from($items)->distinct();
$this->assertTrue($distinct instanceof Linq);
 
$this->assertEquals(2, $distinct->count());
$distinct = $distinct->toArray();
$this->assertEquals("a", $distinct[0]);
$this->assertEquals("b", $distinct[1]);
 
$a1 = new stdClass(); $a1->id = 1; $a1->value = "a";
$a2 = new stdClass(); $a2->id = 2; $a2->value = "a";
$b1 = new stdClass(); $b1->id = 3; $b1->value = "b";
 
$items = array($a1, $a2, $b1);
$distinct = Linq::from($items)->distinct(function($v) { return $v->value; });
$this->assertEquals(2, $distinct->count());
}
 
public function testDistinct_DoesLazyEvaluation()
{
$eval = false;
$a1 = new stdClass(); $a1->id = 1; $a1->value = "a";
$a2 = new stdClass(); $a2->id = 2; $a2->value = "a";
 
$items = array($a1, $a2);
$distinct = Linq::from($items)->distinct(function($v) use(&$eval)
{
$eval = true;
return $v->value;
});
 
$this->assertFalse($eval, "SelectMany did execute before iterating!");
$distinct->toArray();
$this->assertTrue($eval);
}
 
public function testMin_ReturnsMinValueFromNumerics()
{
$items = array(88, 77, 12, 112);
$min = Linq::from($items)->min();
$this->assertEquals(12, $min);
 
$items = array(13);
$min = Linq::from($items)->min();
$this->assertEquals(13, $min);
 
$items = array(0);
$min = Linq::from($items)->min();
$this->assertEquals(0, $min);
 
$items = array(-12);
$min = Linq::from($items)->min();
$this->assertEquals(-12, $min);
 
$items = array(-12, 0, 100, -33);
$min = Linq::from($items)->min();
$this->assertEquals(-33, $min);
}
 
public function testMin_ReturnsMinValueFromStrings()
{
$items = array("c", "a", "b", "d");
$min = Linq::from($items)->min();
$this->assertEquals("a", $min);
 
$items = array("a");
$min = Linq::from($items)->min();
$this->assertEquals("a", $min);
}
 
public function testMin_ReturnsMinValueFromDateTimes()
{
$items = array(
new DateTime("2015-01-01 10:00:00"),
new DateTime("2015-02-01 10:00:00"),
new DateTime("2015-01-01 09:00:00"),
);
$min = Linq::from($items)->min();
$this->assertEquals($items[2], $min);
}
 
public function testMin_ThrowsExceptionIfSequenceIsEmpty()
{
$this->assertException(function()
{
$data = array();
$min = Linq::from($data)->min();
});
}
 
public function testMin_ThrowsExceptionIfSequenceContainsNoneNumericValuesOrStrings()
{
$this->assertException(function()
{
$data = array(null);
$max = Linq::from($data)->min();
}, self::ExceptionName_UnexpectedValue);
 
$this->assertException(function()
{
$data = array(new stdClass());
$min = Linq::from($data)->min();
}, self::ExceptionName_UnexpectedValue);
 
$this->assertException(function()
{
$data = array("string value", 1, new stdClass());
$min = Linq::from($data)->min();
}, self::ExceptionName_UnexpectedValue);
 
$this->assertException(function()
{
$a = new stdClass(); $a->nonNumeric = new stdClass();
$data = array($a);
$min = Linq::from($data)->min(function($x)
{
return $x->nonNumeric;
});
}, self::ExceptionName_UnexpectedValue);
}
 
public function testMax_ReturnsMaxValueFromNumerics()
{
$items = array(88, 77, 12, 112);
$max = Linq::from($items)->max();
$this->assertEquals(112, $max);
 
$items = array(13);
$max = Linq::from($items)->max();
$this->assertEquals(13, $max);
 
$items = array(0);
$max = Linq::from($items)->max();
$this->assertEquals(0, $max);
 
$items = array(-12);
$max = Linq::from($items)->max();
$this->assertEquals(-12, $max);
 
$items = array(-12, 0, 100, -33);
$max = Linq::from($items)->max();
$this->assertEquals(100, $max);
}
 
public function testMax_ReturnsMaxValueFromDateTimes()
{
$items = array(
new DateTime("2015-01-01 10:00:00"),
new DateTime("2015-02-01 10:00:00"),
new DateTime("2015-01-01 09:00:00"),
);
$max = Linq::from($items)->max();
$this->assertEquals($items[1], $max);
}
 
public function testSum_ThrowsExceptionIfSequenceContainsNoneNumericValues()
{
$this->assertException(function()
{
$data = array(null);
$max = Linq::from($data)->sum();
}, self::ExceptionName_UnexpectedValue);
 
$this->assertException(function()
{
$data = array(new stdClass());
$min = Linq::from($data)->sum();
}, self::ExceptionName_UnexpectedValue);
 
$this->assertException(function()
{
$data = array("string value", 1, new stdClass());
$min = Linq::from($data)->sum();
}, self::ExceptionName_UnexpectedValue);
 
$this->assertException(function()
{
$a = new stdClass(); $a->value = 100; $a->nonNumeric = "asdf";
$b = new stdClass(); $b-> value = 133; $a->nonNumeric = "asdf";
 
$data = array($a, $b);
$sum = Linq::from($data)->sum(function($x) {
return $x->nonNumeric;
});
}, self::ExceptionName_UnexpectedValue);
}
 
public function testSum_GetSumOfValues()
{
$data = array();
$sum = Linq::from($data)->sum();
$this->assertEquals(0, $sum);
 
$data = array(4, 9, 100.77);
$sum = Linq::from($data)->sum();
$this->assertEquals(113.77, $sum);
 
$data = array(12, -12);
$sum = Linq::from($data)->sum();
$this->assertEquals(0, $sum);
 
$data = array(12, -24);
$sum = Linq::from($data)->sum();
$this->assertEquals(-12, $sum);
 
$a = new stdClass(); $a->value = 100;
$b = new stdClass(); $b-> value = 133;
 
$data = array($a, $b);
$sum = Linq::from($data)->sum(function($x) {
return $x->value;
});
 
$this->assertEquals(233, $sum);
}
 
public function testMax_ReturnsMaxValueFromStrings()
{
$items = array("c", "a", "b", "d");
$max = Linq::from($items)->max();
$this->assertEquals("d", $max);
 
$items = array("a");
$max = Linq::from($items)->max();
$this->assertEquals("a", $max);
}
 
public function testMax_ThrowsExceptionIfSequenceIsEmpty()
{
$this->assertException(function()
{
$data = array();
$max = Linq::from($data)->max();
});
}
 
public function testMax_ThrowsExceptionIfSequenceContainsNoneNumericValuesOrStrings()
{
$this->assertException(function()
{
$data = array(new stdClass());
$max = Linq::from($data)->max();
}, self::ExceptionName_UnexpectedValue);
 
$this->assertException(function()
{
$data = array(null);
$max = Linq::from($data)->max();
}, self::ExceptionName_UnexpectedValue);
 
$this->assertException(function()
{
$data = array("string value", 1, new stdClass());
$max = Linq::from($data)->max();
}, self::ExceptionName_UnexpectedValue);
 
$this->assertException(function()
{
$a = new stdClass(); $a->nonNumeric = new stdClass();
$data = array($a);
$min = Linq::from($data)->max(function($x)
{
return $x->nonNumeric;
});
}, self::ExceptionName_UnexpectedValue);
}
 
public function testEach_PerformsActionOnEachElement()
{
$items = array("a", "b", "c");
$looped = array();
Linq::from($items)
->each(function($x) use(&$looped)
{
$looped[] = $x;
});
 
$this->assertEquals(3, count($looped));
$this->assertEquals("a", $looped[0]);
$this->assertEquals("b", $looped[1]);
$this->assertEquals("c", $looped[2]);
}
 
public function testEach_ReturnsOriginalLinqSequence()
{
$linq = Linq::from(array(1,2,3,4))
->skip(2)->take(1);
 
$linqAfterEach = $linq->each(function($x) {});
$this->assertSame($linq, $linqAfterEach);
}
 
public function testToArray_WithoutKeySelector_ReturnsIteratorValuesAsArray_UsesDefaultNumericArrayKeys()
{
$linq = Linq::from(array("a", "b", "c"))
->skip(1)->take(3);
 
$array = $linq->toArray();
$this->assertTrue(is_array($array));
$this->assertEquals(2, count($array));
 
$keys = array_keys($array);
$this->assertEquals(0, $keys[0]);
$this->assertEquals(1, $keys[1]);
 
$this->assertEquals("b", $array[0]);
$this->assertEquals("c", $array[1]);
}
 
public function testToArray_WithKeySelector_ReturnsIteratorValuesAsArray_UsesKeySelectorValueAsKey()
{
$linq = Linq::from(array("a", "b", "c"))
->skip(1)->take(3);
 
$array = $linq->toArray(function($x) {
return "keyprefix_" . $x;
});
 
$this->assertTrue(is_array($array));
$this->assertEquals(2, count($array));
 
$keys = array_keys($array);
$this->assertEquals("keyprefix_b", $keys[0]);
$this->assertEquals("keyprefix_c", $keys[1]);
 
$this->assertEquals("b", $array["keyprefix_b"]);
$this->assertEquals("c", $array["keyprefix_c"]);
}
 
public function testToArray_WithKeyAndValueSelector_ReturnsArrayWithKeyValueSetsFromClosures()
{
$source = array(
array("catId" => 11, "name" => "Category11", "additionalcolumn" => "foo"),
array("catId" => 12, "name" => "Category12", "additionalcolumn" => "bar"),
);
$linq = Linq::from($source);
 
$array = $linq->toArray(function($x) {
return $x["catId"];
}, function($y) {
return $y["name"];
});
 
$this->assertTrue(is_array($array));
$this->assertEquals(2, count($array));
 
$keys = array_keys($array);
$this->assertEquals(11, $keys[0]);
$this->assertEquals(12, $keys[1]);
 
$this->assertEquals("Category11", $array[11]);
$this->assertEquals("Category12", $array[12]);
}
 
public function testToArray_WithValueSelector_ReturnsArrayWithDefaultNumericKey_AndValueFromClosure()
{
$source = array(
array("catId" => 11, "name" => "Category11", "additionalcolumn" => "foo"),
array("catId" => 12, "name" => "Category12", "additionalcolumn" => "bar"),
);
 
$linq = Linq::from($source);
 
$array = $linq->toArray(null, function($y) {
return $y["additionalcolumn"];
});
 
$this->assertTrue(is_array($array));
$this->assertEquals(2, count($array));
 
$keys = array_keys($array);
$this->assertEquals(0, $keys[0]);
$this->assertEquals(1, $keys[1]);
 
$this->assertEquals("foo", $array[0]);
$this->assertEquals("bar", $array[1]);
}
 
public function testAggregate_novalues_throwsException()
{
$this->assertException(function() {
 
Linq::from(array())->aggregate(function() {});
}, self::ExceptionName_Runtime);
 
 
$this->assertException(function() {
 
Linq::from(array())->aggregate(function() {}, null);
}, self::ExceptionName_Runtime);
}
 
public function testAggregate_returnsCorrectResult()
{
$this->assertEquals("value", Linq::from(array("value"))->aggregate(function($a, $b) { throw new Exception("Must not becalled"); }));
$this->assertEquals(2, Linq::from(array(2))->aggregate(function($a, $b) { throw new Exception("Must not becalled"); }));
$this->assertEquals(5, Linq::from(array(2, 3))->aggregate(function($a, $b) { return $a + $b; }));
$this->assertEquals(17, Linq::from(array(2, 3, 3, 4, 5))->aggregate(function($a, $b) { return $a + $b; }));
$this->assertEquals("abcde", Linq::from(array("a","b","c","d","e"))->aggregate(function($a, $b) { return $a . $b; }));
}
 
public function testAggregate_withSeedValue_returnsCorrectResult()
{
$this->assertEquals(9999, Linq::from(array())->aggregate(function() {}, 9999));
$this->assertEquals(104, Linq::from(array(2))->aggregate(function($a, $b) { return $a + $b; }, 102));
$this->assertEquals(137, Linq::from(array(2, 2, 20, 11))->aggregate(function($a, $b) { return $a + $b; }, 102));
$this->assertEquals("begin_abcde", Linq::from(array("a","b","c","d","e"))->aggregate(function($a, $b) { return $a . $b; }, "begin_"));
}
 
public function testRange_throwsExceptionIfCountIsNegative()
{
$this->assertException(function() {
 
Linq::range(0, -1);
}, self::ExceptionName_OutOfRange);
}
 
public function testRange_returnsRangeOfIntegers()
{
$range = Linq::range(0, 3)->toArray();
$this->assertEquals(3, count($range));
$this->assertEquals(0, $range[0]);
$this->assertEquals(1, $range[1]);
$this->assertEquals(2, $range[2]);
 
$range = Linq::range(6, 3)->toArray();
$this->assertEquals(3, count($range));
$this->assertEquals(6, $range[0]);
$this->assertEquals(7, $range[1]);
$this->assertEquals(8, $range[2]);
 
$range = Linq::range(-3, 5)->toArray();
$this->assertEquals(5, count($range));
$this->assertEquals(-3, $range[0]);
$this->assertEquals(-2, $range[1]);
$this->assertEquals(-1, $range[2]);
$this->assertEquals(0, $range[3]);
$this->assertEquals(1, $range[4]);
}
 
public function testContains_defaultComparison()
{
$items = array("2", 2);
$linq = Linq::from($items);
$this->assertTrue($linq->contains(2));
$this->assertTrue($linq->contains("2"));
$this->assertFalse($linq->contains(true));
 
$this->assertFalse($linq->contains(3));
$this->assertFalse($linq->contains("3"));
 
$this->assertFalse($linq->contains(3));
$this->assertFalse($linq->contains(null));
 
$a = new stdClass();
$b = new stdClass();
$c = new stdClass();
$linq = Linq::from(array($a, $b));
$this->assertTrue($linq->contains($a));
$this->assertTrue($linq->contains($b));
$this->assertFalse($linq->contains($c));
}
 
public function testChunk_throwsException_IfchunksizeIsInvalid()
{
$this->assertException(function() {
Linq::from(array())->chunk(0);
}, self::ExceptionName_InvalidArgument);
 
$this->assertException(function() {
Linq::from(array())->chunk(-1);
}, self::ExceptionName_InvalidArgument);
}
 
public function testChunk_ReturnsChunkedElementsAccordingToChunksize()
{
$groups = Linq::from(array())->chunk(2);
$this->assertEquals(0, $groups->count());
 
$groups = Linq::from(array("a"))->chunk(2);
$this->assertEquals(1, $groups->count());
$this->assertEquals(1, $groups->ElementAt(0)->count());
$this->assertEquals("a", $groups->ElementAt(0)->ElementAt(0));
 
$groups = Linq::from(array("a","b","c","d","e"))->chunk(2);
$this->assertEquals(3, $groups->count());
$this->assertEquals(2, $groups->ElementAt(0)->count());
$this->assertEquals("a", $groups->ElementAt(0)->ElementAt(0));
$this->assertEquals("b", $groups->ElementAt(0)->ElementAt(1));
 
$this->assertEquals(2, $groups->ElementAt(1)->count());
$this->assertEquals("c", $groups->ElementAt(1)->ElementAt(0));
$this->assertEquals("d", $groups->ElementAt(1)->ElementAt(1));
 
$this->assertEquals(1, $groups->ElementAt(2)->count());
$this->assertEquals("e", $groups->ElementAt(2)->ElementAt(0));
 
$groups = Linq::from(array("a","b","c","d","e"))->chunk(3);
$this->assertEquals(2, $groups->count());
 
$groups = Linq::from(array("a","b","c","d","e"))->chunk(4);
$this->assertEquals(2, $groups->count());
 
$groups = Linq::from(array("a","b","c","d","e"))->chunk(5);
$this->assertEquals(1, $groups->count());
 
$groups = Linq::from(array("a","b","c","d","e"))->chunk(117);
$this->assertEquals(1, $groups->count());
}
 
public function testIssue3_emtpyCollectionOrdering()
{
Linq::from(array())
->orderBy(function(array $x) { return $x["name"]; })
->toArray();
}
 
/**
* @test
*/
public function when_ofType_is_called_with_empty_array()
{
/** @var array $result */
$result = Linq::from(array())
->ofType('StubInterface')
->toArray();
 
$this->assertNotNull($result);
$this->assertCount(0, $result);
}
 
/**
* @test
*/
public function when_ofType_is_called_with_array_containing_expected_interface()
{
/** @var Stub $expectedResult */
$expectedResult = new Stub();
 
/** @var array $result */
$result = Linq::from(array($expectedResult,
new StubWithoutInterface()))
->ofType('StubInterface')
->toArray();
 
$this->assertNotNull($result);
$this->assertCount(1, $result);
$this->assertSame($expectedResult, $result[0]);
}
 
/**
* @test
*/
public function when_ofType_is_called_with_array_containing_expected_object_type()
{
/** @var StubWithoutInterface $expectedResult1 */
$expectedResult1 = new StubWithoutInterface();
 
/** @var array $result */
$result = Linq::from(array(new Stub(),
$expectedResult1))
->ofType('StubWithoutInterface')
->toArray();
 
$this->assertNotNull($result);
$this->assertCount(1, $result);
$this->assertSame($expectedResult1, $result[0]);
 
/** @var StubWithoutInterface $expectedResult2 */
$expectedResult2 = new Stub();
 
$result = Linq::from(array($expectedResult2,
new StubWithoutInterface()))
->ofType('Stub')
->toArray();
 
$this->assertNotNull($result);
$this->assertCount(1, $result);
$this->assertSame($expectedResult2, $result[0]);
}
 
/**
* @test
*/
public function when_ofType_is_called_with_array_not_containing_expected_interface()
{
/** @var array $result */
$result = Linq::from(array(new StubWithoutInterface(),
new StubWithoutInterface()))
->ofType('StubInterface')
->toArray();
 
$this->assertNotNull($result);
$this->assertCount(0, $result);
}
 
/**
* @test
*/
public function when_ofType_is_called_with_array_not_containing_expected_object_type()
{
/** @var array $result */
$result = Linq::from(array(new Stub(),
new Stub()))
->ofType('StubWithoutInterface')
->toArray();
 
$this->assertNotNull($result);
$this->assertCount(0, $result);
}
 
/**
* @test
*/
public function when_ofType_is_called_with_unknown_interface()
{
/** @var array $result */
$result = Linq::from(array(new Stub(),
new Stub()))
->ofType('UnknownInterface')
->toArray();
 
$this->assertNotNull($result);
$this->assertCount(0, $result);
}
 
/**
* @test
*/
public function when_ofType_is_called_with_unknown_object_type()
{
/** @var array $result */
$result = Linq::from(array(new Stub(),
new Stub()))
->ofType('UnknownObject')
->toArray();
 
$this->assertNotNull($result);
$this->assertCount(0, $result);
}
 
/**
* @test
*/
public function when_ofType_is_called_with_int_as_type()
{
/** @var int[] $expectedResult */
$expectedResult = array(1,
2,
10,
20);
 
$result = Linq::from(array(1,
2,
new Stub(),
10,
NULL,
20))
->ofType('int')
->toArray();
 
$this->assertNotNull($result);
$this->assertEquals($expectedResult, $result);
}
 
/**
* @test
*/
public function when_ofType_is_called_with_bool_as_type()
{
/** @var int[] $expectedResult */
$expectedResult = array(TRUE,
FALSE);
 
$result = Linq::from(array(0,
'string',
'true',
TRUE,
'false',
FALSE))
->ofType('bool')
->toArray();
 
$this->assertNotNull($result);
$this->assertEquals($expectedResult, $result);
}
 
/**
* @test
*/
public function when_ofType_is_called_with_string_as_type()
{
/** @var int[] $expectedResult */
$expectedResult = array('string',
'true',
'false');
 
$result = Linq::from(array(0,
'string',
'true',
TRUE,
'false',
FALSE))
->ofType('string')
->toArray();
 
$this->assertNotNull($result);
$this->assertEquals($expectedResult, $result);
}
 
/**
* @test
*/
public function when_ofType_is_called_with_float_as_type()
{
/** @var int[] $expectedResult */
$expectedResult = array(2.5,
10.0,
0.3);
 
$result = Linq::from(array(0,
'string',
2.5,
10.0,
11,
'false',
0.3))
->ofType('float')
->toArray();
 
$this->assertNotNull($result);
$this->assertEquals($expectedResult, $result);
}
 
/**
* @test
*/
public function when_ofType_is_called_with_double_as_type()
{
/** @var int[] $expectedResult */
$expectedResult = array(2.5,
10.0,
0.3);
 
$result = Linq::from(array(0,
'string',
2.5,
10.0,
NULL,
11,
'false',
0.3))
->ofType('double')
->toArray();
 
$this->assertNotNull($result);
$this->assertEquals($expectedResult, $result);
}
 
private function assertException($closure, $expected = self::ExceptionName_Runtime)
{
try
{
$closure();
}
catch(Exception $ex)
{
$exName = get_class($ex);
 
if($exName != $expected)
{
$this->fail("Wrong exception raised. Expected: '" . $expected . "' Actual: '" . get_class($ex) . "'. Message: " . $ex->getMessage());
}
return;
}
 
$this->fail($expected .' has not been raised.');
}
}
/vendor/fusonic/linq/tests/bootstrap.php
@@ -0,0 +1,9 @@
<?php
 
if (!$loader = @include __DIR__.'/../vendor/autoload.php') {
die('You must set up the project dependencies, run the following commands:'.PHP_EOL.
'curl -s http://getcomposer.org/installer | php'.PHP_EOL.
'php composer.phar install'.PHP_EOL);
}
 
$loader->add('Fusonic\Linq\Test', __DIR__);