Closure 类

(PHP 5 >= 5.3.0, PHP 7)

简介

用于代表 匿名函数 的类.

匿名函数(在 PHP 5.3 中被引入)会产生这个类型的对象。在过去,这个类被认为是一个实现细节,但现在可以依赖它做一些事情。自 PHP 5.4 起,这个类带有一些方法,允许在匿名函数创建后对其进行更多的控制。

除了此处列出的方法,还有一个 __invoke 方法。这是为了与其他实现了 __invoke()魔术方法 的对象保持一致性,但调用匿名函数的过程与它无关。

类摘要

Closure {
/* 方法 */
__construct ( void )
public static Closure bind ( Closure $closure , object $newthis [, mixed $newscope = 'static' ] )
public Closure bindTo ( object $newthis [, mixed $newscope = 'static' ] )
}

Table of Contents

User Contributed Notes

centurianii at yahoo dot co dot uk 11-Jun-2017 08:05
The problem: a function that calls another function which is passed in the arguments of the initial function. The callable might re-use the arguments of the initial function. Whatever the callable is we want a simple function for use in our code (here is where closure comes).

Example: a generic Header class that sends files (cached, not cached, for download, inline etc.). Probably readfile() is all you need right?
But what when a captcha library has a function output() that you want to be used in the place of readfile()?

No big deal you might say, pass a callable in the function's arguments, but let's unwind the possibilities of the callable argument:
1. it's another function in this scope,
2. it's a class method,
3. it's a static method of a class.

Php uses different helpers to call this argument-function:
1. call_user_func_array(),
2, 3. call_user_func_array() or Reflection.

A generic solution that exploits most of php's advanced features: closures, callables, Reflection.
/**
 * @param string $file The path of a file
 * @param ... more specific arguments here ...
 * @param string[] $call A hashed array that contains the function we want to
 * operate on our arguments and follows some rules as follows
 * -key 'class': the class name the function belongs to or null,
 * -key 'construct': the class's arguments on construction or null,
 * -key 'args': an array of arguments explicit for the callable or null,
 * -key 'callable': the name of the function (null not allowed).
 */
function generic($file = null, $call = array('callable' => '\readfile')) {
   if (isset($call['class'])) {
      // a closure...
      $action = function() use ($call) {
         $method = new \ReflectionMethod($call['class'], $call['callable']);
         if ($method->isStatic()) {
            // YOU decide what to do with the arguments in generic()
            if (isset($call['args'])) {
               $method->invokeArgs(null, $call['args']);
            } else {
               $method->invoke(null);
            }
         } else {
            $obj = new \ReflectionClass($call['class']);
            if (isset($call['construct'])) {
               $obj->newInstanceArgs($call['construct']);
            } else {
               $obj->newInstance();
            }
            // YOU decide what to do with the arguments in generic()
            if (isset($call['args'])) {
               $method->invokeArgs($obj, $call['args']);
            } else {
               $method->invoke($obj);
            }
         }
      };
   } else {
      // another closure...
      $action = function() use ($call, $file) {
         // YOU decide what to do with the arguments in generic()
         if (isset($call['args'])) {
            \call_user_func_array($call['callable'], $args);
         } else {
            \call_user_func_array($call['callable'], array($file));
         }
      };
   }
   // ...some commands here...
   \header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
   \header("Cache-Control: no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0");
   \header("Content-Description: File Transfer");
   \header("Content-Disposition: inline; filename=\"" . $file . "\"");
   \header("Content-Type: image/jpeg");
   // adjust the following...
   \header('Content-Length: ' . \filesize($file));
   // see how we use a closure
   $action();
}

// let's call the generic()
generic('client/assets/404.jpg');

Just an example use of closures...
tomschrot at gmail dot com 22-Apr-2017 07:20
Encapsulating a closure as a delegate allows really universal and comfortable event handling in your projects.
(ref. to Observer Pattern)

<?php

   
/**
     * Encapsulates a closure.
     */
   
final class Delegate
   
{
        private
$_Closure;

       
/**
         * Standard ctor with a callable as argument.
         * @param callable $closure
         */
       
public function __construct($closure)
        {
           
$this->_Closure = \Closure::fromCallable($closure);
        }

       
/**
         * Allows to call the delegate object directly.
         * @param list ...$args variable number of arguments.
         * @return mixed
         */
       
public function __invoke(...$args)
        {
            return
call_user_func_array($this->_Closure, $args);
        }
    }

   
/**
     * Defines a type for event arguments.
     */
   
class EventArgs
   
{
        protected
$_Sender;

       
/**
         * Standard ctor.
         * @param mixed $sender [optional]
         */
       
public function __construct($sender = NULL)
        {
           
$this->_Sender = $sender;
        }

       
/**
         * @property-read
         * @return object -should contain the event emitting object.
         */
       
final public function Sender() { return $this->_Sender; }
    }

   
/**
     * A basic event type for the delegate.
     */
   
class Event
   
{
        private
$_Receivers = array();

       
/**
         * Add a delegate to the event list.
         * @param Delegate $delegate
         * @return Event
         */
       
final public function Add(Delegate $delegate):Event
       
{
           
$this->_Receivers[] = $delegate;
            return
$this;
        }

       
/**
         * Fires the event.
         * @param EventArgs $e
         */
       
final public function Trigger(EventArgs $e)
        {
            foreach(
$this->_Receivers as $delegate)
               
$delegate($e);
        }
    }

   
// declare anonymous function as delegate
   
$myDelegate =
        new
Delegate
       
(
            function (
EventArgs $e)
            {
                echo
'anonymous function<br>';
            }
        );

   
// declare event, assign the delegate, trigger event
   
$myEvent = new Event();
   
$myEvent->Add($myDelegate);

   
/**
     * Defines a simple type that can handle events
     */
   
class demoEventHandler
   
{
       
/**
         * Handles incomming events.
         * Note: needs declared as public!
         * @param EventArgs $e
         */
       
public function onEvent(EventArgs $e)
        {
            echo
'class event handler<br>';
        }
    }

   
$controller = new demoEventHandler();
   
$myEvent->Add(new Delegate([$controller, 'onEvent']));
   
$myEvent->Trigger(new EventArgs($myEvent));
?>
joe dot scylla at gmail dot com 08-Apr-2016 05:46
Small little trick. You can use a closures in itself via reference.

Example to delete a directory with all subdirectories and files:

<?php
$deleteDirectory
= null;
$deleteDirectory = function($path) use (&$deleteDirectory) {
   
$resource = opendir($path);
    while ((
$item = readdir($resource)) !== false) {
        if (
$item !== "." && $item !== "..") {
            if (
is_dir($path . "/" . $item)) {
               
$deleteDirectory($path . "/" . $item);
            } else {
               
unlink($path . "/" . $item);
            }
        }
    }
   
closedir($resource);
   
rmdir($path);
};
$deleteDirectory("path/to/directoy");
?>
luk4z_7 at hotmail dot com 15-Jun-2015 02:42
Scope
A closure encapsulates its scope, meaning that it has no access to the scope in which it is defined or executed. It is, however, possible to inherit variables from the parent scope (where the closure is defined) into the closure with the use keyword:

function createGreeter($who) {
              return function() use ($who) {
                  echo "Hello $who";
              };
}

$greeter = createGreeter("World");
$greeter(); // Hello World

This inherits the variables by-value, that is, a copy is made available inside the closure using its original name.
font: Zend Certification Study Guide.
chuck at bajax dot us 10-Jun-2015 01:16
This caused me some confusion a while back when I was still learning what closures were and how to use them, but what is referred to as a closure in PHP isn't the same thing as what they call closures in other languages (E.G. JavaScript).

In JavaScript, a closure can be thought of as a scope, when you define a function, it silently inherits the scope it's defined in, which is called its closure, and it retains that no matter where it's used.  It's possible for multiple functions to share the same closure, and they can have access to multiple closures as long as they are within their accessible scope.

In PHP,  a closure is a callable class, to which you've bound your parameters manually.

It's a slight distinction but one I feel bears mentioning.