クロージャ採用ですって!

http://news.php.net/php.cvs/50908

うっはー信じられぬ。
ざっと見たところ、大抵のことは出来てる模様。


コンパイル時のテスト内容を引用して、仕様を確認してみる。

  • Closure 001: Lambda without lexical variables
  • Closure 002: Lambda with lexical variables (global scope)
  • Closure 003: Lambda with lexical variables (local scope)
  • Closure 004: Lambda with lexical variables (scope lifetime)
  • Closure 005: Lambda inside class, lifetime of $this
  • Closure 006: Nested lambdas
  • Closure 007: Nested lambdas in classes
  • Closure 008: Use in preg_replace()
  • Closure 009: Use in preg_replace()
  • Closure 010: Closure calls itself
  • Closure 011: Lexical copies not static in closure
  • Closure 012: Undefined lexical variables

Closure 001: Lambda without lexical variables

レキシカル変数を含まないラムダ式
$lambda = function () {}; は、ちゃんと is_callable($lambda) == true になるみたい。
call_user_func($lambda, $some_variable); も動く。

<?php

$lambda1 = function () {
	echo "Hello World!\n";
};

$lambda2 = function ($x) {
	echo "Hello $x!\n";
};

var_dump(is_callable($lambda1));
var_dump(is_callable($lambda2));
$lambda1();
$lambda2("Universe");
call_user_func($lambda1);
call_user_func($lambda2, "Universe");

echo "Done\n";
?>
bool(true)
bool(true)
Hello World!
Hello Universe!
Hello World!
Hello Universe!
Done

Closure 002: Lambda with lexical variables (global scope)

レキシカル変数を含むラムダ式のうち、そのレキシカル変数がグローバルスコープだった場合。
use() を使う。
use($lexical) だと、定義時の変数をコピーする?
use(&$lexical) だと、定義時の変数にリファレンスを張る?

<?php

$x = 4;

$lambda1 = function () use ($x) {
	echo "$x\n";
};

$lambda2 = function () use (&$x) {
	echo "$x\n";
};

$lambda1();
$lambda2();
$x++;
$lambda1();
$lambda2();

echo "Done\n";
?>
4
4
4
5
Done

Closure 003: Lambda with lexical variables (local scope)

レキシカル変数を含むラムダ式のうち、そのレキシカル変数がローカルスコープだった場合。
同様に動く。

<?php

function run () {
	$x = 4;

	$lambda1 = function () use ($x) {
		echo "$x\n";
	};

	$lambda2 = function () use (&$x) {
		echo "$x\n";
	};

	$lambda1();
	$lambda2();
	$x++;
	$lambda1();
	$lambda2();
}

run();

echo "Done\n";
?>
4
4
4
5
Done

Closure 004: Lambda with lexical variables (scope lifetime)

ローカルスコープのレキシカル変数を含むラムダ式は、そのスコープを抜けても生きている。

<?php

function run () {
	$x = 4;

	$lambda1 = function () use ($x) {
		echo "$x\n";
	};

	$lambda2 = function () use (&$x) {
		echo "$x\n";
		$x++;
	};

	return array($lambda1, $lambda2);
}

list ($lambda1, $lambda2) = run();

$lambda1();
$lambda2();
$lambda1();
$lambda2();

echo "Done\n";
?>
4
4
4
5
Done

Closure 005: Lambda inside class, lifetime of $this

ラムダ式の中に $this が入っていた場合の、オブジェクトの生存期間。
$this を持つラムダ式が全部消えたら消える。
まあ普通よね。
static function () {}; は、 $this を持つラムダ式とはカウントされない。

<?php

class A {
	private $x;

	function __construct($x) {
		$this->x = $x;
	}

	function __destruct() {
		echo "Destroyed\n";
	}

	function getIncer($val) {
		return function() use ($val) {
			$this->x += $val;
		};
	}

	function getPrinter() {
		return function() {
			echo $this->x."\n";
		};
	}
	
	function getError() {
		return static function() {
			echo $this->x."\n";
		};
	}
	
	function printX() {
		echo $this->x."\n";
	}
}

$a = new A(3);
$incer = $a->getIncer(2);
$printer = $a->getPrinter();
$error = $a->getError();

$a->printX();
$printer();
$incer();
$a->printX();
$printer();

unset($a);

$incer();
$printer();

unset($incer);
$printer();

unset($printer);

$error();

echo "Done\n";
?>
3
3
5
5
7
7
Destroyed

Fatal error: Using $this when not in object context in %sclosure_005.php on line 28

Closure 006: Nested lambdas

ネストできるよ!

<?php

$getClosure = function ($v) {
	return function () use ($v) {
		echo "Hello World: $v!\n";
	};
};

$closure = $getClosure (2);
$closure ();

echo "Done\n";
?>
Hello World: 2!
Done

Closure 007: Nested lambdas in classes

ネストできるよ!!

<?php

class A {
	private $x = 0;

	function getClosureGetter () {
		return function () {
			return function () {
				$this->x++;
			};
		};
	}

	function printX () {
		echo $this->x."\n";
	}
}

$a = new A;
$a->printX();
$getClosure = $a->getClosureGetter();
$a->printX();
$closure = $getClosure();
$a->printX();
$closure();
$a->printX();

echo "Done\n";
?>
0
0
0
1
Done

Closure 008: Use in preg_replace()

preg_replace() で使いたくなるよね、やっぱり。

<?php

function replace_spaces($text) {
	$lambda = function ($matches) {
		return str_replace(' ', '&nbsp;', $matches[1]).' ';
	};
	return preg_replace_callback('/( +) /', $lambda, $text);
}

echo replace_spaces("1 2 3\n");
echo replace_spaces("1  2  3\n");
echo replace_spaces("1   2   3\n");
echo "Done\n";
?>
1 2 3
1  2  3
1   2   3
Done

Closure 009: Use in preg_replace()

preg_replace() その 2 。

<?php
$a = 1;
$x = function ($x) use ($a) {
  static $n = 0;
  $n++;
  $a = $n.':'.$a;
  echo $x.':'.$a."\n";
};
$y = function ($x) use (&$a) {
  static $n = 0;
  $n++;
  $a = $n.':'.$a;
  echo $x.':'.$a."\n";
};
$x(1);
$x(2);
$x(3);
$y(4);
$y(5);
$y(6);
?>
1:1:1
2:2:1
3:3:1
4:1:1
5:2:1:1
6:3:2:1:1

Closure 010: Closure calls itself

$lambda = function () { $lambda(); };できないのね。
スコープ的におかしいし、これで良し。

<?php
$i = 3;
$lambda = function ($lambda) use (&$i) {
    if ($i==0) return;
    echo $i--."\n";
    $lambda($lambda);
};
$lambda($lambda);
echo "$i\n";
?>
3
2
1
0

Closure 011: Lexical copies not static in closure

以前のプロトタイプだとおかしかったけど、今はもう大丈夫、ってさ。

<?php
$i = 1;
$lambda = function () use ($i) {
    return ++$i;
};
$lambda();
echo $lambda()."\n";
//early prototypes gave 3 here because $i was static in $lambda
?>
2

Closure 012: Undefined lexical variables

未定義の変数使うと訳分からなくなるよ!

<?php
$lambda = function () use ($i) {
    return ++$i;
};
$lambda();
$lambda();
var_dump($i);
$lambda = function () use (&$i) {
    return ++$i;
};
$lambda();
$lambda();
var_dump($i);
?>
Notice: Undefined variable: i in %sclosure_012.php on line 2

Notice: Undefined variable: i in %sclosure_012.php on line 7
NULL
int(2)