Php статические анонимные функции

Php статические анонимные функции

Watch out when ‘importing’ variables to a closure’s scope — it’s easy to miss / forget that they are actually being *copied* into the closure’s scope, rather than just being made available.

So you will need to explicitly pass them in by reference if your closure cares about their contents over time:

$one (); // outputs NULL: $result is not in scope
$two (); // outputs int(0): $result was copied
$three (); // outputs int(1)
?>

Another less trivial example with objects (what I actually tripped up on):

//set up variable in advance
$myInstance = null ;

$broken = function() uses ( $myInstance )
if(!empty( $myInstance )) $myInstance -> doSomething ();
>;

//$myInstance might be instantiated, might not be
if( SomeBusinessLogic :: worked () == true )
$myInstance = new myClass ();
>

$broken (); // will never do anything: $myInstance will ALWAYS be null inside this closure.
$working (); // will call doSomething if $myInstance is instantiated

/*
(string) $name Name of the function that you will add to class.
Usage : $Foo->add(function()<>,$name);
This will add a public function in Foo Class.
*/
class Foo
public function add ( $func , $name )
$this -> < $name >= $func ;
>
public function __call ( $func , $arguments ) call_user_func_array ( $this ->< $func >, $arguments );
>
>
$Foo = new Foo ();
$Foo -> add (function() echo «Hello World» ;
>, «helloWorldFunction» );
$Foo -> add (function( $parameterone ) echo $parameterone ;
>, «exampleFunction» );
$Foo -> helloWorldFunction (); /*Output : Hello World*/
$Foo -> exampleFunction ( «Hello PHP» ); /*Output : Hello PHP*/
?>

In case you were wondering (cause i was), anonymous functions can return references just like named functions can. Simply use the & the same way you would for a named function. right after the `function` keyword (and right before the nonexistent name).

$x =& $fn ();
var_dump ( $x , $value ); // ‘int(0)’, ‘int(0)’
++ $x ;
var_dump ( $x , $value ); // ‘int(1)’, ‘int(1)’

Every instance of a lambda has own instance of static variables. This provides for great event handlers, accumulators, etc., etc.

Creating new lambda with function() < . >; expression creates new instance of its static variables. Assigning a lambda to a variable does not create a new instance. A lambda is object of class Closure, and assigning lambdas to variables has the same semantics as assigning object instance to variables.

Example script: $a and $b have separate instances of static variables, thus produce different output. However $b and $c share their instance of static variables — because $c is refers to the same object of class Closure as $b — thus produce the same output.

Читайте также:  Ввод числа

function generate_lambda () : Closure
# creates new instance of lambda
return function( $v = null ) static $stored ;
if ( $v !== null )
$stored = $v ;
return $stored ;
>;
>

$a = generate_lambda (); # creates new instance of statics
$b = generate_lambda (); # creates new instance of statics
$c = $b ; # uses the same instance of statics as $b

$a ( ‘test AAA’ );
$b ( ‘test BBB’ );
$c ( ‘test CCC’ ); # this overwrites content held by $b, because it refers to the same object

var_dump ([ $a (), $b (), $c () ]);
?>

This test script outputs:
array(3) [0]=>
string(8) «test AAA»
[1]=>
string(8) «test CCC»
[2]=>
string(8) «test CCC»
>

/*
* An example showing how to use closures to implement a Python-like decorator
* pattern.
*
* My goal was that you should be able to decorate a function with any
* other function, then call the decorated function directly:
*
* Define function: $foo = function($a, $b, $c, . ) <. >
* Define decorator: $decorator = function($func) <. >
* Decorate it: $foo = $decorator($foo)
* Call it: $foo($a, $b, $c, . )
*
* This example show an authentication decorator for a service, using a simple
* mock session and mock service.
*/

/*
* Define an example decorator. A decorator function should take the form:
* $decorator = function($func) * return function() use $func) * // Do something, then call the decorated function when needed:
* $args = func_get_args($func);
* call_user_func_array($func, $args);
* // Do something else.
* >;
* >;
*/
$authorise = function( $func ) return function() use ( $func ) if ( $_SESSION [ ‘is_authorised’ ] == true ) $args = func_get_args ( $func );
call_user_func_array ( $func , $args );
>
else echo «Access Denied» ;
>
>;
>;

/*
* Define a function to be decorated, in this example a mock service that
* need to be authorised.
*/
$service = function( $foo ) echo «Service returns: $foo » ;
>;

/*
* Decorate it. Ensure you replace the origin function reference with the
* decorated function; ie just $authorise($service) won’t work, so do
* $service = $authorise($service)
*/
$service = $authorise ( $service );

/*
* Establish mock authorisation, call the service; should get
* ‘Service returns: test 1’.
*/
$_SESSION [ ‘is_authorised’ ] = true ;
$service ( ‘test 1’ );

/*
* Remove mock authorisation, call the service; should get ‘Access Denied’.
*/
$_SESSION [ ‘is_authorised’ ] = false ;
$service ( ‘test 2’ );

Beware of using $this in anonymous functions assigned to a static variable.

class Foo public function bar () static $anonymous = null ;
if ( $anonymous === null ) // Expression is not allowed as static initializer workaround
$anonymous = function () return $this ;
>;
>
return $anonymous ();
>
>

$a = new Foo ();
$b = new Foo ();
var_dump ( $a -> bar () === $a ); // True
var_dump ( $b -> bar () === $a ); // Also true
?>

In a static anonymous function, $this will be the value of whatever object instance that method was called on first.

Читайте также:  What does thread mean in java

To get the behaviour you’re probably expecting, you need to pass the $this context into the function.

class Foo public function bar () static $anonymous = null ;
if ( $anonymous === null ) // Expression is not allowed as static initializer workaround
$anonymous = function ( self $thisObj ) return $thisObj ;
>;
>
return $anonymous ( $this );
>
>

$a = new Foo ();
$b = new Foo ();
var_dump ( $a -> bar () === $a ); // True
var_dump ( $b -> bar () === $a ); // False
?>

You can always call protected members using the __call() method — similar to how you hack around this in Ruby using send.

class Fun
<
protected function debug ( $message )
<
echo «DEBUG: $message \n» ;
>

public function yield_something ( $callback )
<
return $callback ( «Soemthing!!» );
>

public function having_fun ()
<
$self =& $this ;
return $this -> yield_something (function( $data ) use (& $self )
<
$self -> debug ( «Doing stuff to the data» );
// do something with $data
$self -> debug ( «Finished doing stuff with the data.» );
>);
>

// Ah-Ha!
public function __call ( $method , $args = array())
<
if( is_callable (array( $this , $method )))
return call_user_func_array (array( $this , $method ), $args );
>
>

$fun = new Fun ();
echo $fun -> having_fun ();

When using anonymous functions as properties in Classes, note that there are three name scopes: one for constants, one for properties and one for methods. That means, you can use the same name for a constant, for a property and for a method at a time.

Since a property can be also an anonymous function as of PHP 5.3.0, an oddity arises when they share the same name, not meaning that there would be any conflict.

Consider the following example:

class MyClass const member = 1 ;

public function member () return «method ‘member'» ;
>

public function __construct () $this -> member = function () return «anonymous function ‘member'» ;
>;
>
>

header ( «Content-Type: text/plain» );

var_dump ( MyClass :: member ); // int(1)
var_dump ( $myObj -> member ); // object(Closure)#2 (0) <>
var_dump ( $myObj -> member ()); // string(15) «method ‘member'»
$myMember = $myObj -> member ;
var_dump ( $myMember ()); // string(27) «anonymous function ‘member'»
?>

That means, regular method invocations work like expected and like before. The anonymous function instead, must be retrieved into a variable first (just like a property) and can only then be invoked.

Источник

Анонимные функции

Анонимные функции, также известные как замыкания (closures), позволяют создавать функции, не имеющие определенных имен. Они наиболее полезны в качестве значений callback-параметров, но также могут иметь и множество других применений.

Пример #1 Пример анонимной функции

echo preg_replace_callback ( ‘~-([a-z])~’ , function ( $match ) return strtoupper ( $match [ 1 ]);
>, ‘hello-world’ );
// выведет helloWorld
?>

Читайте также:  Python telegram bot hosting

Замыкания также могут быть использованы в качестве значений переменных; PHP автоматически преобразует такие выражения в экземпляры внутреннего класса Closure. Присвоение замыкания переменной использует тот же синтаксис, что и для любого другого присвоения, включая завершающую точку с запятой:

Пример #2 Пример присвоения анонимной функции переменной

Замыкания могут также наследовать переменные из родительской области видимости. Любая подобная переменная должна быть объявлена в конструкции use.

Пример #3 Наследование переменных из родительской области видимости

// Без «use»
$example = function () var_dump ( $message );
>;
echo $example ();

// Наследуем $message
$example = function () use ( $message ) var_dump ( $message );
>;
echo $example ();

// Значение унаследованной переменной задано там, где функция определена,
// но не там, где вызвана
$message = ‘world’ ;
echo $example ();

// Сбросим message
$message = ‘hello’ ;

// Измененное в родительской области видимости значение
// остается тем же внутри вызова функции
$message = ‘world’ ;
echo $example ();

// Замыкания могут принимать обычные аргументы
$example = function ( $arg ) use ( $message ) var_dump ( $arg . ‘ ‘ . $message );
>;
$example ( «hello» );
?>

Результатом выполнения данного примера будет что-то подобное:

Notice: Undefined variable: message in /example.php on line 6 NULL string(5) "hello" string(5) "hello" string(5) "hello" string(5) "world" string(11) "hello world"

Наследование переменных из родительской области видимости не то же самое, что использование глобальных переменных. Глобальные переменные существуют в глобальной области видимости, которая не меняется, вне зависимости от того, какая функция выполняется в данный момент. Родительская область видимости — это функция, в которой было объявлено замыкание (не обязательно та же самая, из которой оно было вызвано). Смотрите следующий пример:

Пример #4 Замыкания и область видимости

// Базовая корзина покупок, содержащая список добавленных
// продуктов и количество каждого продукта. Включает метод,
// вычисляющий общую цену элементов корзины с помощью
// callback-замыкания.
class Cart
const PRICE_BUTTER = 1.00 ;
const PRICE_MILK = 3.00 ;
const PRICE_EGGS = 6.95 ;

protected $products = array();

public function add ( $product , $quantity )
$this -> products [ $product ] = $quantity ;
>

public function getQuantity ( $product )
return isset( $this -> products [ $product ]) ? $this -> products [ $product ] :
FALSE ;
>

public function getTotal ( $tax )
$total = 0.00 ;

array_walk ( $this -> products , $callback );
return round ( $total , 2 );
>
>

// Добавляем несколько элементов в корзину
$my_cart -> add ( ‘butter’ , 1 );
$my_cart -> add ( ‘milk’ , 3 );
$my_cart -> add ( ‘eggs’ , 6 );

// Выводим общую сумму с 5% налогом на продажу.
print $my_cart -> getTotal ( 0.05 ) . «\n» ;
// Результатом будет 54.29
?>

Список изменений

Версия Описание
5.4.0 Стало возможным использовать $this в анонимных функциях.
5.3.0 Появление анонимных функций.

Примечания

Замечание: Совместно с замыканиями можно использовать функции func_num_args() , func_get_arg() и func_get_args() .

Источник

Оцените статью