pinkcrab / function-constructors
这是一个函数集合,使得使用标准PHP库进行函数组合变得更加容易。它允许创建部分应用库函数,尽可能地接近纯函数。
Requires
- php: >=7.1.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3
- phpstan/phpstan: ^1.0
- phpunit/phpunit: ^7.0 || ^9.0
- yoast/phpunit-polyfills: ^1.0.0
- dev-master
- 0.2.0
- 0.2.0-rc6
- 0.2.0-rc5
- 0.2.0-rc4
- 0.2.0-rc3
- 0.2.0-rc2
- 0.2.0-rc1
- 0.1.3
- 0.1.2
- 0.1.1
- dev-develop
- dev-feature/gh85-array-only
- dev-feature/gh79-arrays-append-prepend
- dev-feature/gh73-use-mb_string-functions
- dev-feature/gh59-fix-scruitinizer-issues
- dev-feature/add-clone-with-object
- dev-feature/gh57-add-has-trait-to-objects
- dev-feature/expand-string-split
- dev-rel2.0.0
- dev-feature/gh53-add-each-to-arrays
- dev-feature/gh53-add-map-with-keys
- dev-feature/gh51-include-filterall-filterany-to-arrays
- dev-feature/add-objects-to-autoloader-file-list
- dev-hotfix/objects-not-included-in-loader-files
- dev-feature/gh48-move-to-array-to-objects
- dev-feature/gh46-reverse-vpsrintf
- dev-feature/gh40-add-missing-tests
- dev-feature/gh43-remove-html-methods
- dev-feature/gh41-tagWrap
- dev-feature/update-readme
- dev-feature/gh37-add-factor-of
- dev-feature/gh29-create-array-take-composables
- dev-revert-32-feature/add-fold-family
- dev-feature/add-fold-family
- dev-feature/add-scan
- dev-feature/gh22-update-pipe
- dev-feature/gh15-update-docblocks
- dev-feature/gh19-string-isblank
- dev-feature/gh11-add-simple-function-constants
- dev-feature/gh16-add-or-equal-to-number-comparissons
- dev-feature/gh14-reverse-greather-than-or-less-than
- dev-feature/gh12-update-testing-deps
- dev-feature/issue9-create-iterable-functions-to-replace-array
- dev-gin0115-actions-setup
This package is auto-updated.
Last update: 2024-09-23 06:06:27 UTC
README
这个库提供了一组函数,旨在让PHP中的函数式编程更加简洁和容易。
设置
可以使用Composer将其包含到您的项目中,或者手动添加到您的代码库中。
通过Composer
$ composer require pinkcrab/function-constructors
通过手动加载器
如果您希望在WordPress或其他无法使用Composer的PHP代码库中使用此库,您可以使用FunctionsLoader
类。只需将存储库克隆到您的代码库中,并执行以下操作。
require_once 'path/to/cloned/repo/FunctionsLoader.php'; FunctionsLoader::include();
所有函数都使用命名空间PinkCrab\FunctionConstructors\{lib}
。因此,使用别名是最简单的方法。在整个wiki文档中,我们使用了以下别名。
use PinkCrab\FunctionConstructors\Arrays as Arr; use PinkCrab\FunctionConstructors\Numbers as Num; use PinkCrab\FunctionConstructors\Strings as Str; use PinkCrab\FunctionConstructors\Comparisons as C; use PinkCrab\FunctionConstructors\GeneralFunctions as F; use PinkCrab\FunctionConstructors\Objects as Obj; // Allowing for Arr\Map('esc_html') or Str\append('foo') or F\pipe($var, 'strtoupper', Str\append('foo'))
用法
Function Constructors库的核心设计是让PHP在函数式编程中使用起来更加容易。使用compose()
和pipe()
函数,可以构造出从简单到复杂的函数。
函数组合和管道
pipe()
请注意,从版本2.0.0开始,现在推荐使用
compose()
。
使用pipe(mixed $value, callable ...$callables)
和pipeR()
,您可以将一个值通过一系列可调用对象传递。第一个函数的结果作为第二个函数的输入,依此类推,直到最终返回结果。
此库的其余部分通过定义一些参数来简化使用标准PHP函数作为可调用对象。
$data = [0,3,4,5,6,8,4,6,8,1,3,4]; // Remove all odd numbers, sort in an acceding order and double the value. $newData = F\pipe( $data, Arr\filter(Num\isMultipleOf(2)), // Remove odd numbers Arr\natsort(), // Sort the remaining values Arr\map(Num\multiply(2)) // Double the values. ); // Result $newData = [ 2 => 8, 6 => 8, 11 => 8, 4 => 12, 7 => 12, 5 => 16, 8 => 16, ];
compose()
当您处理单个值时,管道是理想的,但当您需要处理数组或编写回调时,compose()
就非常有用了。
compose(callable ...$callables)
、composeR(callable ...$callables)
、composeSafe(callable ...$callables)
和composeTypeSafe(callable $validator, callable ...$callables)
都允许您创建自定义闭包。
$data = [ ['details'=>['description' => ' This is some description ']], ['details'=>['description' => ' This is some other description ']], ]; $callback = F\compose( F\pluckProperty('details','description'), // Plucks the description 'trim', // Remove all whitespace Str\slice(0, 20), // Remove all but first 20 chars 'ucfirst', // Uppercase each word Str\prepend('...') // End the string with ... ); $results = array_map($callback, $data); $results = [ 'This Is Some Descrip...', 'This Is Some Other D...' ]
如果您希望在将可调用对象的返回值传递给下一个之前通过验证器验证,可以使用
composeTypeSafe()
。如果验证失败,则跳过链的其余部分并返回null。
与记录一起工作
可以使用一些GeneralFunctions
来检查、获取和设置记录(数组或对象)的属性。可以使用索引或属性。
读取属性
您可以检查属性是否存在,获取其值或将其与定义的值进行比较。
$data = [ ['id' => 1, 'name' => 'James', 'timezone' => '+1', 'colour' => 'red'], ['id' => 2, 'name' => 'Sam', 'timezone' => '+1', 'colour' => 'red', 'special' => true], ['id' => 3, 'name' => 'Sarah', 'timezone' => '+2', 'colour' => 'green'], ['id' => 4, 'name' => 'Donna', 'timezone' => '+2', 'colour' => 'blue', 'special' => true], ]; // Filter all users with +2 timezone. $zonePlus2 = array_filter($data, F\propertyEquals('timezone','+2')); $results = [['id' => 3, ....],['id' => 4, ...]]; // Filter all user who have the special index. $special = array_filter($data, F\hasProperty('special')); $results = [['id' => 2, ....],['id' => 4, ...]]; // Get a list of all colours. $colours = array_map(F\getProperty('colour'), $data); $results = ['red', 'red', 'green', 'blue'];
pluckProperty()
也可以用于需要遍历数组的嵌套属性或对象属性的情况(也处理使用数组语法设置的ArrayAccess
对象)。
写入属性
还可以使用 setProperty()
函数来编写对象的属性,并将值设置到数组的索引中。还可以使用 记录编码器 创建更复杂的结构。
// Set object property. $object = new class(){ public $key = 'default'}; // Create a custom setter function. $setKeyOfObject = F\setProperty($object, 'key'); $object = $setKeyOfObject('new value'); // {"key":"new value"} // Can be used with arrays too $array = ['key' => 'default']; // Create a custom setter function. $setKeyOfSArray = F\setProperty($array, 'key'); $array = $setKeyOfSArray('new value'); // [key => "new value"]
字符串函数
库中许多字符串函数都作为常见标准(PHP)库函数的包装器,但经过柯里化,可以更容易地组合使用。
字符串操作
有一系列函数用于字符串的连接。
$appendFoo = Str\append('foo'); $result = $appendFoo('BAR'); $prependFoo = Str\prepend('foo'); $result = $prependFoo('BAR'); $replaceFooWithBar = Str\replaceWith('foo', 'bar'); $result = $replaceFooWithBar("its all a bit foo foo"); // "its all a bit bar bar" $wrapStringWithBar = Str\wrap('bar-start-', '-bar-end'); $result = $wrapStringWithBar('foo'); // bar-start-foo-bar-end
字符串内容
有一系列函数可以用来检查字符串的内容。
// Check if a string contains $containsFoo = Str\contains('foo'); $containsFoo('foo'); // true $containsFoo('fobar'); // false // Check if string start with (ends with also included) $startsBar = Str\startsWith('bar'); $startsBar('bar-foo'); // true $startsBar('foo-bar'); // false // Check if a blank string Str\isBlank(''); // true Str\isBlank(' '); // false // Unlike using empty(), this checks if the value is a string also. Str\isBlank(0); // false Str\isBlank(null); // false // Contains a regex pattern $containsNumber = Str\containsPattern('~[0-9]+~'); $containsNumber('apple'); // false $containsNumber('A12DFR3'); // true
Str\isBlank()
可以在组合函数时使用,多亏了Functions::isBlank
常量。
$data = [0 => '', 1 => 'fff', 2 => ' ']; $notBlanks = array_filter(PinkCrab\FunctionConstructors\Functions::IS_BLANK, $data); // [0 => '']
子字符串
有一系列函数可以用来处理子字符串。
// Split the string into sub string $inFours = Str\split(4); $split = $inFours('AAAABBBBCCCCDDDD'); // ['AAAA','BBBB','CCCC','DDDD'] // Chunk the string $in5s = Str\chunk(5, '-'); $result = $in5s('aaaaabbbbbccccc'); // 'aaaaa-bbbbb-ccccc-' // Count all characters in a given string. $charCount = Str\countChars(); $results = $charCount('Hello World'); // [32 => 1, 72 => 1, 87 => 1, 100 => 1, 101 => 1, 108 => 3, 111 => 2, 114 => 1] // If the keys are mapped using chr(), you will get $results = (Arr\mapKey('chr')($results)); // ['H' => 1,'e' => 1,'l' => 3,'o' => 2,' ' => 1,'W' => 1,'r' => 1,'d' => 1,] // Count occurrences of a substring. $countFoo = Str\countSubString('foo'); $results = $countFoo('foo is foo and bar is not foo'); // 3 // Find the first position of foo in string. $firstFoo = Str\firstPosition('foo'); $result = $firstFoo('abcdefoog'); // 5
更多字符串函数请参阅 维基页面
数字函数
库中许多数字函数作为常见标准(PHP)库函数的包装器,但经过柯里化,可以更容易地组合使用。
基本算术
您可以使用可组合的函数进行一些基本的算术运算。这允许创建一个基本值,然后使用传递的值进行操作。
所有这些函数只允许使用
INT
或FLOAT
,所有数字字符串在使用之前都必须进行类型转换。否则将抛出TypeError
。
// Add $addTo5 = Num\sum(5); $addTo5(15.5); // 20.5 $addTo5(-2); // 3 // Subtract $subtractFrom10 = Num\subtract(10); $subtractFrom10(3) // 7 $subtractFrom10(20) // -10 // Multiply $multiplyBy10 = Num\multiply(10) $multiplyBy10(5); // 50 $multiplyBy10(2.5); // 25.0 // Divide By $divideBy3 = Num\divideBy(3); $divideBy3(12); // 4 = 12/3 $divideBy3(10); // 3.333333333333 // Divide Into $divideInto12 = Num\divideInto(12); $divideInto12(4); // 3 = 12/4
乘法和模数
可以进行基本的模数运算,并确定一个数是否是另一个数的整数倍。
// Factor of $isMultipleOf2 = Num\isMultipleOf(2); $isMultipleOf2(12); // true $isMultipleOf2(13); // false // Getting the remainder $remainderBy2 = Num\remainderBy(2); $remainderBy2(10); // 0 = (5 * 2) - 10 $remainderBy2(9); // 1 = (4 * 2) - 9
数组函数
正如您所想象的,有许多与数组和它们操作相关的函数。
映射
该库包含许多 array_map
的变体,所有这些都可以预先组合,使用其他函数,功能强大且易于理解。
// Create a mapper which doubles the value. $doubleIt = Arr\map( Num\multiply(2) ); $doubleIt([1,2,3,4]); // [2,4,6,8] // Create mapper to normalise array keys $normaliseKeys = Arr\mapKey(F\compose( 'strval', 'trim', Str\replace(' ', '-') Str\prepend('__') )); $normaliseKeys(1 => 'a', ' 2 ' => 'b', 'some key' => 'c'); // ['__1'=> 'a', '__2' => 'b', '__some-key' => 'c'] // Map and array with the value and key. $mapWithKey = Arr\mapWithKey( function($key, $value) { return $key . $value; }); $mapWithKey('a' => 'pple', 'b' => 'anana'); // ['apple', 'banana']
还包括
flatMap()
和mapWith()
,请参阅维基。
过滤和取
有一系列基于 array_filter()
的可组合函数。结合一组基本的 take*()
函数,您可以更容易地组合函数以处理列表/集合。
// Filter out ony factors of 3 $factorsOf3s = Arr\filter( Num\factorOf(3) ); $factorsOf3s([1,3,5,6,8,7,9,11]); // [3,6,9] // Filer first and last of an array/ $games = [ ['id'=>1, 'result'=>'loss'], ['id'=>2, 'result'=>'loss'], ['id'=>3, 'result'=>'win'], ['id'=>4, 'result'=>'win'], ['id'=>5, 'result'=>'loss'], ]; $firstWin = Arr\filterFirst( F\propertyEquals('result','win') ); $result = $firstWin($games); // ['id'=>3, 'result'=>'win'] $lastLoss = Arr\filterLast( F\propertyEquals('result','loss') ); $result = $lastLoss($games); // ['id'=>5, 'result'=>'loss'] // Count result of filter. $totalWins = Arr\filterCount( F\propertyEquals('result','win') ); $result = $totalWins($games); // 2
如果只想处理集合中的每个结果,过滤非常有用,而
take()
函数系列允许控制过滤数组的大小。
// Take the first or last items from an array $first5 = Arr\take(5); $last3 = Arr\takeLast(5); $nums = [1,3,5,6,8,4,1,3,5,7,9,3,4]; $first5($nums); // [1,3,5,6,8] $last3($nums); // [9,3,4] // Using takeWhile and takeUntil to get the same result. $games = [ ['id'=>1, 'result'=>'loss'], ['id'=>2, 'result'=>'loss'], ['id'=>3, 'result'=>'win'], ['id'=>4, 'result'=>'win'], ['id'=>5, 'result'=>'loss'], ]; // All games while the result is a loss, then stop $initialLoosingStreak = Arr\takeWhile(F\propertyEquals('result','loss')); // All games until the first win, then stop $untilFirstWin = Arr\takeUntil(F\propertyEquals('result', 'win')); $result = $initialLoosingStreak($game); $result = $untilFirstWin($game); // [['id' => 1, 'result' => 'loss'], ['id' => 2, 'result' => 'loss']]
折叠和扫描
折叠或归约列表是一个相当常见的操作,与原生的 array_reduce
不同,您有更多的灵活性。
$payments = [ 'gfg1dg3d' => ['type' => 'card', 'amount' => 12.53], 'eg43ytfh' => ['type' => 'cash', 'amount' => 21.95], '5g7tgxfb' => ['type' => 'card', 'amount' => 1.99], 'oitu87uo' => ['type' => 'cash', 'amount' => 4.50], 'ew1e5435' => ['type' => 'cash', 'amount' => 21.50], ]; // Get total for all cash payment. $allCash = Arr\fold(function($total, $payment){ if($payment['type'] === 'cash'){ $total += $payment['amount']; } return $total; },0.00); $result = $allCash($payments); // 47.95 // Log all card payment in some class, with access to array keys. $logCardPayments = Arr\foldKeys(function($log, $key, $payment){ if($payment['type'] === 'card'){ $log->addPayment(payment_key: $key, amount: $payment['amount']); } return $log; }, new CardPaymentLog('some setup') ); $cardPaymentLog = $logCardPayments($payments); var_dump($cardPayments->getPayments()); // [{'key': 'gfg1dg3d', 'amount': 12.53}, {'key': '5g7tgxfb', 'amount': 1.99}] // Generate a running total of all payments. $runningTotal = Arr\scan(function($runningTotal, $payment){ $runningTotal += $payment['amount']; return $runningTotal; }, 0.00); $result = $runningTotal($payments); // [0.0, 12.53, 34.48, 36.47, 40.97, 62.47]
您还可以访问
foldR()
和scanR()
,它们将反向遍历数组。
分组和分区
函数构造器有几个函数,可以轻松地对数组进行分组和分区。
$data = [ ['id'=>1, 'name'=>'John', 'age'=>20, 'someMetric' => 'A12'], ['id'=>2, 'name'=>'Jane', 'age'=>21, 'someMetric' => 'B10'], ['id'=>3, 'name'=>'Joe', 'age'=>20, 'someMetric' => 'C15'], ['id'=>4, 'name'=>'Jack', 'age'=>18, 'someMetric' => 'B10'], ['id'=>5, 'name'=>'Jill', 'age'=>22, 'someMetric' => 'A12'], ]; // Group by the return value of the function. $groupedByMetric = Arr\groupBy(function($item){ return $item['someMetric']; }); $results = $groupedByMetric($data); ["A12" => [ ["id" => 1,"name" => "John", ...], ["id" => 5,"name" => "Jill", ...] ], "B10" => [ ["id" => 2,"name" => "Jane", ...], ["id" => 4,"name" => "Jack", ...] ], "C15" => [ ["id" => 3,"name" => "Joe", ...] ]]; // Partition using a predicate function. $over21 = Arr\partition(function($item){ return $item['age'] >= 21; }); $results = $over21($data); [0 => [ // false values ["name" => "John", "age" => 20, ...], ["name" => "Joe", "age" => 20, ...], ["name" => "Jack", "age" => 18, ...] ], 1 => [ // true values ["name" => "Jane", "age" => 21, ...], ["name" => "Jill", "age" => 22, ...] ]];
可以分块和拆分数组,更多详情请参阅维基。
排序
由于原生 PHP 的 sort
函数通过引用排序,而不是通过返回值排序,因此使用函数式方法处理起来有些棘手。函数构造器库涵盖了所有原生排序作为部分应用函数。
// Sorting simple arrays $dataWords = ['Zoo', 'cat', 'Dog', 'ant', 'bat', 'Cow']; $sortWords = Arr\sort(SORT_STRING); $result = $sortWords($dataWords); // ['ant', 'bat', 'cat', 'Cow', 'Dog', 'Zoo']; // Sorting associative arrays $dataBooks = [ 'ehjf89' => ['id'=>'ehjf89', 'title'=>'Some title', 'author'=> 'Adam James'], 'retg23' => ['id'=>'retg23', 'title'=>'A Title', 'author'=> 'Jane Jones'], 'fvbi43' => ['id'=>'fvbi43', 'title'=>'Some title words', 'author'=> 'Sam Smith'], 'mgged3' => ['id'=>'mgged3', 'title'=>'Book', 'author'=> 'Will Adams'], ]; // Sort by key $sortBookByKey = Arr\ksort(SORT_STRING | SORT_FLAG_CASE); $result = $sortBookByKey($dataBooks); [ 'ehJF89' => ['id' => 'ehjf89', 'title' => 'Some title', 'author' => 'Adam James'], 'fvbI43' => ['id' => 'fvbi43', 'title' => 'Some title words', 'author' => 'Sam Smith'], 'MggEd3' => ['id' => 'mgged3', 'title' => 'Book', 'author' => 'Will Adams'], 'Retg23' => ['id' => 'retg23', 'title' => 'A Title', 'author' => 'Jane Jones'], ] // Sort by author $sortBookByAuthor = Arr\uasort(function ($a, $b) { return strcmp($a['author'], $b['author']); }); $sortBookByAuthor($dataBooks); [ 'ehJF89' => ['id' => 'ehjf89', 'title' => 'Some title', 'author' => 'Adam James'], 'Retg23' => ['id' => 'retg23', 'title' => 'A Title', 'author' => 'Jane Jones'], 'fvbI43' => ['id' => 'fvbi43', 'title' => 'Some title words', 'author' => 'Sam Smith'], 'MggEd3' => ['id' => 'mgged3', 'title' => 'Book', 'author' => 'Will Adams'], ]
贡献
如果您想为此项目做出贡献,请随意在 GitHub 上fork该项目并提交一个 pull request。
有关更多详细信息,请阅读 维基
更改
-
0.2.0 -
- 新函数
Numbers\isMultipleOf()
Numbers\isFactorOf()
Strings\isBlank()
Strings\splitByLength()
GeneralFunctions\ifThen()
GeneralFunctions\ifElse()
GeneralFunctions\composeR()
Arrays\fold()
Arrays\foldR()
Arrays\foldKey()
Arrays\scan()
数组\scanR()
数组\take()
数组\takeLast()
数组\takeUntil()
数组\takeWhile()
数组\filterAny()
数组\filterAll()
数组\mapWithKey()
对象\isInstanceOf()
对象\implementsInterface()
对象\toArray()
对象\usesTrait()
对象\createWith()
- 重大变更
通用函数\pipe()
&通用函数\pipeR()
已更改,不再是对compose()
的别名通用函数\setProperty()
现在接受属性参数,在创建闭包时使用。字符串\tagWrap()
已移除字符串\asUrl()
已移除字符串\vSprintf()
的参数顺序已反转。字符串\split()
现在是 explode() 的包装器,现有的字符串\split()
已重命名为字符串\splitByLength()
- 其他变更
- 使用
函数
类名添加的常量,函数::isBlank
可以用作可调用字符串。 通用函数\toArray()
已移动到对象\toArray()
,对象\toArray()
现在是对通用函数\toArray()
的别名
-
0.1.2 - 添加了
数组\zip()
-
0.1.3 - 添加了
数组\filterKey()