承接 peekandpoke/psi 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

peekandpoke/psi

Composer 安装命令:

composer require peekandpoke/psi

包简介

Php Simple Iterations. Inspired by Java Streams Api.

README 文档

README

Code Coverage Scrutinizer Code Quality Build Status

Introduction

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

TOC

Examples

Let's have a look at some examples.

Get all adults

/** @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
  }
}
 

Get all adults and sort them by age

/** @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(); 
}); 

Get all adults and sort them by age descending

/** @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();

Mapping

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], ...]

Advanced filtering

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();

Check if all or any elements match

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

Grouping

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) {
    ...
  }
  ...  
}

Multiple inputs

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();

Skip and Limit

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();

Count, sum, min, max, average, median

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();

Get first, last, random

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()
    

Detailed Documentation

Filters - type checks

IsArray and IsNotArray

Filters for Arrays using is_array()

$result = Psi::it($input)->filter(new Psi\IsArray())->toArray();

$result = Psi::it($input)->filter(new Psi\IsNotArray())->toArray();

IsBool and IsNotBool

Filter for Booleans using is_bool()

$result = Psi::it($input)->filter(new Psi\IsBool())->toArray();

$result = Psi::it($input)->filter(new Psi\IsNotBool())->toArray();

IsCallable and IsNotCallable

Filter for Callables using is_callable()

$result = Psi::it($input)->filter(new Psi\IsCallable())->toArray();

$result = Psi::it($input)->filter(new Psi\IsNotCallable())->toArray();

IsDateString and IsNotDateString

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();

IsEmpty and IsNotEmpty

Filter for empty things using empty()

$result = Psi::it($input)->filter(new Psi\IsEmpty())->toArray();

$result = Psi::it($input)->filter(new Psi\IsNotEmpty())->toArray();

IsInstanceOf and IsNotInstanceOf

Filter for class instance

$result = Psi::it($input)->filter(new Psi\IsInstanceOf())->toArray();

$result = Psi::it($input)->filter(new Psi\IsNotInstanceOf())->toArray();

IsInteger and IsNotInteger

Filter for integers using is_int()

$result = Psi::it($input)->filter(new Psi\IsInteger())->toArray();

$result = Psi::it($input)->filter(new Psi\IsNotInteger())->toArray();

IsIntegerString and IsNotIntegerString

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();

IsNull and IsNotNull

Filter for nulls

$result = Psi::it($input)->filter(new Psi\IsNull())->toArray();

$result = Psi::it($input)->filter(new Psi\IsNotNull())->toArray();

IsNumeric and IsNotNumeric

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();

IsObject and IsNotObject

Filter for objects

$result = Psi::it($input)->filter(new Psi\IsObject())->toArray();

$result = Psi::it($input)->filter(new Psi\IsNotObject())->toArray();

IsResource and IsNotResource

Filter for resources

$result = Psi::it($input)->filter(new Psi\IsResource())->toArray();

$result = Psi::it($input)->filter(new Psi\IsNotResource())->toArray();

IsScalar and IsNotScalar

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 - comparison

IsEqualTo and IsNotEqualTo

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();

UnitTests

First install all dependencies

./composer install

Then run all tests

vendor/bin/phpunit

Releases

new in v1.2.0

REMOVED PHP 5.6 SUPPORT

Added

  • Psi::all()
  • Psi::any()

new in v1.1.0

Moved mapper ToFloat, ToInteger, ToString to Psi\Map...

Old versions are kept for compatibility and marked as deprecated.

new in v0.6.4

Psi::chunk

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]
]

Psi::skip

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]

Psi::limit

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]

Psi::takeWhile

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]

Psi::takeUntil

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]

Psi::getLast

Get the last element of the stream.

Psi::it([1, 2, 3])
  ->filter(function ($i) { return $i < 3; })
  ->getLast()

will result in

2

Psi::getRandom

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

Num::IsMultipleOf and Num::IsNotMultipleOf

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] 

Num::IsPrime and Num::IsNotPrime

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]

new in v0.6.3

Psi::filterBy()

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()

Psi::uniqueBy

Finds unique elements by first passing them through a mapper.

$result = Psi::it($persons)
  ->uniqueBy(
    function (Person $p) { return $person->getName(); }
  )
  ->toArray();

ToFloat

Maps inputs to floats.

$result = Psi::it($values)
  ->map(new Psi\ToFloat())
  ->toArray();

ToInteger

Maps inputs to integers.

$result = Psi::it($values)
  ->map(new Psi\ToInteger())
  ->toArray();

ToString

Maps inputs to strings.

$result = Psi::it($values)
  ->map(new Psi\ToString())
  ->toArray();

Str::IsStartingWith and Str::IsNotStartingWith - with or with case

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();

Str::IsEndingWith and Str::IsNotEndingWith - with or without case

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();

Str::IsContaining and Str::IsNotContaining - with or without case

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();

Str::IsMatchingRegex and Str::IsNotMatchingRegex

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();

Str::WithoutAccents

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();

new in v0.6.0 to v0.6.2

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"

new in v0.5.0

Psi::groupBy

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' => ... ]  

Psi::sortBy

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()

Todos and ideas

general

  • make custom TerminalOperations possible Psi::reduce()

Unary filter functions for strings

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

peekandpoke/psi 适用场景与选型建议

peekandpoke/psi 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 5.5k 次下载、GitHub Stars 达 2, 最近一次更新时间为 2015 年 05 月 09 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 peekandpoke/psi 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 peekandpoke/psi 我们能提供哪些服务?
定制开发 / 二次开发

基于 peekandpoke/psi 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 5.5k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 2
  • 点击次数: 8
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 2
  • Watchers: 2
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: BSD-3-Clause
  • 更新时间: 2015-05-09