/** * @link http://php.net/manual/en/serializable.serialize.php */ public function serialize() { // prepare code $file = new SplFileObject($this->reflection->getFileName()); $file->seek($this->reflection->getStartLine() - 1); $code = ''; while ($file->key() < $this->reflection->getEndLine()) { $code .= $file->current(); $file->next(); } $start = strpos($code, 'function'); $code = substr($code, $start, strpos($code, '}') - $start + 1); // prepare variables $variables = []; $index = stripos($code, 'use'); // if 'use' keyword found if (false !== $index) { // get the names of the variables inside the use statement $start = strpos($code, '(', $index) + 1; $end = strpos($code, ')', $start); $use_variables = explode(',', substr($code, $start, $end - $start)); $static_variables = $this->reflection->getStaticVariables(); // keep only the variables that appeared in both scopes foreach ($use_variables as $variable) { $variable = trim($variable, '$&'); $variables[$variable] = $static_variables[$variable]; } } return serialize(['code' => $code, 'variables' => $variables]); }
public function view() { $services = array(); $bindings = Core::getBindings(); foreach ($bindings as $name => $binding) { /** @var \Closure $binding */ $reflection = new \ReflectionFunction($binding['concrete']); $static = $reflection->getStaticVariables(); $className = null; if (!isset($static['concrete'])) { try { $class = Core::make($name); $className = get_class($class); } catch (\Exception $e) { } } else { $className = $static['concrete']; } if ($className !== null && $className !== get_class($this)) { if ($className[0] !== '\\') { $className = '\\' . $className; } $services[$name] = array('class' => $className); } } ksort($services); $this->set('services', $services); }
public function parse(&$variable) { if (!$variable instanceof Closure) { return false; } $this->name = 'Closure'; $reflection = new ReflectionFunction($variable); $ret = array('Parameters' => array()); if ($val = $reflection->getParameters()) { foreach ($val as $parameter) { // todo http://php.net/manual/en/class.reflectionparameter.php $ret['Parameters'][] = $parameter->name; } } if ($val = $reflection->getStaticVariables()) { $ret['Uses'] = $val; } if (method_exists($reflection, 'getClousureThis') && ($val = $reflection->getClosureThis())) { $ret['Uses']['$this'] = $val; } if ($val = $reflection->getFileName()) { $this->value = Kint::shortenPath($val) . ':' . $reflection->getStartLine(); } return $ret; }
/** * Returns an associative array containing this function's static variables * and their values * * @return array<sting,mixed> This function's static variables */ public function getStaticVariables() { if ($this->reflectionSource instanceof ReflectionFunction) { return $this->reflectionSource->getStaticVariables(); } else { return parent::getStaticVariables(); } }
function dumpFuncInfo($name) { $funcInfo = new ReflectionFunction($name); var_dump($funcInfo->getName()); var_dump($funcInfo->isInternal()); var_dump($funcInfo->isUserDefined()); var_dump($funcInfo->getStartLine()); var_dump($funcInfo->getEndLine()); var_dump($funcInfo->getStaticVariables()); }
public function render() { $file = '<?php namespace PHPSTORM_META { $STATIC_METHOD_TYPES = array(\\Core::make(\'\') => array(' . PHP_EOL; $legacyHelpers = array(); $bindings = Core::getBindings(); foreach ($bindings as $name => $binding) { /** @var \Closure $binding */ $reflection = new \ReflectionFunction($binding['concrete']); $static = $reflection->getStaticVariables(); $className = null; if (!isset($static['concrete'])) { try { $class = Core::make($name); if (is_object($class)) { $className = get_class($class); } } catch (\Exception $e) { } } else { $className = $static['concrete']; } if ($className !== null && $className !== get_class($this)) { if ($className[0] !== '\\') { $className = '\\' . $className; } $file .= '\'' . $name . '\' instanceof ' . $className . ',' . PHP_EOL; if (substr($name, 0, 7) === 'helper/') { $legacyHelpers[substr($name, 7)] = $className; } } } $app = Core::make('app'); $reflection = new \ReflectionClass($app); $instances = $reflection->getProperty("instances"); $instances->setAccessible(true); // :) foreach ($instances->getValue($app) as $name => $instance) { if (!isset($bindings[$name])) { $className = get_class($instance); $file .= '\'' . $name . '\' instanceof ' . $className . ',' . PHP_EOL; } } $file .= '), \\Loader::helper(\'\') => array('; foreach ($legacyHelpers as $legacyHelper => $className) { $file .= '\'' . $legacyHelper . '\' instanceof ' . $className . ',' . PHP_EOL; } $file .= '), \\Package::getByHandle(\'\') => array('; $packages = \Package::getAvailablePackages(false); foreach ($packages as $package) { /** @var \Package $package */ $file .= '\'' . $package->getPackageHandle() . '\' instanceof \\' . get_class($package) . ',' . PHP_EOL; } $file .= '));}'; return $file; }
public function makeId(\Closure $callback) { $ref = new \ReflectionFunction($callback); $file = new \SplFileObject($ref->getFileName()); $file->seek($ref->getStartLine() - 1); $content = ''; while ($file->key() < $ref->getEndLine()) { $content .= $file->current(); $file->next(); } return sha1(json_encode(array($content, $ref->getStaticVariables(), $ref->getFileName(), $ref->getStartLine() - 1, $ref->getEndLine()))); }
/** * Get the variables used by the Closure. * * @return array */ public function getVariables() { if (!$this->getUseIndex()) { return array(); } $staticVariables = $this->reflection->getStaticVariables(); // When looping through the variables, we will only take the variables that are // specified in the use clause, and will not take any other static variables // that may be used by the Closures, allowing this to re-create its state. $usedVariables = array(); foreach ($this->getUseClauseVariables() as $variable) { $variable = trim($variable, ' $&'); $usedVariables[$variable] = $staticVariables[$variable]; } return $usedVariables; }
public static function unwrap(\Closure $closure) { $reflectionFunction = new \ReflectionFunction($closure); if (substr($reflectionFunction->getName(), -1) === '}') { $vars = $reflectionFunction->getStaticVariables(); return isset($vars['_callable_']) ? $vars['_callable_'] : $closure; } else { if ($obj = $reflectionFunction->getClosureThis()) { return [$obj, $reflectionFunction->getName()]; } else { if ($class = $reflectionFunction->getClosureScopeClass()) { return [$class->getName(), $reflectionFunction->getName()]; } } } return $reflectionFunction->getName(); }
private function verifyCallbackJob($callback, LinkTarget $expectedTitle, $expectedUserId, callable $notificationTimestampCondition) { $this->assertInternalType('callable', $callback); $callbackReflector = new ReflectionFunction($callback); $vars = $callbackReflector->getStaticVariables(); $this->assertArrayHasKey('job', $vars); $this->assertInstanceOf(ActivityUpdateJob::class, $vars['job']); /** @var ActivityUpdateJob $job */ $job = $vars['job']; $this->assertEquals($expectedTitle->getDBkey(), $job->getTitle()->getDBkey()); $this->assertEquals($expectedTitle->getNamespace(), $job->getTitle()->getNamespace()); $jobParams = $job->getParams(); $this->assertArrayHasKey('type', $jobParams); $this->assertEquals('updateWatchlistNotification', $jobParams['type']); $this->assertArrayHasKey('userid', $jobParams); $this->assertEquals($expectedUserId, $jobParams['userid']); $this->assertArrayHasKey('notifTime', $jobParams); $this->assertTrue($notificationTimestampCondition($jobParams['notifTime'])); }
/** * Format static (used) variable names. * * @param \Closure $value * * @return string */ protected function formatStaticVariables(\Closure $value) { $r = new \ReflectionFunction($value); $used = $r->getStaticVariables(); if (empty($used)) { return ''; } $names = array_map(array($this, 'formatParamName'), array_keys($used)); return sprintf(self::USE_FMT, implode(', ', $names)); }
<?php function dummy() { static $a = array(); } class Test { const A = 0; public function func() { static $a = array(self::A => 'a'); } } $reflect = new ReflectionFunction("dummy"); print_r($reflect->getStaticVariables()); $reflect = new ReflectionMethod('Test', 'func'); print_r($reflect->getStaticVariables());
/** * Dump an object * * @param mixed $var Variable to dump * @param KObjectConfig $config The configuration options * @param integer $level Current recursion level (internal usage only) * @return string */ protected function _dumpObject($var, KObjectConfig $config, $level = 0) { $fields = $this->_getObjectVars($var); if ($var instanceof Closure) { $rc = new ReflectionFunction($var); $fields = array(); foreach ($rc->getParameters() as $param) { $fields[] = '$' . $param->getName(); } $fields = array('file' => $rc->getFileName(), 'line' => $rc->getStartLine(), 'variables' => $rc->getStaticVariables(), 'parameters' => implode(', ', $fields)); } if ($var instanceof SplFileInfo) { $fields = array('path' => $var->getPathname()); } if ($var instanceof SplObjectStorage) { $fields = array(); foreach (clone $var as $obj) { $fields[] = array('object' => $obj, 'data' => $var[$obj]); } } if ($var instanceof KObjectInterface) { unset($fields['__methods']); } if ($var instanceof KObjectManager) { $fields = array(); } if ($var instanceof KObjectConfigInterface) { $fields = $var->toArray(); } $result = ''; $result .= '<span class="koowa-dump-object">' . get_class($var) . '</span>'; if ($var instanceof KObjectInterface) { $result .= '<span class="koowa-dump-identifier">(' . $var->getIdentifier() . ')</span>'; } $result .= '<span class="koowa-dump-hash">#' . substr(md5(spl_object_hash($var)), 0, 4) . '</span>'; static $list = array(); if (!empty($fields)) { if (!in_array($var, $list, true)) { if ($level < $config->object_depth || $var instanceof Closure) { $collapsed = $level ? count($var) >= 7 : false; $result = '<span class="koowa-toggle' . ($collapsed ? ' koowa-collapsed' : '') . '">' . $result . '</span>'; $result .= '<div' . ($collapsed ? ' class="koowa-collapsed"' : '') . '>'; $list[] = $var; foreach ($fields as $key => $value) { $vis = ''; if ($key[0] === "") { $vis = ' <span class="koowa-dump-visibility">' . ($key[1] === '*' ? 'protected' : 'private') . '</span>'; $key = substr($key, strrpos($key, "") + 1); } $result .= '<span class="koowa-dump-indent"> ' . str_repeat('| ', $level) . '</span>'; $result .= '<span class="koowa-dump-key">' . (preg_match('#^\\w+\\z#', $key) ? $key : $this->getTemplate()->escape($key)) . '</span> ' . $vis . ' => '; $result .= $this->_dumpVar($value, $config, $level + 1); } array_pop($list); $result .= '</div>'; } else { $result .= ' { ... }' . "\n"; } } else { $result .= ' { <i>RECURSION</i> }' . "\n"; } } else { $result .= "\n"; } return $result; }
/** * cache function result and return it * @param $function closure to cache * @return object */ public function cacheFun($function) { if ($this->getConfig('debug') && !is_callable($function)) { echo "no valid function"; } $r = new ReflectionFunction($function); $id = null; foreach ($r->getStaticVariables() as $key => $var) { if ($key == 'key') { $id = $var; } } if (is_object($id)) { $id = $this->getIDfromOjb($id); } if ($id == null && $this->getConfig('debug')) { echo "no caching key"; return $function(); } $cachefile = $this->getConfig('cacheDir') . '/' . utf8_decode($id); $cachetime = $this->getConfig('cacheTime'); if (file_exists($cachefile) && time() - $cachetime < fileatime($cachefile)) { $ser = file_get_contents($cachefile); $val = bzdecompress(unserialize($ser)); return $val; } else { $value = $function(); $ser = serialize(bzcompress($value)); file_put_contents($cachefile, $ser); return $value; } if ($this->getConfig('debug')) { echo "no caching"; } }
/** * Get a memory usage breakdown * @return array */ function getMemoryBreakdown() { $memStats = array(); foreach ($GLOBALS as $name => $value) { $memStats['$' . $name] = strlen(serialize($value)); } $classes = get_declared_classes(); foreach ($classes as $class) { $rc = new ReflectionClass($class); $props = $rc->getStaticProperties(); $memStats[$class] = strlen(serialize($props)); $methods = $rc->getMethods(); foreach ($methods as $method) { $memStats[$class] += strlen(serialize($method->getStaticVariables())); } } $functions = get_defined_functions(); foreach ($functions['user'] as $function) { $rf = new ReflectionFunction($function); $memStats["{$function}()"] = strlen(serialize($rf->getStaticVariables())); } asort($memStats); return $memStats; }
/** * @return array */ private static function exportClosure(\Closure $obj) { $rc = new \ReflectionFunction($obj); $res = array(); foreach ($rc->getParameters() as $param) { $res[] = '$' . $param->getName(); } return array('file' => $rc->getFileName(), 'line' => $rc->getStartLine(), 'variables' => $rc->getStaticVariables(), 'parameters' => implode(', ', $res)); }
var_dump($rf->getStaticVariables()); foo(); echo "ReflectionFunction(2):"; var_dump($rf->getStaticVariables()); foo(); $rf = new ReflectionFunction('foo'); echo "ReflectionFunction(3):"; var_dump($rf->getStaticVariables()); echo "---- ReflectionMethod ----\n"; $rf = new ReflectionMethod('Bar', 'foo'); echo "ReflectionMethod(1):"; var_dump($rf->getStaticVariables()); Bar::foo(); echo "ReflectionMethod(2):"; var_dump($rf->getStaticVariables()); Bar::foo(); $rf = new ReflectionFunction('foo'); echo "ReflectionMethod(3):"; var_dump($rf->getStaticVariables()); echo "---- ReflectionClosure ----\n"; $rf = new ReflectionFunction($foo); echo "ReflectionFunction(1-closure):"; var_dump($rf->getStaticVariables()); $foo(); echo "ReflectionFunction(2-closure):"; var_dump($rf->getStaticVariables()); $foo(); $rf = new ReflectionFunction($foo); echo "ReflectionFunction(3-closure):"; var_dump($rf->getStaticVariables());
/** * This method gets invoked before a test method is invoked, and before * the setUp() method is called. * * @param unittest.TestCase $t * @throws unittest.PrerequisitesNotMetError */ public function beforeTest(TestCase $t) { if (!$this->verify()) { $compare = ''; foreach ($this->compare as $f) { $test = ''; $reflect = new \ReflectionFunction($f); // TODO: Closure reflection via XP foreach ($reflect->getStaticVariables() as $name => $value) { $test .= ', ' . $name . '= ' . var_export($value, true); } $compare .= ' && (' . substr($test, 2) . ')'; } throw new PrerequisitesNotMetError('Test not intended for this version (' . PHP_VERSION . ')', null, [substr($compare, 4)]); } }
/** * Extract bound variables * @param ReflectionFunction The reflected function of the closure * @param String The string of code the closure runs * @return Array The variable within the use() */ private function fetchUsedVariables(\ReflectionFunction $reflection, $code) { $use_index = stripos($code, 'use'); if (!$use_index) { return array(); } $begin = strpos($code, '(', $use_index) + 1; $end = strpos($code, ')', $begin); $vars = explode(',', substr($code, $begin, $end - $begin)); $static_vars = $reflection->getStaticVariables(); $used_vars = array(); foreach ($vars as $var) { $var = preg_replace("/\\\$|\\&/", "", trim($var)); if (is_callable($static_vars[$var]) && !$static_vars[$var] instanceof SerializableClosure) { $used_vars[$var] = new SerializableClosure($static_vars[$var]); } else { $used_vars[$var] = $static_vars[$var]; } } return $used_vars; }
<pre> <?php function sayHello($name, $h) { static $count = 0; return "<h{$h}>Hello, {$name}</h{$h}>"; } // Обзор функции Reflection::export(new ReflectionFunction('sayHello')); // Создание экземпляра класса ReflectionFunction $func = new ReflectionFunction('sayHello'); // Вывод основной информации printf("<p>===> %s функция '%s'\n" . " объявлена в %s\n" . " строки с %d по %d\n", $func->isInternal() ? 'Internal' : 'User-defined', $func->getName(), $func->getFileName(), $func->getStartLine(), $func->getEndline()); // Вывод статических переменных, если они есть if ($statics = $func->getStaticVariables()) { printf("<p>---> Статическая переменная: %s\n", var_export($statics, 1)); } // Вызов функции printf("<p>---> Результат вызова: "); $result = $func->invoke("John", "1"); echo $result; ?> </pre>
<?php $a = 4; $b = 'i am a string'; function fn($c) { return $c + 5; } $lam = function ($c) use($a, $b) { return $a + $c; }; class C { public static $i; public $j; public function meth($c) { return $c + 3; } } $refl = new ReflectionFunction('fn'); var_dump($refl->getStaticVariables()); $refl = new ReflectionFunction($lam); var_dump($refl->getStaticVariables()); $refl = new ReflectionMethod('C', 'meth'); var_dump($refl->getStaticVariables());
/** * Show reflection * * Show reflection * * <code> * Panda_Debug::reflect('BEAR_Form'); // Class * Panda_Debug::reflect($obj); // Objecy * Panda_Debug::reflect('p'); // Function * </code> * * @param string $target target * @param boll $cehckParent check parent class * * @return void */ public static function reflect($target, $cehckParent = false) { if (is_object($target)) { $target = get_class($target); } switch (true) { case function_exists($target): $ref = new ReflectionFunction($target); $info['name'] = $ref->isInternal() ? 'The internal ' : 'The user-defined '; $info['name'] .= $targetName = $ref->getName(); $info['declare in'] = $ref->getFileName() . ' lines ' . $ref->getStartLine() . ' to ' . $ref->getEndline(); $info['Documentation'] = $ref->getDocComment(); $statics = $ref->getStaticVariables(); if ($statics) { $info['Static variables'] = $statics; } $type = 'function'; break; case class_exists($target, false): $ref = new ReflectionClass($target); $type = 'class'; $info['name'] = $ref->isInternal() ? 'The internal ' : 'The user-defined '; $info['name'] .= $ref->isAbstract() ? ' abstract ' : ''; $info['name'] .= $ref->isFinal() ? ' final ' : ''; $info['name'] .= $ref->isInterface() ? 'interface ' : 'class '; $info['name'] .= $targetName = $ref->getName(); $info['declare in'] = $ref->getFileName() . ' lines ' . $ref->getStartLine() . ' to ' . $ref->getEndline(); $info['modifiers'] = Reflection::getModifierNames($ref->getModifiers()); $info['Documentation'] = $ref->getDocComment(); $info['Implements'] = $ref->getInterfaces(); $info['Constants'] = $ref->getConstants(); foreach ($ref->getProperties() as $prop) { // ReflectionProperty クラスのインスタンスを生成する $propRef = new ReflectionProperty($targetName, $prop->name); if ($propRef->isPublic()) { $porps[] = $prop->name; } } // $info['Public Properties'] = $porps; foreach ($ref->getMethods() as $method) { $methodRef = new ReflectionMethod($targetName, $method->name); if ($methodRef->isPublic() || $method->isStatic()) { $final = $method->isFinal() ? 'final ' : ''; $pubic = $method->isPublic() ? 'public ' : ''; $static = $method->isStatic() ? ' static ' : ''; $methods[] = sprintf("%s%s%s %s", $final, $pubic, $static, $method->name); } } $info['Public Methods'] = $methods; if ($ref->isInstantiable() && is_object($target)) { $info['isInstance ?'] = $ref->isInstance($target) ? 'yes' : 'no'; } if ($parent) { $info['parent'] .= $ref->getParentClass(); } break; default: $type = 'Invalid Object/Class'; $targetName = $target; $info = null; break; } print_a($info, "show_objects:1;label: Reflection of {$type} '{$targetName}'"); }
/** * Gets closure hash * @static * @param callback $closure * @return string */ public static function getClosureHash($closure) { $reflection = new \ReflectionFunction($closure); $file = new \SplFileObject($reflection->getFileName()); $file->seek($reflection->getStartLine() - 1); $content = ''; while ($file->key() < $reflection->getEndLine()) { $content .= $file->current(); $file->next(); } return self::getObjectHash(\json_encode(array($content, $reflection->getStaticVariables()))); }
/** * @param Closure $closure * @return string */ function closureHash(Closure $closure) { $ref = new ReflectionFunction($closure); $file = new SplFileObject($ref->getFileName()); $file->seek($ref->getStartLine() - 1); $content = ''; while ($file->key() < $ref->getEndLine()) { $content .= $file->current(); $file->next(); } $hash = md5(json_encode([$content, $ref->getStaticVariables()])); return $hash; }
public function testClosureHasSameStaticVariables() { $expected = new \ReflectionFunction($this->closure); $actual = new \ReflectionFunction($this->unserializeClosure()->getClosure()); $this->assertSame($expected->getStaticVariables(), $actual->getStaticVariables()); }
private static function dumpObject(&$var, $options, $level) { if ($var instanceof \Closure) { $rc = new \ReflectionFunction($var); $fields = array(); foreach ($rc->getParameters() as $param) { $fields[] = '$' . $param->getName(); } $fields = array('file' => $rc->getFileName(), 'line' => $rc->getStartLine(), 'variables' => $rc->getStaticVariables(), 'parameters' => implode(', ', $fields)); } elseif ($var instanceof \SplFileInfo) { $fields = array('path' => $var->getPathname()); } elseif ($var instanceof \SplObjectStorage) { $fields = array(); foreach (clone $var as $obj) { $fields[] = array('object' => $obj, 'data' => $var[$obj]); } } else { $fields = (array) $var; } static $list = array(); $rc = $var instanceof \Closure ? new \ReflectionFunction($var) : new \ReflectionClass($var); $out = '<span class="nette-dump-object"' . ($rc->getFileName() ? ' data-nette-href="' . htmlspecialchars(strtr(Debugger::$editor, array('%file' => rawurlencode($rc->getFileName()), '%line' => $rc->getStartLine()))) . '"' : '') . '>' . get_class($var) . '</span> <span class="nette-dump-hash">#' . substr(md5(spl_object_hash($var)), 0, 4) . '</span>'; if (empty($fields)) { return $out . "\n"; } elseif (in_array($var, $list, TRUE)) { return $out . " { <i>RECURSION</i> }\n"; } elseif (!$options[self::DEPTH] || $level < $options[self::DEPTH] || $var instanceof \Closure) { $collapsed = $level ? count($fields) >= $options[self::COLLAPSE_COUNT] : $options[self::COLLAPSE]; $out = '<span class="nette-toggle' . ($collapsed ? '-collapsed' : '') . '">' . $out . "</span>\n<div" . ($collapsed ? ' class="nette-collapsed"' : '') . '>'; $list[] = $var; foreach ($fields as $k => &$v) { $vis = ''; if ($k[0] === "") { $vis = ' <span class="nette-dump-visibility">' . ($k[1] === '*' ? 'protected' : 'private') . '</span>'; $k = substr($k, strrpos($k, "") + 1); } $out .= '<span class="nette-dump-indent"> ' . str_repeat('| ', $level) . '</span>' . '<span class="nette-dump-key">' . (preg_match('#^\\w+\\z#', $k) ? $k : self::encodeString($k)) . "</span>{$vis} => " . self::dumpVar($v, $options, $level + 1); } array_pop($list); return $out . '</div>'; } else { return $out . " { ... }\n"; } }
/** * Unwraps closure created by self::closure(), used i.e. by ObjectMixin in PHP < 5.4 * @internal * @return callable */ public static function unwrap(\Closure $closure) { $rm = new \ReflectionFunction($closure); $vars = $rm->getStaticVariables(); return isset($vars['_callable_']) ? $vars['_callable_'] : NULL; }
/** * @param Closure $callback * @return null * @throws EntityValidationException * @throws Exception */ public function transaction(Closure $callback) { $result = null; try { $this->beginTransaction(); $r = new ReflectionFunction($callback); // reload on UOW entities that could not being on the update context foreach ($r->getStaticVariables() as $var) { if ($var instanceof IEntity && $var->getIdentifier() > 0) { UnitOfWork::getInstance()->scheduleForUpdate($var); } } $result = $callback($this); $this->commit(); } catch (ValidationException $ex1) { $this->rollBack(); throw new EntityValidationException($ex1->getMessage()); } catch (Exception $ex) { $this->rollBack(); throw $ex; } return $result; }
/** * Makes this to consume the services defined in provided container * * @param Set $container * @return mixed */ public function consumeSlimContainer(Set $container) { foreach ($container as $key => $value) { if ($value instanceof \Closure) { // Try to determin if this belongs to a singleton or not $refFunc = new \ReflectionFunction($value); // Slim singletons have a static 'object' variable $shared = in_array('object', $refFunc->getStaticVariables()); $this->registerFactory($key, $value, $shared); } elseif (is_callable($value)) { // Register as non-shared factories any other callable $this->registerFactory($key, $value, false); } else { $this->sm->setService($key, $value); } } }
return count($args) >= $n ? array_reduce(array_slice($args, $n), function ($f, $x) use($op, $curry_) { return call_user_func($op($f), $x); }, call_user_func_array($op($f), array_slice($args, 0, $n))) : function () use($args, $n, $f, &$curry_) { static $curried = true; // Used by $arity return $curry_(array_merge($args, func_get_args()), $n, $f); }; }; // Find a function's arity, even if it's curried or defined as an expression $arity = function ($f_) use(&$arity, $op) { $f = $op($f_); if (!is_callable($f)) { error("Can't get arity of {$f_}"); } $rf = new ReflectionFunction($f); $sv = $rf->getStaticVariables(); // If we're defined as $f, return $f's arity if (isset($sv['defined'])) { return $arity($sv['f']); } // If we're a curried version of $f, return $f's arity if (isset($sv['curried'])) { if (isset($sv['n']) && $sv['n']) { return $sv['n'] - count($sv['args']); } if (isset($sv['f'])) { return $arity($sv['f']); } } // Otherwise, reflect the arity return $rf->getNumberOfParameters();