Last active
April 19, 2017 18:15
-
-
Save jeremeamia/bafd3557a35c8949f64d57f520169a68 to your computer and use it in GitHub Desktop.
Functional PHP tomfoolery
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace fn { | |
use Closure as Fn; | |
use InvalidArgumentException as IAE; | |
const SKIP = "\0...\0"; | |
/** | |
* @param callable $fn | |
* @param mixed $scope | |
* @return Fn | |
*/ | |
function scope(callable $fn, $scope) { | |
list($scope, $object) = determine_subject($scope); | |
return from_callable($fn)->bindTo($object, $scope); | |
} | |
/** | |
* @param string $function | |
* @return Fn | |
*/ | |
function from_function($function) { | |
if (!is_string($function)) { | |
throw new IAE('Expected the name of a function or static method to be provided.'); | |
} | |
if (strpos($function, '::') > 0) { | |
return from_method(...explode('::', $function, 2)); | |
} | |
if (!is_callable($function)) { | |
throw new IAE('Expected the function to exist and be callable.'); | |
} | |
return from_callable($function); | |
} | |
/** | |
* @param mixed $class | |
* @param string|null $method | |
* @return Fn | |
*/ | |
function from_method($class, $method = null) { | |
// Supports: | |
// - `$class = ['class', 'method'], $method = null | |
// - `$class = [$object, 'method'], $method = null | |
// - `$class = 'class', $method = 'method' | |
// - `$class = $object, $method = 'method' | |
if (!$method) { | |
if (is_array($class) && count($class) === 2) { | |
list($class, $method) = $class; | |
} else { | |
throw new IAE('Expected method name to be provided.'); | |
} | |
} | |
list($class, $object) = determine_subject($class); | |
if (!method_exists($class, $method)) { | |
throw new IAE('Expected the method to exist.'); | |
} | |
if ($object) { | |
return scope(function (...$args) use ($object, $method) { | |
return $object->{$method}(...$args); | |
}, $object); | |
} else { | |
return scope(function (...$args) use ($class, $method) { | |
return $class::{$method}(...$args); | |
}, $class); | |
} | |
} | |
/** | |
* @param callable $fn | |
* @return Fn | |
*/ | |
function from_callable(callable $fn) { | |
return $fn instanceof Fn ? $fn : function (...$args) use ($fn) { | |
return $fn(...$args); | |
}; | |
} | |
function determine_subject($class) { | |
$object = null; | |
if (is_object($class)) { | |
$object = $class; | |
$class = get_class($object); | |
} elseif (!is_string($class)) { | |
throw new IAE('Expected class name or object instance to be provided.'); | |
} | |
return [$class, $object]; | |
} | |
/** | |
* Creates a partial application of a function. | |
* | |
* @param callable $fn | |
* @param mixed[] $args | |
* @return Fn | |
*/ | |
function apply(callable $fn, ...$args) { | |
return function (...$partialArgs) use ($fn, $args) { | |
foreach ($args as &$arg) { | |
if ($arg === SKIP) { | |
$arg = array_shift($partialArgs); | |
} | |
} | |
return $fn(...array_merge($args, $partialArgs)); | |
}; | |
} | |
} | |
namespace { | |
class Five { | |
private static $five = 5; | |
private static function getFive() { | |
return self::$five; | |
} | |
} | |
$trimColons = fn\apply('trim', fn\SKIP, ':'); | |
var_dump($trimColons(':foo:bar:')); | |
$getFive = fn\from_method(Five::class, 'getFive'); | |
var_dump($getFive()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment