39
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PHPでは関数内に関数を書けるしグローバル関数になる

Posted at

ちょっと何言ってるのかわからない。

関数内関数

PHP
function foo(){
	echo 'foo';
	function bar(){
		echo 'bar';
	}
}

foo(); // foo
bar(); // bar ← !?

マジかよ。

実はしっかりマニュアルにも書かれている仕様です。

仕様ではあるのですが、つい先日までこの文法を知らなかったので、初めて見たとき面食らいました。
そもそもPHPで関数内に関数を書くという発想がなかったからということもありますが、普通はこれ関数内に閉じこもるでしょう。

JavaScript
function foo(){
	console.log('foo');
	function bar(){
		console.log('bar');
	}
}

foo(); // foo
bar(); // bar is not defined

一手加えるだけでクロージャになるやつだ。

ではPHPではクロージャができないのかというともちろんできます。

PHP
function foo(){
	echo 'foo';
	return function(){
		echo 'bar';
	}
}

$foo = foo(); // foo
$foo(); // bar

関数内の関数をreturnすることで、クロージャとして使えるようになります。
外側の値を使うにはuseが必要でJavaScriptより少しだけ面倒ですが。

でも私がこれを書くことになったとしたら、たぶんこう書きます。

PHP
class foo{
	public function __construct(){
		echo 'foo';
	}
	public function bar(){
		echo 'bar';
	}
}
$foo = new foo(); // foo
$foo->bar(); // bar

クラスのなかったJavaScriptでクロージャが発展したのはわからないでもないですが、PHPにクロージャがあって何が嬉しいのか、未だによくわかりません。
そもそもPHPのクロージャってClosureクラスですし。
クロージャがなければこれができなかった!みたいなのって何かあるんですかね?

まあ無名関数はarray_mapとかで時々使うんですが、マニュアルには『無名関数はクロージャとも呼ばれ』とか書いてあるんだけど別物だよね???

無名関数
$foo = function(){
	echo 'foo';
};

$foo(); // foo

関数内変数

これは当然グローバル展開されません。

function foo(){
	$bar = 1;
}

foo();
echo $bar; // Undefined variable: bar

変数は見えないのに関数は外に出る。
なぜなのか。

…はて、そういえば変数以外はどうなるんだ?

関数内クラス

function declareClass(int $id){
	if($id===1){
		class HOGE{
			public function __construct(){echo '$idは1';}
		}
	}else{
		class HOGE{
			public function __construct(){echo '$idは1以外';}
		}
	}
}

declareClass(1);
new HOGE(); // $idは1

declareClass(2); // 1ファイル内で書くとCannot declare classになる
new HOGE(); // $idは1以外

クラスもグローバルになるようです。
つまり、これを使えばDIできるというわけですね(できない)。

関数内インターフェイス、トレイト

function foo(int $id){
	if($id===1){
		interface HOGE{
			const bar = 1;
		}
		trait FUGA{
			public function baz(){
				return 1;
			}
		}
	}else{
		interface HOGE{
			const bar = 2;
		}
		trait FUGA{
			public function baz(){
				return 2;
			}
		}
	}
}

foo(2);

class HOGE2 implements HOGE{
	use FUGA;
}
$a = new HOGE2();
echo $a::bar, $a->baz(); // 2,2

foo(1)としたら$a::bar$a->baz()も1になります。
traitやinterfaceもグローバルに展開されるようです。

つまり、関数の中身は基本的にグローバルなんだけど、変数だけわざわざ関数外から見えないようにされているだけ、ということのようです。
なんだか元々関数に抱いていたイメージと全く逆なかんじの実装になっていました。

39
30
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?