PSI helps you to write cleaner and more stable PHP-Code. Especially when it comes to iterating, filtering, mapping, sorting arrays or array-like data.
PSI consists of multiple small operations that can be combined and chained. Doing so results in easier to read and more expressive code.
PSI supports PHP versions >= 7.0
Let's have a look at some examples.
/** @var Person[] $input */
$input = $service->getStuff();
/** @var Person[] $result */
$result = Psi::it($input)
->filter(new Psi\IsInstanceOf(Person::class))
->filter(function (Person $p) { return $p->getAge() >= 18; })
->toArray();
In plain PHP this might looks like:
/** @var Person[] $input */
$ipnut = $service->getStuff();
$result = [];
foreach ($input as $item) {
if ($item instanceof Person && $item->getAge() >= 18) {
$result[] = $item
}
}
/** @var Person[] $input */
$input = $service->getStuff();
/** @var Person[] $result */
$result = Psi::it($input)
->filter(new Psi\IsInstanceOf(Person::class))
->filter(function (Person $p) { return $p->getAge() >= 18; })
->sortBy(function (Person $p) { return $p->getAge(); })
->toArray();
In plain PHP this might looks like:
/** @var Person[] $input */
$ipnut = $service->getStuff();
$result = [];
foreach ($input as $item) {
if ($item instanceof Person && $item->getAge() >= 18) {
$result[] = $item
}
}
usort($result, function (Person $p1, Person $p2) {
return $p1->getAge() <=> $p2->getAge();
});
/** @var Person[] $input */
$input = $service->getStuff();
/** @var Person[] $result */
$result = Psi::it($input)
->filter(new Psi\IsInstanceOf(Person::class))
->filter(function (Person $p) { return $p->getAge() >= 18; })
->sortBy(function (Person $p) { return $p->getName(); })
->reverse()
->toArray();
With Psi::map() you can convert incoming data.
For example lets convert all persons to ints (the persons age)
/** @var Person[] $input */
$input = $service->getStuff();
$result = Psi::it($input)
->map(function (Person $p) { return $p->getAge(); })
->toArray();
This might result in
[10, 20, 18, 31, ...]
We could also convert each Person into something else.
/** @var Person[] $input */
$input = $service->getStuff();
$result = Psi::it($input)
->sortBy(function (Person $p) { return $p->getName(); })
->map(function (Person $p) { return [ $p->getName(), $p->getAge() ]; })
->toArray();
This might result in
[["Elsa", 20], ["Joe", 10], ["John", 18], ["Mary", 31], ...]
With Psi::filterBy() you can combine mapping and filtering.
Let's get all adults by checking the age using Psi\IsGreaterThanOrEqual().
The example below will also give you all Persons with an age greater than or equal to 18.
$input = $service->getStuff();
/** @var Person[] $result */
$result = Psi::it($input)
->filter(new Psi\IsInstanceOf(Person::class))
->filterBy(
function (Person $p) { return $p->getAge(); }, // map the input
new Psi\IsGreaterThanOrEqual(18) // apply filter on the mapped value
)
->toArray();
With Psi::all() you can check if all elements match the given condition
$result = Psi::it([1, 1, 1])
->all(new Psi\IsEqualTo(1)); // true, all elements are equal to 1
$result = Psi::it([1, 1, 1])
->all(function ($it) { return $it === 1 }); // true, all elements are equal to 1
$result = Psi::it([1, 2, 3])
->all(new Psi\IsEqualTo(1)); // false, not all elements are equal to 1
$result = Psi::it([])
->all(new Psi\IsEqualTo(1)); // true, since no element does not match the condition
With Psi::any() you can check if there is at least one element matching the condition
$result = Psi::it([2, 1, 4])
->any(new Psi\IsEqualTo(1)); // true, there is one element that is equal to 1
$result = Psi::it([2, 1, 4])
->any(function ($it) { return $it === 1 }); // true, there is one element that is equal to 1
$result = Psi::it([2, 3, 4])
->any(new Psi\IsEqualTo(1)); // false, there is no element that is equal to 1
$result = Psi::it([])
->any(new Psi\IsEqualTo(1)); // false, when there is no element in the list, then none can match
Let's group all persons by their age
$input = $service->getStuff();
/** @var Person[] $result */
$result = Psi::it($input)
->filter(new Psi\IsInstanceOf(Person::class))
->groupBy(
function (Person $p) { return $p->getAge(); }, // define the group
)
->toKeyValueArray(); // toKeyValueArray() will preserve keys, in our case the age-groups
var_dump($result);
This might output:
array(3) {
[7] =>
array(2) {
[0] =>
object(Person) ...
[1] =>
object(Person) ...
}
[15] =>
array(1) {
[0] =>
object(Person) ...
...
}
[21] =>
array (2) {
...
}
...
}
You can pass multiple array or array-like parameters to Psi::it(... $inputs)
/** @var Person[] $result */
$result = Psi::it(
$service->getStuff(),
$service->getMoreStuff(),
$service->getEvenMoreStuff()
)
->filter(new Psi\IsInstanceOf(Person::class))
->toArray();
Let's get up to 5 adults but skip the first 10:
$input = $service->getStuff();
/** @var Person[] $result */
$result = Psi::it($input)
->filter(new Psi\IsInstanceOf(Person::class))
->filter(function (Person $p) { return $p->getAge() >= 18; })
->skip(10)
->limit(5)
->toArray();
Or let's skip the first 10 no matter what they are and then get up to 5 adults
$input = $service->getStuff();
/** @var Person[] $result */
$result = Psi::it($input)
->filter(new Psi\IsInstanceOf(Person::class))
->skip(10)
->filter(function (Person $p) { return $p->getAge() >= 18; })
->limit(5)
->toArray();
Or let's skip the first 10, then get up to 5 and then filter the adults out of these
$input = $service->getStuff();
/** @var Person[] $result */
$result = Psi::it($input)
->filter(new Psi\IsInstanceOf(Person::class))
->skip(10)
->limit(5)
->filter(function (Person $p) { return $p->getAge() >= 18; })
->toArray();
Let's count the number of adults:
$input = $service->getStuff();
/** @var float $result */
$result = Psi::it($input)
->filter(new Psi\IsInstanceOf(Person::class))
->filter(function (Person $p) { return $p->getAge() >= 18; })
->count();
Let's sum the age of all persons:
$input = $service->getStuff();
/** @var float $result */
$result = Psi::it($input)
->filter(new Psi\IsInstanceOf(Person::class))
->map(function (Person $p) { return $p->getAge(); })
->sum();
Let's get the youngest age of all people we know:
$input = $service->getStuff();
/** @var float $result */
$result = Psi::it($input)
->filter(new Psi\IsInstanceOf(Person::class))
->map(function (Person $p) { return $p->getAge(); })
->min();
Let's get the oldest age of all:
$input = $service->getStuff();
/** @var float $result */
$result = Psi::it($input)
->filter(new Psi\IsInstanceOf(Person::class))
->map(function (Person $p) { return $p->getAge(); })
->max();
Let's get the average age:
$input = $service->getStuff();
/** @var float $result */
$result = Psi::it($input)
->filter(new Psi\IsInstanceOf(Person::class))
->map(function (Person $p) { return $p->getAge(); })
->avg();
Let's get the median age:
$input = $service->getStuff();
/** @var float $result */
$result = Psi::it($input)
->filter(new Psi\IsInstanceOf(Person::class))
->map(function (Person $p) { return $p->getAge(); })
->median();
Let's get the median age of all adults:
$input = $service->getStuff();
/** @var float $result */
$result = Psi::it($input)
->filter(new Psi\IsInstanceOf(Person::class))
->filter(function (Person $p) { return $p->getAge() >= 18; })
->map(function (Person $p) { return $p->getAge(); })
->median();
Let's get the first person that fits a certain condition (name starting with "A")
/** @var Person|null $result */
$result = Psi::it(input)
->filter(new Psi\IsInstanceOf(Person::class))
->filterBy(
function(function (Person $p) { return $p->getName(); }),
new Psi\Str\IsStartingWith('A')
)
->getFirst()
Let's get the last person that fits a certain condition (name starting with "A")
/** @var Person|null $result */
$result = Psi::it(input)
->filter(new Psi\IsInstanceOf(Person::class))
->filterBy(
function(function (Person $p) { return $p->getName(); }),
new Psi\Str\IsStartingWith('A')
)
->getLast()
Let's get a random person that fits a certain condition (name starting with "A")
/** @var Person|null $result */
$result = Psi::it(input)
->filter(new Psi\IsInstanceOf(Person::class))
->filterBy(
function(function (Person $p) { return $p->getName(); }),
new Psi\Str\IsStartingWith('A')
)
->getRandom()
Filters for Arrays using is_array()
$result = Psi::it($input)->filter(new Psi\IsArray())->toArray();
$result = Psi::it($input)->filter(new Psi\IsNotArray())->toArray();
Filter for Booleans using is_bool()
$result = Psi::it($input)->filter(new Psi\IsBool())->toArray();
$result = Psi::it($input)->filter(new Psi\IsNotBool())->toArray();
Filter for Callables using is_callable()
$result = Psi::it($input)->filter(new Psi\IsCallable())->toArray();
$result = Psi::it($input)->filter(new Psi\IsNotCallable())->toArray();
Checks if the given string is a string that would be understood be new \DateTime($str)
$result = Psi::it($input)->filter(new Psi\IsDateString())->toArray();
$result = Psi::it($input)->filter(new Psi\IsNotDateString())->toArray();
Filter for empty things using empty()
$result = Psi::it($input)->filter(new Psi\IsEmpty())->toArray();
$result = Psi::it($input)->filter(new Psi\IsNotEmpty())->toArray();
Filter for class instance
$result = Psi::it($input)->filter(new Psi\IsInstanceOf())->toArray();
$result = Psi::it($input)->filter(new Psi\IsNotInstanceOf())->toArray();
Filter for integers using is_int()
$result = Psi::it($input)->filter(new Psi\IsInteger())->toArray();
$result = Psi::it($input)->filter(new Psi\IsNotInteger())->toArray();
Filter for strings that contain an integer
$result = Psi::it($input)->filter(new Psi\IsIntegerString())->toArray();
$result = Psi::it($input)->filter(new Psi\IsNotIntegerString())->toArray();
Filter for nulls
$result = Psi::it($input)->filter(new Psi\IsNull())->toArray();
$result = Psi::it($input)->filter(new Psi\IsNotNull())->toArray();
Filter for numeric values using is_numeric()
$result = Psi::it($input)->filter(new Psi\IsNull())->toArray();
$result = Psi::it($input)->filter(new Psi\IsNotNull())->toArray();
Filter for objects
$result = Psi::it($input)->filter(new Psi\IsObject())->toArray();
$result = Psi::it($input)->filter(new Psi\IsNotObject())->toArray();
Filter for resources
$result = Psi::it($input)->filter(new Psi\IsResource())->toArray();
$result = Psi::it($input)->filter(new Psi\IsNotResource())->toArray();
Filter for scalar values using is_scalar()
$result = Psi::it($input)->filter(new Psi\IsResource())->toArray();
$result = Psi::it($input)->filter(new Psi\IsNotResource())->toArray();
Filter for NON type safe eqaulity (==). See IsSame / IsNotSame for === comparison
$result = Psi::it($input)->filter(new Psi\IsEqualTo("Summer"))->toArray();
$result = Psi::it($input)->filter(new Psi\IsNotEqualTo("Winter"))->toArray();
First install all dependencies
./composer install
Then run all tests
vendor/bin/phpunit
REMOVED PHP 5.6 SUPPORT
Added
- Psi::all()
- Psi::any()
Moved mapper ToFloat, ToInteger, ToString to Psi\Map...
Old versions are kept for compatibility and marked as deprecated.
Will split the stream into chunks of the given size.
Psi::it(range(0, 10))
->chunk(3)
->toArray();
will result in
[
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[9, 10]
]
Skips the first n elements in the current stream
Psi::it(range(0, 20))
->filter(new IsMultipleOf(2))
->skip(5)
->toArray();
will result in
[10, 12, 14, 16, 18, 20]
Limits the result to the first n element in the current stream
Psi::it(range(0, 20))
->filter(new IsMultipleOf(2))
->limit(5)
->toArray();
will result in
[0, 2, 4, 6, 8]
Take all elements of the input stream while the condition is met
Psi::it(range(0, 20))
->takeWhile(new Psi\IsLessThan(5))
->toArray();
will result in
[0, 1, 2, 3, 4]
Take all elements of the input stream until the condition is met
Psi::it(range(0, 20))
->takeUntil(new Psi\IsGreaterThan(5))
->toArray();
will result in
[0, 1, 2, 3, 4, 5]
Get the last element of the stream.
Psi::it([1, 2, 3])
->filter(function ($i) { return $i < 3; })
->getLast()
will result in
2
Will select a random element from the stream.
Psi::it([1, 2, 3])
->filter(function ($i) { return $i < 3; })
->getRandom()
will result in
1 or 2
Filters all numbers that are a multiple of the given factor
Psi::it([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
->filter(new Psi\Num\IsMultipleOf(3))
->toArray();
will result in
[0, 3, 6, 9]
Filters all number that are prime number.
CAUTION: do not use this impl for big numbers, a it can be very slow.
Psi::it(range(0, 30))
->filter(new Psi\Num\IsPrime())
->toArray();
will result in
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
In order to by able to use all other matchers on nested property / values of input objects, the method Psi::filterBy() was added
$result = Psi::it($persons)
->filterBy(
function (Person $p) { return $p->getAge(); },
new IsLessThan(18)
)
->toArray()
Finds unique elements by first passing them through a mapper.
$result = Psi::it($persons)
->uniqueBy(
function (Person $p) { return $person->getName(); }
)
->toArray();
Maps inputs to floats.
$result = Psi::it($values)
->map(new Psi\ToFloat())
->toArray();
Maps inputs to integers.
$result = Psi::it($values)
->map(new Psi\ToInteger())
->toArray();
Maps inputs to strings.
$result = Psi::it($values)
->map(new Psi\ToString())
->toArray();
Filters all strings starting with the given needle. By default case-sensitive. Pass false as the second parameter to be non-case-sensitive.
$result = Psi::it($values)
->filter(new Psi\Str\IsStartingWith('a'))
->toArray();
$result = Psi::it($values)
->filter(new Psi\Str\IsNotStartingWith('a', false))
->toArray();
Filters all strings ending with the given needle. By default case-sensitive. Pass false as the second parameter to be non-case-sensitive.
$result = Psi::it($values)
->filter(new Psi\Str\IsEndingWith('a'))
->toArray();
$result = Psi::it($values)
->filter(new Psi\Str\IsNotEndingWith('a', false))
->toArray();
Filters all strings containing the given needle. By default case-sensitive. Pass false as the second parameter to be non-case-sensitive.
$result = Psi::it($values)
->filter(new Psi\Str\IsContaining('a'))
->toArray();
$result = Psi::it($values)
->filter(new Psi\Str\IsNotContaining('a', false))
->toArray();
Filters all strings containing the given needle. By default case-sensitive. Pass false as the second parameter to be non-case-sensitive.
$result = Psi::it($values)
->filter(new Psi\Str\IsMatchingRegex('/[0-9]{2,}'))
->toArray();
$result = Psi::it($values)
->filter(new Psi\Str\IsNotMatchingRegex('/ABC/i'))
->toArray();
Modifies a string by replacing special characters with the "normal" form, e.g.
Dragoş -> Dragos
Ärmel -> Aermel
Blüte -> Bluete
Straße -> Strasse
passé -> passe
$result = Psi::it($values)
->map(new Psi\Str\WithoutAccents())
->toArray();
The internal folder structure was changed:
- public interface live now on the root level
- some over-engineering was removed (some classes removed)
- interface no longer have names like "UnaryFunctionInterface" but "UnaryFunction"
Psi::it(
[ ['name' => 'a', 'val' => 1], ['name' => 'a', 'val' => 2], ['name' => 'b', 'val' => 1] ]
)
->groupBy(
function ($o) { return $o['name']; }
)
->toArray()
would become
['a' => [ ['name' => 'a', 'val' => 1], ['name' => 'a', 'val' => 2] ], 'b' => ... ]
Sort a list of objects by one of their properties, e.g. sorting persons by their age
$result = Psi::it($values)
->filterBy(
function (Person $p) { return $p->getAge(); }
)
->toArray()
- make custom TerminalOperations possible Psi::reduce()
String-Mappers
- Str::Camelize (StrToCamelCase)
- ... camel to dashes (StrToSlug)
- Str::UcFirst, Str::LcFirst
- Str::StrReplace
- Str::StrMbReplace
- Str::StrRegexReplace
- Str::StrMbRegexReplace
... for PHP-Types ... LocalDate::isSameDay