Skip to content

Commit

Permalink
Merge pull request #41 from ace411/v2.x
Browse files Browse the repository at this point in the history
v2.2.0 changes
  • Loading branch information
ace411 authored Mar 6, 2023
2 parents 0801c16 + 504f051 commit d16bee8
Show file tree
Hide file tree
Showing 16 changed files with 559 additions and 45 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ jobs:
strategy:
fail-fast: false
matrix:
php: ['7.2', '7.3', '7.4', '8.0']
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
name: PHP ${{ matrix.php }}
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: Install PHP
uses: shivammathur/setup-php@master
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
- name: Validate composer.json and composer.lock
run: composer validate
- name: Get Composer cache directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache dependencies
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }}
Expand Down
19 changes: 12 additions & 7 deletions .php-cs-fixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@
$config = new Config;

return $config
->setRules([
'@PSR12' => true,
'linebreak_after_opening_tag' => true,
'binary_operator_spaces' => [
'operators' => ['=>' => 'align', '=' => 'align'],
],
])
->setRules(
[
'@PSR12' => true,
'linebreak_after_opening_tag' => true,
'binary_operator_spaces' => [
'operators' => [
'=>' => 'align',
'=' => 'align'
]
]
]
)
->setFinder($finder)
->setIndent(' ');
13 changes: 13 additions & 0 deletions changes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# bingo-functional changes

## v2.2.0

- Added `equals` and `path` helper functions
- Added default parameters to `head` and `last` functions
- Added `_refobj` internal function for object comparisons
- Improved `memoize` function amenability to `igbinary` and `apcu` extensions

## v2.1.0

- Added `page` function
- Fixed erroneous namespace in `last` function
- Added `kleisli` composition function

## v2.0.1

- Modified `cmatch` function internals to better handle wildcard patterns
Expand Down
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"functional-php/pattern-matching": "~1"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "~2",
"friendsofphp/php-cs-fixer": "~3",
"giorgiosironi/eris": "~0",
"phpunit/phpunit": "~7 || ~8 || 9.3.*"
},
Expand Down Expand Up @@ -104,6 +104,8 @@
"src/Functional/Trampoline.php",
"src/Functional/IndexOf.php",
"src/Functional/Unzip.php",
"src/Functional/Equals.php",
"src/Functional/Paths.php",
"src/Functional/PartialLeft.php",
"src/Functional/Intersperse.php",
"src/Functional/Extend.php",
Expand Down
63 changes: 63 additions & 0 deletions src/Functional/Equals.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

/**
* equals function
*
* @package bingo-functional
* @author Lochemem Bruno Michael
* @license Apache-2.0
*/

namespace Chemem\Bingo\Functional;

use function Chemem\Bingo\Functional\Internal\_refobj;

const equals = __NAMESPACE__ . '\\equals';

/**
* equals
* perform artifact comparisons to ascertain equivalence
*
* equals :: a -> b -> Bool -> Bool
*
* @param mixed $fst
* @param mixed $snd
* @param boolean $recurse
* @return boolean
* @example
*
* equals((object)range(1, 5), (object)range(2, 4));
* => false
*/
function equals($fst, $snd, bool $recurse = false): bool
{
$ft = \gettype($fst);
$st = \gettype($snd);
// store core types in list to preempt redeclaration
$core = [
'integer',
'string',
'boolean',
'array',
'double',
'NULL',
];

if ($ft !== $st) {
return false;
}

if (\in_array($ft, $core) && \in_array($st, $core)) {
return $fst === $snd;
}

if ($ft === 'resource' && $st === 'resource') {
return \get_resource_type($fst) === \get_resource_type($snd);
}

if ($ft === 'object' && $st === 'object') {
return _refobj($fst, $recurse) === _refobj($snd, $recurse);
}

return false;
}
2 changes: 1 addition & 1 deletion src/Functional/Head.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@
*/
function head($list, $def = null)
{
return \reset($list);
return \reset($list) ?? $def;
}
129 changes: 129 additions & 0 deletions src/Functional/Internal/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,132 @@ function _curry(
}

const _curry = __NAMESPACE__ . '\\_curry';

/**
* _refobj
* condense Reflection object into an array structure
*
* _refobj :: Object -> Bool -> Array
*
* @internal
* @param object $obj
* @param boolean $recurse
* @return array
*/
function _refobj($obj, bool $recurse = false): array
{
$data = [];

if ($obj instanceof \Closure || \is_callable($obj)) {
$ref = new \ReflectionFunction($obj);
$data['name'] = $ref->getName();
$data['params'] = _fold(
function (array $acc, \ReflectionParameter $param) {
if (!\is_null($param) && !\is_null($param->getType())) {
$acc[$param->getName()] = $param->getType()->getName();
}

return $acc;
},
$ref->getParameters(),
[]
);

if (!\is_null($ref->getReturnType())) {
$data['return'] = $ref->getReturnType()->getName();
}

$data['file'] = $ref->getFileName();
$data['static'] = $ref->getStaticVariables();
$data['ext'] = $ref->getExtensionName();
$data['scope'] = \is_null($ref->getClosureScopeClass()) ?
null :
$ref
->getClosureScopeClass()
->getName();

if ($obj instanceof \Closure) {
$data['start'] = $ref->getStartLine();
$data['end'] = $ref->getEndLine();
}
} else {
$ref = new \ReflectionClass($obj);
$data['name'] = $ref->getName();
$data['ext'] = $ref->getExtensionName();
$data['file'] = $ref->getFileName();

$props = PHP_VERSION_ID < 80100 ?
$ref->getProperties(
\ReflectionProperty::IS_STATIC |
\ReflectionProperty::IS_PUBLIC |
\ReflectionProperty::IS_PRIVATE |
\ReflectionProperty::IS_PROTECTED
) :
$ref->getProperties(
\ReflectionProperty::IS_STATIC |
\ReflectionProperty::IS_PUBLIC |
\ReflectionProperty::IS_PRIVATE |
\ReflectionProperty::IS_READONLY |
\ReflectionProperty::IS_PROTECTED
);

$data['props'] = _fold(
function (array $acc, $prop, $key) use ($obj, $recurse) {
if ($prop instanceof \ReflectionProperty) {
$name = $prop->getName();

if (PHP_VERSION_ID < 80100) {
$prop->setAccessible(true);
}

$value = $prop->getValue($obj);
if (!$recurse) {
$acc[$name] = $value;
}

if (\is_object($value) && $recurse) {
$acc[$name][] = _refobj($value);
}
} else {
if (\is_object($prop) && !($prop instanceof \ReflectionProperty) && $recurse) {
$acc = \array_merge($acc, _refobj($prop));
} else {
$acc[$key] = $prop;
}
}

return $acc;
},
!empty($props) ? $props : \get_object_vars($obj),
[]
);
$data['constants'] = $ref->getConstants();
$data['interfaces'] = $ref->getInterfaceNames();
$data['traits'] = $ref->getTraitNames();
$data['methods'] = _fold(
function (array $acc, \ReflectionMethod $method) {
$acc[] = $method->getName();

return $acc;
},
$ref->getMethods(),
[]
);

if (PHP_VERSION_ID >= 80000) {
$data['attributes'] = _fold(
function ($acc, $attr) {
$acc[] = $attr->getName();

return $acc;
},
$ref->getAttributes(),
[]
);
}
}

return $data;
}

const _refobj = __NAMESPACE__ . '\\_refobj';
6 changes: 4 additions & 2 deletions src/Functional/Last.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
* last(range(4, 7))
* => 7
*/
function last($list)
function last($list, $def = null)
{
return \end($list);
$last = \end($list);

return $last ? $last : $def;
}
6 changes: 1 addition & 5 deletions src/Functional/LastIndexOf.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,5 @@
*/
function lastIndexOf($list, $value, $def = null)
{
$last = last(indexesOf($list, $value));

return \is_bool($last) ?
($last !== false ? $last : $def) :
$last;
return last(indexesOf($list, $value), $def);
}
33 changes: 18 additions & 15 deletions src/Functional/Memoize.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,32 @@
* @return callable
* @example
*
* $fact = memoize(function ($x) use (&$fact) {
* $fact = function ($x) use (&$fact) {
* return $x < 2 ? 1 : $x * $fact($x - 1);
* })(5);
* };
* memoize($fact)(5);
* => 120
*/
function memoize(callable $function): callable
function memoize(callable $function, bool $apc = false): callable
{
return function (...$args) use ($function) {
return function (...$args) use ($apc, $function) {
static $cache = [];
$key = \md5(\serialize($args));
// store result in variable to preempt re-computation
$result = $function(...$args);
$key = \md5(
\extension_loaded('igbinary') ?
\igbinary_serialize($args) :
\serialize($args)
);

// store the result in the apcu cache if the extension is present
if (\extension_loaded('apcu')) {
\apcu_add($key, $result);
if ($apc && \extension_loaded('apcu')) {
\apcu_add($key, $function(...$args));

return \apcu_exists($key) ? \apcu_fetch($key) : $function(...$args);
}

$cache[$key] = $result;
if (!isset($cache[$key])) {
$cache[$key] = $function(...$args);
}

return !\extension_loaded('apcu') ?
// return cached result if it exists; pre-computed result otherwise
(isset($cache[$key]) ? $cache[$key] : $result) :
(\apcu_exists($key) ? \apcu_fetch($key) : $result);
return isset($cache[$key]) ? $cache[$key] : $function(...$args);
};
}
Loading

0 comments on commit d16bee8

Please sign in to comment.