peekandpoke / psi
PHP 简单迭代。受 Java Streams Api 启发。
Requires
- php: >=7.0
- ext-pcre: *
- peekandpoke/php-types: ^1.2.0
- symfony/polyfill-mbstring: ^1.6.0
Requires (Dev)
- phpunit/phpunit: ^6.5.9
This package is not auto-updated.
Last update: 2024-09-28 16:10:19 UTC
README
介绍
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