Skip to content

Instantly share code, notes, and snippets.

@jeremeamia
Last active April 19, 2017 18:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeremeamia/bafd3557a35c8949f64d57f520169a68 to your computer and use it in GitHub Desktop.
Save jeremeamia/bafd3557a35c8949f64d57f520169a68 to your computer and use it in GitHub Desktop.
Functional PHP tomfoolery
<?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