浜松のWEBシステム開発・スマートフォンアプリ開発・RTK-GNSS関連の開発はお任せください
株式会社シーポイントラボ
TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:静岡県浜松市中区富塚町1933-1 佐鳴湖パークタウンサウス2F

【PHP】PHP8.1で名前が数値や記号で始まる変数を作れる小ネタ

 使い道のなさそうな PHP8.1 の小ネタです。

 PHP は通常変数名を数値や_以外の記号で始められません。

$0 = 31; // ←は文法エラーとなります。
// 次リンクには" 有効な変数名は文字またはアンダースコアから始まり、任意の数の文字、 数字、アンダースコアが続きます"とあります。
// https://www.php.net/manual/ja/language.variables.basics.php

 しかし PHP8.1 では次の様に可変変数や $GLOBALS を使うことで本来無効な変数名でも変数として宣言できます。

PHP: 可変変数 – Manual
PHP: $GLOBALS – Manual

 ソースコードとデモは次です。
Online PHP editor | output for 2nUnl#↓のデモ

<?php

// よくある変数宣言と代入の例
$a = 17;
// グローバルスコープに現在定義されているすべての変数への参照を含む連想配列である $GLOBALS から $a を見る
// @see https://www.php.net/manual/ja/reserved.variables.globals.php
echo '$a: '.$GLOBALS['a']."\n"; // $a: 17

// $0 を作る例
// $0 がグローバルスコープにないことを確認
var_dump(isset($GLOBALS[0])); // bool(false)
// 可変変数で変数名として 0 を使います
$varName  = 0;
$$varName = 31;
// $0 がグローバルスコープに増えたことを確認
var_dump(isset($GLOBALS[0])); // bool(true)
echo '$0: '.$GLOBALS[0]."\n"; // $0: 31 // グローバルスコープ参照
echo '$0: '.$$varName."\n";   // $0: 31 // 可変変数参照
echo "$0: ${$varName}\n";     // $0: 31 // 可変変数的文字列中の変数のパース https://www.php.net/manual/ja/language.types.string.php#language.types.string.parsing
echo "$0: 
<?php

// よくある変数宣言と代入の例
$a = 17;
// グローバルスコープに現在定義されているすべての変数への参照を含む連想配列である $GLOBALS から $a を見る
// @see https://www.php.net/manual/ja/reserved.variables.globals.php
echo '$a: '.$GLOBALS['a']."\n"; // $a: 17

// $0 を作る例
// $0 がグローバルスコープにないことを確認
var_dump(isset($GLOBALS[0])); // bool(false)
// 可変変数で変数名として 0 を使います
$varName  = 0;
$$varName = 31;
// $0 がグローバルスコープに増えたことを確認
var_dump(isset($GLOBALS[0])); // bool(true)
echo '$0: '.$GLOBALS[0]."\n"; // $0: 31 // グローバルスコープ参照
echo '$0: '.$$varName."\n";   // $0: 31 // 可変変数参照
echo "$0: ${$varName}\n";     // $0: 31 // 可変変数的文字列中の変数のパース https://www.php.net/manual/ja/language.types.string.php#language.types.string.parsing
echo "$0: ${0}\n";            // $0: 31 // 直打ち的文字列中の変数のパース https://www.php.net/manual/ja/language.types.string.php#language.types.string.parsing

// また、上の $0 同様にこれを利用して定義済み変数の $GLOBALS とは別のグローバルスコープ下にある $GLOBALS を作れます。
// $GLOBALS が通常のグローバルスコープ下にないことを確認
var_dump(isset($GLOBALS['GLOBALS'])); // bool(false)

$varName = 'GLOBALS';
$$varName = 47;
// $GLOBALS が通常のグローバルスコープ下に増えたことを確認
var_dump(isset($GLOBALS['GLOBALS'])); // bool(true)
echo '$GLOBALS: '.$GLOBALS['GLOBALS']."\n"; // $GLOBALS: 47 // 通常のグローバルスコープ参照
echo '$GLOBALS: '.$$varName."\n";           // $GLOBALS: 47 // 可変変数参照

// $GLOBALSと文字列中の変数のパースの組み合わせでは可変変数的に呼び出すか定義済み名そのもので呼び出すかで挙動が変わります。
// 可変変数的に文字列中の変数のパース
echo "\$GLOBALS: ${$varName}\n";  // $GLOBALS: 47
// 定義済み名そのもので文字列中の変数のパース
echo "\$GLOBALS: ${'GLOBALS'}\n";
// Warning: Array to string conversion in xxx
// $GLOBALS: Array

\n"; // $0: 31 // 直打ち的文字列中の変数のパース https://www.php.net/manual/ja/language.types.string.php#language.types.string.parsing // また、上の $0 同様にこれを利用して定義済み変数の $GLOBALS とは別のグローバルスコープ下にある $GLOBALS を作れます。 // $GLOBALS が通常のグローバルスコープ下にないことを確認 var_dump(isset($GLOBALS['GLOBALS'])); // bool(false) $varName = 'GLOBALS'; $$varName = 47; // $GLOBALS が通常のグローバルスコープ下に増えたことを確認 var_dump(isset($GLOBALS['GLOBALS'])); // bool(true) echo '$GLOBALS: '.$GLOBALS['GLOBALS']."\n"; // $GLOBALS: 47 // 通常のグローバルスコープ参照 echo '$GLOBALS: '.$$varName."\n"; // $GLOBALS: 47 // 可変変数参照 // $GLOBALSと文字列中の変数のパースの組み合わせでは可変変数的に呼び出すか定義済み名そのもので呼び出すかで挙動が変わります。 // 可変変数的に文字列中の変数のパース echo "$GLOBALS: ${$varName}\n"; // $GLOBALS: 47 // 定義済み名そのもので文字列中の変数のパース echo "$GLOBALS: ${'GLOBALS'}\n"; // Warning: Array to string conversion in xxx // $GLOBALS: Array

 使い道はちょっと思いつきませんが上記の様に$0や定義済みの$GLOBALSとは異なる$GLOBALSを扱えます。この辺り PHP のバージョンに関わらず平易な使い方以外をするとなかなか危そうです。例えば次の様なことも起こせます。

<?php

var_dump(isset($GLOBALS['GLOBALS'])); // PHP8.0以前は bool(true)、PHP8.1以降は bool(false)
var_dump(is_array($GLOBALS) ? 'array' : $GLOBALS); // どのバージョンでも 'array'
$GLOBALS['GLOBALS'] = 'exmaple';
var_dump(isset($GLOBALS['GLOBALS'])); // PHP5.0.0~5.0.2とPHP5.4~8.0は bool(false)、PHP8.1以降とPHP5.0.3~PHP5.3.29とPHP4.4以前は bool(true)
var_dump(is_array($GLOBALS) ? 'array' : $GLOBALS); // PHP8.0以前は 'example'、PHP8.1以降は 'array'
  • この記事いいね! (0)