PHP 简单迭代。受 Java Streams Api 启发。

v1.2.0 2018-09-17 12:06 UTC

README

Code Coverage Scrutinizer Code Quality Build Status

介绍

PSI 帮助您编写更干净、更稳定的 PHP 代码。特别是在迭代、过滤、映射、排序数组或类似数组的数据时。

PSI 由多个小型操作组成,可以组合和链式调用。这样做会使代码更易于阅读且更具表现力。

PSI 支持 PHP 版本 >= 7.0

目录

示例

让我们看一下一些示例。

获取所有成年人

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

在纯 PHP 中,这可能会看起来像

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

在纯 PHP 中,这可能会看起来像

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

映射

使用 Psi::map(),您可以将传入的数据进行转换。

例如,让我们将所有人员转换为整数(人员的年龄)

/** @var Person[] $input */
$input = $service->getStuff();

$result = Psi::it($input)
  ->map(function (Person $p) { return $p->getAge(); })
  ->toArray();

这可能会导致

[10, 20, 18, 31, ...]

我们还可以将每个 Person 转换为其他东西。

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

这可能会导致

[["Elsa", 20], ["Joe", 10], ["John", 18], ["Mary", 31], ...]

高级过滤

使用 Psi::filterBy(),您可以将映射和过滤组合起来。

让我们通过使用 Psi\IsGreaterThanOrEqual() 检查年龄来获取所有成年人。

下面的示例也将给出所有年龄大于或等于 18 的 Person。

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

检查所有或任何元素是否匹配

使用 Psi::all(),您可以检查所有元素是否匹配给定的条件

$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

使用 Psi::any(),您可以检查至少有一个元素匹配条件

$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

分组

让我们按年龄分组所有人员

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

这可能输出

array(3) {
  [7] =>
  array(2) { 
    [0] =>
    object(Person) ...
    [1] =>
    object(Person) ...
  }
  [15] =>
  array(1) {
    [0] =>
    object(Person) ...
    ...
  }
  [21] =>
  array (2) {
    ...
  }
  ...  
}

多个输入

您可以将多个数组或类似数组参数传递给 Psi::it(... $inputs)

/** @var Person[] $result */
$result = Psi::it(
    $service->getStuff(),
    $service->getMoreStuff(),
    $service->getEvenMoreStuff()    
  )
  ->filter(new Psi\IsInstanceOf(Person::class))
  ->toArray();

跳过和限制

让我们获取最多 5 个成年人,但跳过前 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();

或者,让我们跳过前 10 个,不管它们是什么,然后获取最多 5 个成年人

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

或者,让我们跳过前 10 个,然后获取最多 5 个,然后从这些中过滤出成年人

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

计数、求和、最小值、最大值、平均值、中位数

让我们计算成年人的数量

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

让我们计算所有人员的年龄总和

$input = $service->getStuff();

/** @var float $result */
$result = Psi::it($input)  
  ->filter(new Psi\IsInstanceOf(Person::class))
  ->map(function (Person $p) { return $p->getAge(); })
  ->sum();

让我们获取我们所知所有人的最小年龄

$input = $service->getStuff();

/** @var float $result */
$result = Psi::it($input)  
  ->filter(new Psi\IsInstanceOf(Person::class))
  ->map(function (Person $p) { return $p->getAge(); })
  ->min();

让我们获取所有人的最大年龄

$input = $service->getStuff();

/** @var float $result */
$result = Psi::it($input)  
  ->filter(new Psi\IsInstanceOf(Person::class))
  ->map(function (Person $p) { return $p->getAge(); })
  ->max();

让我们获取平均年龄

$input = $service->getStuff();

/** @var float $result */
$result = Psi::it($input)  
  ->filter(new Psi\IsInstanceOf(Person::class))
  ->map(function (Person $p) { return $p->getAge(); })
  ->avg();

让我们获取中位数年龄

$input = $service->getStuff();

/** @var float $result */
$result = Psi::it($input)  
  ->filter(new Psi\IsInstanceOf(Person::class))
  ->map(function (Person $p) { return $p->getAge(); })
  ->median();

让我们获取所有成年人的中位数年龄

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

获取第一个、最后一个、随机一个

让我们获取符合特定条件(名字以 "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()
    

让我们获取符合特定条件(名字以 "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()
    

让我们获取符合特定条件(名字以 "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()
    

详细文档

过滤器 - 类型检查

IsArray 和 IsNotArray

使用 is_array() 对数组进行过滤

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

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

IsBool 和 IsNotBool

使用 is_bool() 对布尔值进行过滤

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

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

IsCallable 和 IsNotCallable

使用 is_callable() 对可调用对象进行过滤

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

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

IsDateString 和 IsNotDateString

检查给定的字符串是否是 new \DateTime($str) 能够理解的字符串

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

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

IsEmpty 和 IsNotEmpty

使用 empty() 对空值进行过滤

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

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

IsInstanceOf 和 IsNotInstanceOf

对类实例进行过滤

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

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

IsInteger 和 IsNotInteger

使用 is_int() 对整数进行过滤

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

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

IsIntegerString 和 IsNotIntegerString

对包含整数的字符串进行过滤

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

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

IsNull 和 IsNotNull

对 null 进行过滤

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

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

IsNumeric 和 IsNotNumeric

使用 is_numeric() 对数值进行过滤

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

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

IsObject 和 IsNotObject

对对象进行过滤

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

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

IsResource 和 IsNotResource

对资源进行过滤

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

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

IsScalar 和 IsNotScalar

使用is_scalar()过滤标量值

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

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

过滤 - 比较操作

IsEqualTo 和 IsNotEqualTo

过滤非类型安全的等价性 (==)。使用 IsSame / IsNotSame 进行 === 比较

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

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

单元测试

首先安装所有依赖项

./composer install

然后运行所有测试

vendor/bin/phpunit

版本发布

新功能 v1.2.0

已移除 PHP 5.6 支持

新增

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

新功能 v1.1.0

将 Mapper ToFloat, ToInteger, ToString 移至 Psi\Map...

保留旧版本以兼容性,并标记为已弃用。

新功能 v0.6.4

Psi::chunk

将流分割为指定大小的块。

Psi::it(range(0, 10))
  ->chunk(3)
  ->toArray();

结果将是

[
  [0, 1, 2], 
  [3, 4, 5], 
  [6, 7, 8], 
  [9, 10]
]

Psi::skip

跳过当前流中的前 n 个元素

Psi::it(range(0, 20))
  ->filter(new IsMultipleOf(2))
  ->skip(5)
  ->toArray();

结果将是

[10, 12, 14, 16, 18, 20]

Psi::limit

将结果限制为当前流中的前 n 个元素

Psi::it(range(0, 20))
  ->filter(new IsMultipleOf(2))
  ->limit(5)
  ->toArray();

结果将是

[0, 2, 4, 6, 8]

Psi::takeWhile

在条件满足时,获取输入流的全部元素

Psi::it(range(0, 20))
  ->takeWhile(new Psi\IsLessThan(5))
  ->toArray();

结果将是

[0, 1, 2, 3, 4]

Psi::takeUntil

在条件满足时,获取输入流的全部元素

Psi::it(range(0, 20))
  ->takeUntil(new Psi\IsGreaterThan(5))
  ->toArray();

结果将是

[0, 1, 2, 3, 4, 5]

Psi::getLast

获取流中的最后一个元素。

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

结果将是

2

Psi::getRandom

将从流中随机选择一个元素。

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

结果将是

1 or 2

Num::IsMultipleOf 和 Num::IsNotMultipleOf

过滤所有给定因子的倍数

Psi::it([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
  ->filter(new Psi\Num\IsMultipleOf(3))
  ->toArray();

结果将是

[0, 3, 6, 9] 

Num::IsPrime 和 Num::IsNotPrime

过滤所有质数

注意:不要为此实现使用大数,因为它可能非常慢。

Psi::it(range(0, 30))
    ->filter(new Psi\Num\IsPrime())
    ->toArray();

结果将是

[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

新功能 v0.6.3

Psi::filterBy()

为了能够使用所有其他匹配器在输入对象的嵌套属性/值上,添加了 Psi::filterBy() 方法

$result = Psi::it($persons)
  ->filterBy(
    function (Person $p) { return $p->getAge(); },
    new IsLessThan(18)
  )
  ->toArray()

Psi::uniqueBy

通过首先将它们通过映射器传递来查找唯一元素

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

ToFloat

将输入映射到浮点数。

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

ToInteger

将输入映射到整数。

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

ToString

将输入映射到字符串。

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

Str::IsStartingWith 和 Str::IsNotStartingWith - 带或不带大小写

过滤所有以给定针值为开头的字符串。默认情况下为区分大小写。将 false 作为第二个参数传递以实现非区分大小写。

$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 和 Str::IsNotEndingWith - 带或不带大小写

过滤所有以给定针值为结尾的字符串。默认情况下为区分大小写。将 false 作为第二个参数传递以实现非区分大小写。

$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 和 Str::IsNotContaining - 带或不带大小写

过滤所有包含给定针值的字符串。默认情况下为区分大小写。将 false 作为第二个参数传递以实现非区分大小写。

$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 和 Str::IsNotMatchingRegex

过滤所有包含给定针值的字符串。默认情况下为区分大小写。将 false 作为第二个参数传递以实现非区分大小写。

$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

通过将特殊字符替换为“正常”形式来修改字符串,例如。

Dragoş  ->  Dragos

Ärmel   ->  Aermel
Blüte   ->  Bluete
Straße  ->  Strasse

passé   ->  passe
$result = Psi::it($values)
  ->map(new Psi\Str\WithoutAccents())
  ->toArray();

新功能 v0.6.0 到 v0.6.2

内部文件夹结构已更改

  • 公共接口现在位于根级别
  • 移除了一些过度设计(删除了一些类)
  • 接口名称不再像“UnaryFunctionInterface”那样,而是“UnaryFunction”

新功能 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() 

将变为

['a' => [ ['name' => 'a', 'val' => 1], ['name' => 'a', 'val' => 2] ], 'b' => ... ]  

Psi::sortBy

按对象的某个属性对对象列表进行排序,例如按年龄对人员进行排序

$result = Psi::it($values)
  ->filterBy(
    function (Person $p) { return $p->getAge(); }
  )
  ->toArray()

待办事项和想法

通用

  • 使自定义 TerminalOperations 成为可能 Psi::reduce()

字符串的单个过滤器函数

字符串-Mapper

  • Str::Camelize (StrToCamelCase)
  • ... camel to dashes (StrToSlug)
  • Str::UcFirst, Str::LcFirst
  • Str::StrReplace
  • Str::StrMbReplace
  • Str::StrRegexReplace
  • Str::StrMbRegexReplace

... for PHP-Types ... LocalDate::isSameDay