slava-basko / functional-php
一组PHP函数,允许您以声明方式编写代码。
Requires
- php: ^5.5|^7|^8
Requires (Dev)
- ext-json: *
- phpunit/phpunit: ^4|^5|^7|^8|^9
- squizlabs/php_codesniffer: 3.*
This package is auto-updated.
Last update: 2024-09-13 20:44:25 UTC
README
一组PHP函数,允许您以声明方式编写代码。
通用 ⚙️
命名约定
使用 snake_case 以更接近PHP原生函数。
无依赖
此库不依赖于任何外部库。
PHP 5.5+
支持PHP版本从5.5开始。为什么?因为遗留项目也值得采用一些函数式方法。
"数据最后"原则
要操作的数据通常在最后提供(最后函数参数)。这种方式使得函数更便于柯里化。
函数默认柯里化
这允许我们通过不提供最终参数,更高效地从旧函数中构建新函数。
最后两点结合使得构建函数序列变得容易,每个函数都转换数据并将其传递给下一个。
文档 📚
在这里您可以找到可用的函数。
其他有用的东西。
- 身份 / 即简单容器
- 常量 / 保持值不变的容器
- 也许 / 包含
value或Nothing的容器 - 要么 / 包含两个值的容器,
Left即错误和Right即成功 - 可选 / 包含
value包括 NULL 或Nothing的容器 - IO / 包含返回值的纯函数的容器
OOP 🤝 FP
此库的目的不是取代命令式和OOP。它们可以结合使用,我相信它们应该结合使用,因为任何一种方法都不是万能的。
我将省略关于函数式编程的理论,因为您自己可以找到很多关于它的信息。但我想给您展示一些示例。
集合示例
让我们假设您正在使用集合库,并且想要将所有元素转换为大写。您需要编写如下内容
$collection = new Collection(['one']); $collection->map(function ($value) { return strtoupper($value); });
当您编写 $collection->map('strtoupper'); 时,您可能会得到一个错误,例如 ArgumentCountError : strtoupper() expects exactly 1 argument, X given。只有用户定义的函数在调用时带有更多参数时不会抛出异常。但您可以这样做
$collection = new Collection(['one']); $collection->map(unary('strtoupper'));
bam! 您得到了更简洁的代码,没有 function、{、return、}、;。函数 unary 是一个高阶函数,它接受任意数量的函数并返回只接受一个参数的新函数。
这就是我所说的将命令式/OOP和函数式代码结合起来的意思。
集合的另一个示例。我们需要通过 isActive 方法过滤用户,例如。
$collection = new Collection([$user1, $user2, $user3]); $collection->filter(function ($user) { return $user->isActive(); }); // VS $collection->filter(invoker('isActive'));
无参数示例
现在让我们考虑第二个示例,当我们需要按顺序计算商品数量时。
$products = [ [ 'description' => 't-shirt', 'qty' => 2, 'value' => 20 ], [ 'description' => 'jeans ', 'qty' => 1, 'value' => 30 ], [ 'description' => ' boots', 'qty' => 1, 'value' => 40 ], ]; $imperativeTotalQty = 0; foreach ($products as $product) { $imperativeTotalQty += $product['qty']; } // OR $totalQty = compose(sum, pluck('qty'))($products);
您可以将代码 compose(sum, pluck('qty')) 读取为 'quantity' 属性的总和。好吧,我理解这可能对您来说有点奇怪。您习惯了以不同的方式编写代码。
管道和部分应用
我们有一个 $products[],我们需要从每个商品的 description 属性中创建一个通用描述。所以,这里有一些基本步骤
- 从产品中获取属性 'description'。
- 去除每个值的首尾空白字符。
- 删除空元素。
- 用逗号连接元素。
- 将生成的描述截断到34个字符。
- 如果存在,删除结尾的逗号。
命令式的方法可能是
$commonDescription = trim(substr(implode(', ', array_filter(array_map('trim', array_column($products, 'description')), 'strlen')), 0, 34), ', '); // OR $commonDescription = trim( substr( implode( ', ', array_filter( array_map( 'trim', array_column($products, 'description') ), 'strlen' ) ) , 0, 34 ), ', ' );
认知负荷相当大 🤯。让我们重新排序并使其更易于阅读。
$descriptions = array_column($products, 'description'); $trimmedDescriptions = array_map('trim', $descriptions); $nonEmptyDescriptions = array_filter($trimmedDescriptions, 'strlen'); $descriptionString = implode(', ', $nonEmptyDescriptions); $shortDescription = substr($descriptionString, 0, 34); $commonDescription = trim($shortDescription, ', ');
现在它更容易阅读了,但我们需要处理状态。
函数式代码可能如下所示
$commonDescription = pipe( pluck('description'), map(unary('trim')), select(unary('strlen')), join(', '), take(34), partial_r('trim', ', ') )($products);
这正是我们需要的。它按自然顺序排列。没有中间状态。
另一个例子,其中我们需要获取用户的首字母。
$initials = pipe( partial('explode', ' '), map(pipe(take(1), partial_r(concat, '.'))), join(' ') )('Slava Basko'); // $initials = 'S. B.'
我知道,也许那一行看起来有点奇怪,但无需在每个步骤停下来考虑控制流结构和参数命名,就可以组合函数的想法非常强大。
有什么现实生活中的例子吗? 🤔
没问题,这个项目有一个自动生成文档的脚本。完全以纯函数式的方式编写。
在脚本开发过程中没有损坏任何变量。
许可证 ⚖️
随意使用。我没有任何责任或保证。可以视为MIT。