gallu’s blog

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

うわ怖い

元ネタは、某所の書籍でみた、こんなコード。

foreach($data as $key => $val) {
  $$key = $val;
}


見た瞬間から色々気になってはいたんだけど。
ふと思って試した実験コードの内容が、思ったよりもちょっとアレゲだったので共有。


まずは、初手の実験コード。

<?php

$data = array();
$data['test'] = '1';
$data['_test'] = '2';
$data['0test'] = '3';
$data['te-st'] = '4';
$data['te-st'] = '5';

foreach($data as $key => $val) {
  $$key = $val;
}

$awk = get_defined_vars();
unset($awk['_SERVER']);
unset($awk['_POST']);
unset($awk['_GET']);
unset($awk['_FILES']);
unset($awk['_COOKIE']);
unset($awk['argv']);
unset($awk['argc']);
var_dump($awk);

意図は明白。
ん…意図を理解するために、ここを引用。
http://www.php.net/manual/ja/language.variables.basics.php

PHP の変数はドル記号の後に変数名が続く形式で表されます。 変数名は大文字小文字を区別します。
変数名は、PHPの他のラベルと同じルールに従います。 有効な変数名は文字またはアンダースコアから始まり、任意の数の文字、 数字、アンダースコアが続きます。正規表現によれば、これは次の ように表現することができます。 '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'

ポイントは「有効な変数名は文字またはアンダースコアから始まり、任意の数の文字、 数字、アンダースコアが続きます」。
つまり、先頭が数字だったりハイフンが含まれてたり他の記号が含まれてたりすると「だめぽよ〜」という事。


…あれ?
んぢゃ、hashのkeyがそーゆー状態の時って、可変変数はどんな動きするんだべさ?
ってのがソースコードの意図。


で、結果。

[gallu@localhost ~]$ php t.php
array(9) {
["data"]=>
array(5) {
["test"]=>
string(1) "1"
["_test"]=>
string(1) "2"
["0test"]=>
string(1) "3"
["te-st"]=>
string(1) "5"
}
["val"]=>
string(1) "6"
["key"]=>
string(4) "this"
["test"]=>
string(1) "1"
["_test"]=>
string(1) "2"
["0test"]=>
string(1) "3"
["te-st"]=>
string(1) "5"
}

……………あれ?
エラー吐かない?


実験。

<?php

$data = array();
$data['test'] = '1';
$data['_test'] = '2';
$data['0test'] = '3';
$data['te-st'] = '4';
$data['te-st'] = '5';

foreach($data as $key => $val) {
  $$key = $val;
}
var_dump( $0test );

$awk = get_defined_vars();
unset($awk['_SERVER']);
unset($awk['_POST']);
unset($awk['_GET']);
unset($awk['_FILES']);
unset($awk['_COOKIE']);
unset($awk['argv']);
unset($awk['argc']);
var_dump($awk);

結果

[gallu@localhost ~]$ php t.php
Parse error: syntax error, unexpected '0' (T_LNUMBER), expecting variable (T_VARIABLE) or '$' in /home/furu/t.php on line 13

そうだよねぇ?
なにこれ?
「アクセスできない」変数が出来てる?


ちなみにもうちょいと実験。面倒だから部分的に切り抜くよ。

$s = '0test';
var_dump( $$s );

これだと通るんだ orz


もう一つ。
もうちょっと、ある意味深刻なケース。

var_dump( $te-st );


これ、エラー出ずに通るんだわ。ただし値が「intの0」になる。
これって「$te - st」ってパースするんだね orz
んで「$teは設定されていないからNULL」「右辺は、おそらく"文字列である st"」って解釈される。
で、算術演算子だから双方とも「一端数値にして」計算した結果、0になるんだねぇ。


……………うんすげぇや止めろ馬鹿。


ちなみに
http://www.php.net/manual/ja/language.variables.basics.php

注意: $this は特別な変数であり、ここに代入することはできません。

があるんで

$data = array();
$data['test'] = '1';
$data['_test'] = '2';
$data['0test'] = '3';
$data['te-st'] = '4';
$data['te-st'] = '5';
$data['this'] = '6';

も試したけど問題無いですよ?
面倒だから実験する気ないけど、これ、オブジェクトん中でやったらどうなるんだべさね?
…と思ったけど気になるから。

<?php

class hoge
{
  public function h($awk) {
var_dump($this);
    foreach($awk as $key => $val) {
      $$key = $val;
    }
var_dump($this);
  }
}

$data = array();
$data['test'] = '1';
$data['_test'] = '2';
$data['0test'] = '3';
$data['te-st'] = '4';
$data['te-st'] = '5';
$data['this'] = '6';

$obj = new hoge();
$obj->h($data);

$awk = get_defined_vars();
unset($awk['_SERVER']);
unset($awk['_POST']);
unset($awk['_GET']);
unset($awk['_FILES']);
unset($awk['_COOKIE']);
unset($awk['argv']);
unset($awk['argc']);
var_dump($awk);


結果

[gallu@localhost ~]$ php t.php
object(hoge)#1 (0) {
}
string(1) "6"
array(2) {
["data"]=>
array(5) {
["test"]=>
string(1) "1"
["_test"]=>
string(1) "2"
["0test"]=>
string(1) "3"
["te-st"]=>
string(1) "5"
["this"]=>
string(1) "6"
}
["obj"]=>
object(hoge)#1 (0) {
}
}

すげぇハラショー代入できちゃってるよ「ここに代入することはできません」なんだもんせめてエラーくらい吐こうよ orz


うんまぁ「嫌な予感」っておいちゃんが感じたときは大抵相応になにかあるんだけどさ。
思った以上に何かあったよ orz


上述書籍。
これをもって「簡単なテンプレートエンジン」とかって書いてあったんだけど。しょ〜じき、検証が甘すぎると思う orz
値に変な記号は使わないと思うけどさ。ハイフンは多分使う可能性が十分に考えられるし、thisとかも怖い。他にもいくつか「可能性のある」面倒毎は想像出来るし。
最低限、使うんなら「その辺を使うな」っていうか、或いはチェックロジックくらい入れておかなきゃ orz


とりあえずまぁ「可変変数」に関するおもころい(っつか恐ろしい/おぞましい)実験ができあがってしまったので、共有。