定制 mlg/shovel 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

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

mlg/shovel

Composer 安装命令:

composer require mlg/shovel

包简介

A PHP library for manipulating Arrays, Strings and Objects - inspired by ramda.js

README 文档

README

A PHP library for manipulating Arrays, Strings and Objects - inspired by ramda.js

Requirements

PHP 7.2

Install

composer require mlg/shovel

Example

<?php

use Shovel\A;
use Shovel\S;

$fruits = ['banana', 'raspberry', 'orange', 'strawberry', 'apple', 'blueberry'];

$berries = A::filter(function($fruit) {
  return S::endsWith('berry', $fruit);
}, $fruits);

print_r($berries); // ['raspberry', 'strawberry', 'blueberry']

API

Array

A::of

Concatenates every argument into an array as is

See also: A::concat()

A::of(1, 2, [3]); // [1, 2, [3]]

A::isArray

checks whether the given parameter is an array (returns true for both numeric and associative)

A::isArray([1, 2, 3]); // true
A::isArray(["a" => 10]); // true
A::isArray("asdf"); // false
A::isArray(50); // false
A::isArray(new stdClass()); // false

A::isAssoc

checks whether the given parameter is an associative array. empty arrays are treated as normal arrays and the function will return false for them

The method is based on this solution: https://stackoverflow.com/a/173479/1806628

A::isAssoc([]); // false
A::isAssoc([1, 2, 3]); // false;
A::isAssoc(["x" => 10, "y" => 20]); // true

A::reduce

Calls $fn with each element from left to right in the array and passes the returned value to the subsequent $fn calls

See also: A::reduceRight()

A::reduce($fn, $init, [1, 2, 3]); // same as $fn(3, $fn(2, $fn(1, $init)))

A::reverse

Flips the order of values around in an array

A::reverse([1, 2, 3]); // [3, 2, 1]

A::reduceRight

Calls $fn with each element from right to left in the array and passes the returned value to the subsequent $fn calls

See also: A::reduce()

A::reduceRight($fn, $init, [1, 2, 3]); // same as $fn(1, $fn(2, $fn(3, $init)))

A::sum

adds up the numbers in the given array and returns the sum

A::sum([1, 2, 3, 4, 5]); // 15

A::map

Calls $fn with all the elements and return the results in an array

$numbers = [1, 2, 3, 4, 5];

function double(int $n){
  return $n * 2;
}

$doubles = A::map(double, $numbers); // [2, 4, 6, 8, 10]

A::keys

returns the indices of an array

See also: O::keys()

A::keys([3, 6, 9]); // [0, 1, 2]

A::values

returns the values of an array

See also: O::values()

A::values([3, 6, 9]); // [3, 6, 9]

A::equals

Compares two numeric arrays and returns true when their content is the same

A::equals([1, 2, 3], [1, 2, 3]); // true
A::equals([1, 2, 3], [1, 2, 3, 4, 5]); // false

A::length

Returns the size of an array

A::length([1, 2, 3]); // 3

A::isEmpty

Returns true when the given array has no elements inside

See also: A::isNotEmpty()

A::isEmpty([]); // true
A::isEmpty([1, 2, 3]); // false

A::isNotEmpty

Returns true when the given array has elements inside

See also: A::isEmpty()

A::isNotEmpty([1, 2, 3]); // true
A::isNotEmpty([]); // false

A::ensureArray

Wraps the given value into an array (even associative arrays) unless it already is

A::ensureArray([10]) // [10]
A::ensureArray(['a' => 10]) // [['a' => 10]]
A::ensureArray(123); // [123]
A::ensureArray([4, 5, 6]); // [4, 5, 6]

A::append

A::prepend

A::pluck

A::uniq

A::uniqByKey

A::sortBy

A::sortByKey

A::unnest

A::forEach

A::head

returns the first element of an array, or null, if empty

A::head([1, 2, 3]) // 1
A::head([]) // null

A::first

alias for A::head()

See also: A::head()

A::last

returns the last element of an array, or null, if empty

A::last([1, 2, 3, 4, 5]) // 5
A::last([]) // null

A::init

returns a copy of a given array without the last element

A::init([1, 2, 3, 4, 5]) // [1, 2, 3, 4]

A::tail

returns a copy of a given array without the first element

A::tail([1, 2, 3, 4, 5]) // [2, 3, 4, 5]

A::filter

calls the given function on the elements of an array and returns every value where the function gave truthy value

$numbers = [1, 2, 3, 4, 5, 6];

function isOdd($n){
  return $n % 2 === 0;
}

A::filter('isOdd', $numbers); // [2, 4, 6]

A::reject

calls the given function on the elements of an array and removes every value where the function gave truthy value

$numbers = [1, 2, 3, 4, 5, 6];

function isOdd($n){
  return $n % 2 === 0;
}

A::reject('isOdd', $numbers); // [1, 3, 5]

A::find

calls the given function on the elements of an array and returns the value for the first match. if there's no match, it will return null

$data = [
  ["a" => 8],
  ["a" => 10],
  ["a" => 12]
];

$result = A::find(fn($x) => $x["a"] > 3, $data);

// $result = ["a" => 8]
$data = [
  ["a" => 8],
  ["a" => 10],
  ["a" => 12]
];

$result = A::find(fn($x) => $x["a"] === -4, $data);

// $result = null

A::findLast

calls the given function on the elements of an array and returns the value for the last match. if there's no match, it will return null

$data = [
  ["a" => 8],
  ["a" => 10],
  ["a" => 12]
];

$result = A::findLast(fn($x) => $x["a"] > 3, $data);

// $result = ["a" => 12]
$data = [
  ["a" => 8],
  ["a" => 10],
  ["a" => 12]
];

$result = A::findLast(fn($x) => $x["a"] === -4, $data);

// $result = null

A::findIndex

calls the given function on the elements of an array and returns the key for the first match. if there's no match it will return null

$data = [1, 1, 1, 0, 0, 0, 0, 0];

$result = A::findIndex(fn($x) => $x === 0, $data);

// $result = 3
$data = [1, 1, 1, 0, 0, 0, 0, 0];

$result = A::findIndex(fn($x) => $x === 2, $data);

// $result = null

A::findLastIndex

calls the given function on the elements of an array and returns the key for the last match. if there's no match it will return null

$data = [1, 1, 1, 0, 0, 0, 0, 0];

$result = A::findLastIndex(fn($x) => $x === 1, $data);

// $result = 2
$data = [1, 1, 1, 0, 0, 0, 0, 0];

$result = A::findLastIndex(fn($x) => $x === 2, $data);

// $result = null

A::any

calls the given predicate function on the elements in the given array and returns true if for at least one of them the predicate returns true

$data = [2, 3, 5, 6, 7, 9, 10];

$result = A::any(fn($x) => $x % 5 === 0, $data);

// $result = true

A::none

A::all

A::includes

A::contains

A::slice

Returns the elements of the given list from fromIndex (inclusive) to toIndex (exclusive)

A::join

A::pickRandom

selects a random item from the given array

A::concat

concatenates every argument into an array. if any of the arguments are numeric arrays, then those will get unnested

See also: A::of()

A::concat([1, 2], 3, [4, 5]); // [1, 2, 3, 4, 5]

A::zipObj

A::without

removes items from the second array by values in the first array. if first value is not an array, then it is transformed into one

A::without([1, 3], [1, 2, 3, 4, 5]); // [2, 4, 5]
A::without(['a' => 12], [1, 2, 3, 4, ['a' => 12]]); // [1, 2, 3, 4]
A::without('t', ['t', 'f', 'f', 't', 'f']); // ['f', 'f', 'f']

String

Most string operations come with an optional 3rd parameter called $caseSensitivity, which can be either S::CASE_SENSITIVE (default) or S::CASE_INSENSITIVE.

All string operations are multibyte safe!

S::isString

checks whether given argument is a string

S::isString('hello'); // true
S::isString(['hello']); // false
S::isString(304.2); // false

S::length

counts the number of characters in the given parameter

S::length('őz'); // 2 -- strlen('őz') returns 3

S::isEmpty

checks whether the given string has no characters

S::isEmpty(''); // true
S::isEmpty('caterpillar'); // false

S::isNotEmpty

checks whether the given string contains any characters

S::isNotEmpty(''); // false
S::isNotEmpty('caterpillar'); // true

S::toLower

converts every character in a string to lowercase

S::toLower('AsDf JkLÉ'); // "asdf jklé"

S::toUpper

converts every character in a string to uppercase

S::toUpper('AsDf JkLÉ'); // "ASDF JKLÉ"

S::includes

checks, if the string given as the 1st parameter is a substring of the 2nd parameter string

S::includes('erf', 'butterfly'); // true
S::includes('ERF', 'butterfly', S::CASE_INSENSITIVE); // true
S::includes('ERF', 'butterfly', S::CASE_SENSITIVE); // false
S::includes('', 'butterfly'); // false -- edge case

S::contains

alias for S::includes()

See also: S::includes

S::split

splits a string into multiple parts at points matching another string

S::split("/", "foo/bar/baz") // ["foo", "bar", "baz"]

S::splitAt

splits a string into 2 at a given position

S::splitAt(3, "abcdef") // ["abc", "def"]

S::equals

compares two strings together to see if they match

S::equals('asdf', 'asdf'); // true
S::equals('asdf', 'ASDF', S::CASE_INSENSITIVE); // true
S::equals('asdf', 'ASDF', S::CASE_SENSITIVE); // false

S::slice

copies a substring between starting(inclusive) and ending(exclusive) positions

S::slice(2, 5, "abcdefgh"); // "cde"
S::slice(-3, PHP_INT_MAX, "abcdefgh") // "fgh"

S::startsWith

checks if the second parameter starts with the first

S::startsWith("inf", "infinity"); // true
S::startsWith("inf", "iNFinItY", S::CASE_INSENSITIVE); // true
S::startsWith("inf", "iNFinItY", S::CASE_SENSITIVE); // false

S::endsWith

checks if the second parameter ends with the first

S::endsWith("ed", "throwed"); // true
S::endsWith("ed", "tHRoWeD", S::CASE_SENSITIVE); // false
S::endsWith("ed", "tHRoWeD", S::CASE_INSENSITIVE); // true

S::trim

removes leading and trailing whitespaces from a string

S::trim("  asd f     "); // "asd f"

S::replace

replaces substring with another

S::replace("a", "e", "alabama"); // "elebeme"

Object

O::isObject

check whether the passed in argument is an object

$point = new stdClass();
$point->x = 10;
$point->y = 20;
O::isObject($point); // true
O::isObject("asdf"); // false

O::toPairs

gets all keys and values of an array or object and returns it as array of key-value pairs

$point = new stdClass();
$point->x = 10;
$point->y = 20;
O::toPairs($point); // [["x", 10], ["y", 20]]
$user = [
  "firstName" => "John",
  "lastName" => "Doe"
];
O::toPairs($user); // [["firstName", "John"], ["lastName", "Doe"]]
$temperatures = [75, 44, 36];
O::toPairs($temperatures); // [[0, 75], [1, 44], [2, 36]]

O::pick

O::assoc

assigns value to an object via a given key. already existing keys will get overwritten

$point2d = new stdClass();
$point2d->x = 10;
$point2d->y = 20;

$point3d = O::assoc("z", 30, $point2d); // {"x": 10, "y": 20, "z": 30}
$point2d = [
  "x" => 10,
  "y" => 20
];

$point3d = O::assoc("z", 30, $point2d); // ["x" => 10, "y" => 20, "z" => 30]

Does not work on arrays with numeric keys!

O::dissoc

removes a key from an object

$point3d = new stdClass();
$point3d->x = 10;
$point3d->y = 20;
$point3d->z = 30;

$point2d = O::dissoc("z", 30, $point3d); // {"x": 10, "y": 20}
$point3d = [
  "x" => 10,
  "y" => 20,
  "z" => 30
];

$point2d = O::dissoc("z", 30, $point3d); // ["x" => 10, "y" => 30]

Does not work on arrays with numeric keys!

O::has

checks presence of a key inside an object and an associative array

uses array_key_exists() internally

$data = new stdClass();
$data->x = 10;

O::has('x', $data); // true
O::has('y', $data); // false
$data = ['x' => 10];

O::has('x', $data); // true
O::has('y', $data); // false

O::keys

O::values

O::prop

Reads the given value for the given key from objects and associative arrays. If not found, then returns null.

$data = new stdClass();
$data->x = 10;

O::prop('x', $data); // 10
O::prop('y', $data); // null
$data = ['x' => 10];

O::prop('x', $data); // 10
O::prop('y', $data); // null

Functions

F::complement

Wraps the passed in function in a way that when called it inverts it's returning value

function isOdd(int $n):boolean {
  return $n % 2 === 1;
}

$isEven = F::complement(isOdd);

Concepts

Every method is abide to the following rules ( or at least they should. if they don't, then 1) PRs are welcome, 2) Issues are welcome ):

stateless

each method should get all the necessary info from the parameters and should not rely on any external parameters or state

static

since every method is stateless, there is no need to create class instances

pure

not using anything apart from the passed in parameters

immutable

not going to change any of the parameters, no & references or stuff like that

the last parameter should be the input data you are working on...

like in Lodash FP or Ramda

except if the argument list has optional parameters!

suggestions are welcome on where to place optional parameters

not doing any validation on the parameters

if you are using a method from A, then you better be sending it an array. PHP is a loosely typed language and you could spend all day validating input parameters.

not casting any of the input parameters

it's the same as for the validation, you should check the data you pass to the function beforehand

does only a single, well defined thing

small is beautiful, and maintainable - and probably easier to test later on when I'll get the willpower to write tests for this lib

null return values on error

when an error happens and the underlying php function returns false (eg. end or strpos), then it's being normalized to null

camelCase naming

Plain numeric arrays are handled best via the methods in A, while associative arrays and objects are handled via the methods in O.

Future plans

I keep adding methods as I come across the need for them, so if you're missing a method you'd use, then 1) PRs are welcome, 2) Issues are welcome.

If the methods are all static and stateless, then why not just write simple functions?

There are multiple functions, which have the same parameter signature, but operate with different parameter types. To avoid having to type check the parameters at every function call I've chose to namespace the functions based on the types they work on into static methods.

For example take a look at includes for both Arrays and Strings. Their implementation is relative simple, because their types are expected to be arrays and strings respectively (type hinting will come soon).

class A {
  public static function includes($value, $data)
  {
    return in_array($value, $data, true);
  }
}

class S {
  public static function includes($value, $data)
  {
    return mb_strpos($data, $str) !== false;
  }
}

If I were to have a single, combined includes function, then I would have to do type checking every time and it would make the code unnecessarily noisy.

Plus, sometimes the function name I would like to use is already taken by PHP, like in the case of S::split

Credits

https://www.geeksforgeeks.org/php-startswith-and-endswith-functions/ - used for S::startsWith() and S::endsWith()

https://stackoverflow.com/a/173479/1806628 - used for A::isAssoc()

https://www.php.net/manual/en/function.array-unique.php#116302 - used for A::uniqByKey()

mlg/shovel 适用场景与选型建议

mlg/shovel 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 117 次下载、GitHub Stars 达 0, 最近一次更新时间为 2021 年 04 月 06 日, 在 PHP 生态内属于活跃度较高的组件。

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

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

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

BUG 修复 & 性能优化

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

项目外包 & 长期维护

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

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

统计信息

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

GitHub 信息

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

其他信息

  • 授权协议: GPL-3.0-or-later
  • 更新时间: 2021-04-06