変数

編集

変数は、値に名前をつけてプログラム中から参照できるようにする仕組みです。 変数は必ず名前を持ち、名前は識別子のルールに従います。

変数の名前と識別子のルール

編集

変数の名前は、$ で始まり識別子が続きます。

識別子は、先頭が _ または英字、それに 0 文字以上の _ または 英数字が続きます。

有効な変数
$kk3k
$_kkk54
無効な変数
$89aa
$7_loop

また、PHPでは、変数名の大文字小文字を区別します。

$name  $NAME  $Name  $namE  $nAME
はすべて違う変数です。

関数、ラベル、定数、クラス、トレイト、インターフェース、名前空間なども名前を持つことができますが、それぞれ識別子の規則に従います。

変数を使う

編集

変数を使ったプログラム。

<?php
$a = 99;

echo $a, PHP_EOL;
?>
実行結果
99
上記コードは変数 $a に値 99を代入し、それをecho文によって表示するプログラムです。
このように変数は、文字列を入れることもできます。
PHPでは、変数は宣言する必要はありません(できません)。強いて言うなら、最初の代入が変数のスコープのはじまりという意味で宣言に相当します。
引用符の中での変数の埋込み

ほかの例も下記に見ていきましょう。

<?php
$a = "こんにちは";

echo "挨拶 : {$a}", PHP_EOL;
?>
実行結果
挨拶 : こんにちは
このように、引用符の中に変数を埋め込むことができます。
$aを{}で囲んでいるのは、分かち書きされていないと変数名を正しくparseできないからです。

文字列への変数の埋込み

編集
<?php $a = 7; >>
<?=  $a, " is answer"; ?>
実行結果
7 is answer

これは、変数の文字列への埋込み機能を使うと次のようにも書けます。

<?php $a = 7; >>
<?= "$a is answer"; ?>
また、$aが答えです。では、どこまでが変数かわからないので
{$a}が答えです。{ }(波括弧)で囲います。

echo, print, printfとsprintf

編集

print は上位互換で短縮表記もある echo があるので殆ど使われませんが、Cのprintf()/sprintf()と同じ機能の、 printf()/sprintf()がPHPにはあり、書式化出力に便利です。

printf()
<?php
$a = 7;
printf("%d is answer", $a);
?>
sprintf()
<?= sprintf("%d is answer", 7); ?>
実行結果
7 is answer
sprintf()は、標準出力に書き出すのではなく、文字列を返します。
Cのsprintf()と違いバッファの管理は処理系が行ってくれるので、バッファオーバーランを気にしなくても良いのがメリットです。

演算子

編集

PHPでは他のプログラミング言語同様、数学風の演算子式で計算を行います。

<?php
header('Content-Type: text/plain');

$x = 11;
$y = 7;

echo "$x + $y --> ", $x + $y, PHP_EOL;
echo "$x - $y --> ", $x - $y, PHP_EOL;
echo "$x * $y --> ", $x * $y, PHP_EOL;
echo "$x / $y --> ", $x / $y, PHP_EOL;
echo "intdiv($x, $y) --> ", intdiv($x, $y), PHP_EOL;
echo "$x % $y --> ", $x % $y, PHP_EOL;
?>
実行結果
11 + 7 --> 18
11 - 7 --> 4
11 * 7 --> 77
11 / 7 --> 1.5714285714286
intdiv(11, 7) --> 1 
11 % 7 --> 4
PHPの /(除算)の結果は浮動小数点数です。
整数の範囲で割り算をしたい場合には、PHP7以降ではintdiv() 関数を使うことで実現できます。
PHP7以前でも、小数点斬り捨ての関数 floor()などで、割り算の整数値を求められます。
負数の丸め方向には特に注意が必要です。
<?= 11 / 7; ?>
実行結果
1.5714285714286
という浮動小数点数です。

整数除算は、intdiv(被除数, 除数) です

intdiv()
<?= intdiv(70,12); ?>
実行結果
5
まとめ
+, -, *, /, %のような演算を表す記号を演算子とよびます(これらは二項演算子(中置演算子とも))。
それに対し、intdiv()のような形式は関数と呼びます。
とはいうものの、PHPは、演算子・関数・コマンドの区別が曖昧で、公式のドキュメントでも混用が見られます。
なにか型ジャグリングと通じるものがありますね。

演算子の優先順位と結合方向

編集

演算子の優先順位

編集

PHPも、多くのプログラミング言語と同じく演算子に優先順位があります。

1 + 2 * 3

( 1 + 2 ) * 3

ではなく

1 + ( 2 * 3 )

の順で評価されます(それぞれの演算子の優先順位については、後ほど表にまとめます)。

演算子の結合方向

編集

異なる優先順位の演算子であれば、優先順位に従い評価すればよいのですが、同じ優先順位の演算子が続いた場合はどうでしょう。

1 + 2 + 3

…これは交換則が成り立つので良い例ではないですね。

1 - 2 - 3

1 - ( 2 - 3 )

ではなく

( 1 - 2 ) - 3

の順で評価されます。このような評価順序の演算子を左結合の演算子と呼びます。

演算子は概ね左結合ですが、少数の例外があります。

$a = $b = 42;

これ(代入演算子)は、

$a = ( $b = 42 );

の順(右結合)で評価されます。

また、

2 ** 3 ** 4

(冪乗演算子)も

2 ** ( 3 ** 4 )

の順(右結合)で評価されます。

演算子の優先順位表

編集
演算子の優先順位
結合方向 演算子 備考
(非) clone new clone と new
** 算術
(非) + - ++ -- ~ (int) (float) (string) (array) (object) (bool) @ 算術 (単項+-), インクリメント/デクリメント, ビット演算, キャストとエラー制御
instanceof
(非) ! 論理
* / % 算術
+ - . 算術 (binary +-), array と string (. はPHP 8.0.0以降)
<< >> ビット演算
. string (PHP 8.0.0以前)
(無) < <= > >= 比較演算子
(無) == != === !== <> <=> 比較演算子
& ビット演算 と リファレンス
^ ビット演算
| ビット演算
&& 論理積
|| 論理和
?? null 合体演算子
(無) ? : 条件演算 (PHP 8.0.0より前は左結合)
= += -= *= **= /= .= %= &= |= ^= <<= >>= ??= 代入
(非) yield from yield from
(非) yield yield
(非) print print
and 論理積
xor 排他的論理和
or 論理和

リテラル

編集

リテラルとは、ハードコードされた値のことです。リテラルはスクリプトの中で特定の値を表現する手段となります。例えば代入の右辺には次のようなものがあります。

文字リテラルの例
<?php
header('Content-Type: text/plain');

$myLiteral = "a fixed value";
var_dump($myLiteral);
?>
実行結果
string(13) "a fixed value"
リテラルにはいくつかの種類があります。最も一般的なのは文字列リテラルですが、他にも整数や浮動小数点のリテラル、論理値のリテラル、配列や連想配列のリテラルなどがあります。
連想配列リテラルの例
<?php
header('Content-Type: text/plain');

$myHash = [
  'name' => 'value',
  'anotherName' => 'anotherValue',
];
var_export($myHash);
?>
実行結果
array (
  'name' => 'value',
  'anotherName' => 'anotherValue', 
)

型と型名とリテラル

編集

PHPの型と型名とリテラルの例を示します。

PHPの型と型名とリテラル
種類 型名 リテラル 説明
null null
<?php
null;
?>
  • nullは値がない状態を示し、$a = null; のとき、isset($a) は false となります。
  • null | 型 の短縮表記は ?型 です。
  • リテラルでは、大文字と小文字を区別しません。
論理型 bool
<?php
false, true;
?>
  • 論理型は、falseとtrueしか取りえない enum のように振る舞います。
  • リテラルでは、大文字と小文字を区別しません。
  • 歴史的な理由で、boolean と表現される場合があります。
数値 整数 int
<?php
123, // 10進数
0123, // 8進数
0o123, // 8進数
0xbadbeef, // 16進数
0b01011100, // 2進数
1_234_567;  // _ で区切られた10進数
?>
  • 整数型は、10進数・8進数・16進数・2進数のいずれかです。
  • 読みやすさのために、_(アンダースコアー)を挿入できます。
    • 先頭と末尾には挿入できません。
    • 2つ以上続けて _ を挿入することもできません。
  • 歴史的な理由で、integer と表現される場合があります。
浮動小数点数 float
<?php
1.23, // 小数表現
1.1234e-4, // 指数表現
1E99, // 指数表現
2.345_670_898e4, // _ で区切られた指数表現
Inf, // 無限大
-Inf;  // 負の無限大
NAN; // 非数
?>
  • 浮動小数点数型は、ISO/IEC/IEEE 60559:2011のPHP実装です。
  • 歴史的な理由で、double と表現される場合があります。
文字列 string
<?php
$a = "XYZ"
'abc def $a', // ⇒ ’abc def $a’
"abc def $a"; // ⇒ ’abc def XYZ’

$b = <<<EOS
123
abc
\r\n
EOS;

$c = <<<'EOS'
123
abc
\r\n
EOS;
?>
  • 文字列型は一連の文字です。
  • 1 文字は 1 バイトと同じです。
    つまり、PHP は 256 文字の文字集合しかサポートいません。
    ネイティブの Unicode サポートはありません。
配列 array
<?php
$ary = [1,2,3]; // ブラケット表現
$ary = array(1,2,3); // array()表現
?>
  • 配列型は、任意の型の値を順序付けて保持します。
  • 配列型は、連想配列型のキーを整数に限定した特殊なケースで、型名も同じ array となります。
連想配列 array
<?php
# ブラケット表現
$ary = [
    "a" => 1,
    "b" => 2,
    "c" => 3,
]; 

# array()表現
$ary = array(
    "a" => 1,
    "b" => 2,
    "c" => 3,
); 
?>
  • 連想配列型は、整数または文字列のキーと任意の値のペアを順序なく保持します。
    キーの重複は許されません。
リソース resource (なし)
  • リソースは、データベースへの接続やファイルハンドルなど、外界のリソースと結びついたインスタンスの型で、delegate の一種と考えられます。
<?php
header("Content-Type: text/plain");

$fh = fopen("/etc/hosts", "r");
echo get_debug_type($fh), PHP_EOL;
fclose($fh);
echo get_debug_type($fh), PHP_EOL;
実行結果
resource (stream) 
resource (closed)
オブジェクト object (なし)
  • オブジェクトは、クラスのインスタンスです。
  • クラスを型と考えることもできます。
<?php
header("Content-Type: text/plain");

echo get_debug_type(new stdClass), PHP_EOL;
echo get_debug_type(new class {}), PHP_EOL;
実行結果
stdClass 
class@anonymous

定数

編集

PHPには、$で始まる変数とは別に、イミュータブルなオブジェクト=定数を宣言することができます。

<?php
const a = 42;

echo "a = ", a , PHP_EOL;
?>
実行結果
a = 42
定数は変数と違い $ を前置しません。
定数への代入(エラーになります)
<?php
const a = 99;

echo "a = ", a , PHP_EOL;

a = 10;
?>
エラー
PHP Parse error:  syntax error, unexpected token "=" in /workspace/Main.php on line 6
このように、定数への代入はパース時に構文エラーになります。
また変数と定数は名前空間が異なるので、同じ名前の変数と定数があっても問題ありません。

define()

編集

組込み関数 define() を使っても定義できますが

  • キーワードも定数として定義できてしまう
  • 大文字小文字を区別しない定数が定義できてしまう
    PHP 8.0.0 以降では無効。この機能を使っていたプログラムはPHP 8.0.0 以降では動きません。
  • const 宣言と機能が重複する

というバグ級の仕様上の問題(8.0.0で一部是正)があります。

const が使えるのであれば、define()は使うべきではありません。

あえて define() を使うべきケース
クラスのインスタンスを定数にしたい場合
const では、クラスのインスタンスを定数にできません(8.1.13時点で、コンパイル時に確定している値しか定数にできません)。
例えば、複素数型の定数として虚数単位 i を定義したい場合困ります。
function i() { return new Complex(0, 1); }でも良い気がしますが、毎回インスタンスが新たに作られるので、 === が思ったように機能しません。

readonlyプロパティ

編集

const と似た仕組みに、PHP 8.1.0 以降のクラスのプロパティのreadonly修飾子があります。 ただし、readonly修飾子はプロパティにしか使えません。

また、クラスはクラススコープの定数「クラス定数」を定義することができます。

マジカル定数

編集

PHPには9つのマジカル定数( Magic constants )があり、それらは使用される場所によって変化します[1]。 たとえば、__LINE__ の値は、スクリプトのどの行で使用されるかに依存します。 これらの「魔法の」定数は、実行時に解決される通常の定数とは異なり、すべてコンパイル時に解決されます。 これらの特殊な定数は大文字と小文字を区別しません。

PHP のマジカル定数
名称 説明
__LINE__ ファイル中の現在の行番号
__FILE__ シンボリックリンクを解決したファイルのフルパスとファイル名。インクルード内で使用された場合、インクルードファイルの名前が返されます。
__DIR__ ファイルが存在するディレクトリ。インクルード内で使用された場合、インクルードファイルのディレクトリが返されます。これは、dirname(__FILE__)と同等です。このディレクトリ名は、ルートディレクトリでない限り、末尾のスラッシュを持ちません。
__FUNCTION__ 関数名。匿名関数の場合は {closure}
__CLASS__ クラス名。クラス名には、宣言された名前空間が含まれます (例: Foo\Bar)。trait メソッドで使用される場合、__CLASS__ は trait が使用されるクラスの名前になります。
__TRAIT__ トライト名。トレイト名には宣言された名前空間が含まれます (例: Foo\Bar)。
__METHOD__ クラスメソッド名。
__NAMESPACE__ 現在のネームスペース名。
クラス::class 完全修飾クラス名。
<?php
declare(strict_types=1);

namespace MyNamespace {
    header('Content-Type: text/plain');

    trait MyTrait {
        public function myTraitFunction(): void
        {
            echo "--- In trait method call ---", PHP_EOL;
            echo "__DIR__ = ", __DIR__, PHP_EOL;
            echo "__FILE__ = ", __FILE__, PHP_EOL;
            echo "__LINE__ = ", __LINE__, PHP_EOL;
            echo "__FUNCTION__ = ", __FUNCTION__, PHP_EOL;
            echo "__METHOD__ = ", __METHOD__, PHP_EOL;
            echo "__CLASS__ = ", __CLASS__, PHP_EOL;
            echo "__TRAIT__ = ", __TRAIT__, PHP_EOL;
            echo "__NAMESPACE__ = ", __NAMESPACE__, PHP_EOL;
            echo "MyClass::class = ", MyClass::class, PHP_EOL, PHP_EOL;
        }
    }
    class MyClass {
        use MyTrait;
        
        public function __construct() {
            echo "--- In construcror ---", PHP_EOL;
            echo "__DIR__ = ", __DIR__, PHP_EOL;
            echo "__FILE__ = ", __FILE__, PHP_EOL;
            echo "__LINE__ = ", __LINE__, PHP_EOL;
            echo "__FUNCTION__ = ", __FUNCTION__, PHP_EOL;
            echo "__METHOD__ = ", __METHOD__, PHP_EOL;
            echo "__CLASS__ = ", __CLASS__, PHP_EOL;
            echo "__TRAIT__ = ", __TRAIT__, PHP_EOL;
            echo "__NAMESPACE__ = ", __NAMESPACE__, PHP_EOL;
            echo "MyClass::class = ", MyClass::class, PHP_EOL, PHP_EOL;
        }
        public function myFunction(): void
        {
            echo "--- In method call ---", PHP_EOL;
            echo "__DIR__ = ", __DIR__, PHP_EOL;
            echo "__FILE__ = ", __FILE__, PHP_EOL;
            echo "__LINE__ = ", __LINE__, PHP_EOL;
            echo "__FUNCTION__ = ", __FUNCTION__, PHP_EOL;
            echo "__METHOD__ = ", __METHOD__, PHP_EOL;
            echo "__CLASS__ = ", __CLASS__, PHP_EOL;
            echo "__TRAIT__ = ", __TRAIT__, PHP_EOL;
            echo "__NAMESPACE__ = ", __NAMESPACE__, PHP_EOL;
            echo "MyClass::class = ", MyClass::class, PHP_EOL, PHP_EOL;
        }
    }

    $obj = new MyClass();
    $obj->myTraitFunction();
    $obj->myFunction();
    
    $sub = function() {
        echo "--- In anonymous function call ---", PHP_EOL;
        echo "__DIR__ = ", __DIR__, PHP_EOL;
        echo "__FILE__ = ", __FILE__, PHP_EOL;
        echo "__LINE__ = ", __LINE__, PHP_EOL;
        echo "__FUNCTION__ = ", __FUNCTION__, PHP_EOL;
        echo "__METHOD__ = ", __METHOD__, PHP_EOL;
        echo "__CLASS__ = ", __CLASS__, PHP_EOL;
        echo "__TRAIT__ = ", __TRAIT__, PHP_EOL;
        echo "__NAMESPACE__ = ", __NAMESPACE__, PHP_EOL;
        echo "MyClass::class = ", MyClass::class, PHP_EOL, PHP_EOL;
    };
    $sub();
    
    echo "--- In namespace top level ---", PHP_EOL;
    echo "__DIR__ = ", __DIR__, PHP_EOL;
    echo "__FILE__ = ", __FILE__, PHP_EOL;
    echo "__LINE__ = ", __LINE__, PHP_EOL;
    echo "__FUNCTION__ = ", __FUNCTION__, PHP_EOL;
    echo "__METHOD__ = ", __METHOD__, PHP_EOL;
    echo "__CLASS__ = ", __CLASS__, PHP_EOL;
    echo "__TRAIT__ = ", __TRAIT__, PHP_EOL;
    echo "__NAMESPACE__ = ", __NAMESPACE__, PHP_EOL;
    echo "MyClass::class = ", MyClass::class, PHP_EOL, PHP_EOL;
}
実行結果
--- In construcror ---
__DIR__ = /workspace
__FILE__ = /workspace/Main.php
__LINE__ = 25
__FUNCTION__ = __construct
__METHOD__ = MyNamespace\MyClass::__construct
__CLASS__ = MyNamespace\MyClass
__TRAIT__ = 
__NAMESPACE__ = MyNamespace
MyClass::class = MyNamespace\MyClass

--- In trait method call ---
__DIR__ = /workspace
__FILE__ = /workspace/Main.php
__LINE__ = 9
__FUNCTION__ = myTraitFunction
__METHOD__ = MyNamespace\MyTrait::myTraitFunction
__CLASS__ = MyNamespace\MyClass
__TRAIT__ = MyNamespace\MyTrait
__NAMESPACE__ = MyNamespace
MyClass::class = MyNamespace\MyClass

--- In method call ---
__DIR__ = /workspace
__FILE__ = /workspace/Main.php
__LINE__ = 38
__FUNCTION__ = myFunction
__METHOD__ = MyNamespace\MyClass::myFunction
__CLASS__ = MyNamespace\MyClass
__TRAIT__ = 
__NAMESPACE__ = MyNamespace
MyClass::class = MyNamespace\MyClass

--- In anonymous function call ---
__DIR__ = /workspace
__FILE__ = /workspace/Main.php
__LINE__ = 56
__FUNCTION__ = MyNamespace\{closure}
__METHOD__ = MyNamespace\{closure}
__CLASS__ = 
__TRAIT__ = 
__NAMESPACE__ = MyNamespace
MyClass::class = MyNamespace\MyClass

--- In namespace top level ---
__DIR__ = /workspace
__FILE__ = /workspace/Main.php
__LINE__ = 69
__FUNCTION__ = 
__METHOD__ = 
__CLASS__ = 
__TRAIT__ = 
__NAMESPACE__ = MyNamespace
MyClass::class = MyNamespace\MyClass

その他

編集

今後に使う予定のない変数に unset() 関数を適用することで、明示的にその変数を使わないことを表明できます。 また、unsetした変数に束縛されていたメモリーオブジェクトのリファレンスカウントが一つ減るので、メモリーが開放される可能性があります。

unsetの適用後、変数を参照すると、言語処理系が警告をしてくれるので、バグなどを発見しやすくなります。

ためしに下記コード例では、unsetの適用後、変数を参照しています。

<?php
header('Content-Type: text/plain');

$n = 70;
$m = 12;

echo $n, PHP_EOL;
echo $m, PHP_EOL;
unset($m);
echo $m, PHP_EOL;
echo $x, PHP_EOL;
unset($x);
実行結果
70
12
PHP Warning:  Undefined variable $m in /workspace/Main.php on line 10
PHP Warning:  Undefined variable $x in /workspace/Main.php on line 11
unset() された $m は、存在しない変数 $x と同じ挙動をします。
unset($x) しても何も起こりません。

PHPでは、ループ変数のスコープはループではなく関数スコープ(あるいはグローバルスコープ)なので、ループを抜けたあと悪さをしないように、unset() しましょう(ループから脱走したリファレンスを使って代入すると発見困難なバグになります)。

ドメインによっては、 $ループ変数 = null; で標準化していることもありますが趣旨は同じです。

null は、変数の束縛を解く時につかわれ、その意味では unset() と似ています。 NULLが入った変数は、isset()はfalseを返します。

<?php
header('Content-Type: text/plain');

$n = 70;
$m = 12;

echo 'var_export($n, true) --> ', var_export($n, true), PHP_EOL;
echo 'var_export($m, true) --> ', var_export($m, true), PHP_EOL;

$m = null;
echo '$m = null;', PHP_EOL;

echo 'var_export($m, true) --> ', var_export($m, true), PHP_EOL;

echo 'var_export(isset($m), true) --> ', var_export(isset($m), true), PHP_EOL;
echo 'var_export(isset($n), true) --> ', var_export(isset($n), true), PHP_EOL;

echo 'var_export($n ?? "no", true) --> ', var_export($n ?? "no", true), PHP_EOL;
echo 'var_export($m ?? "no", true) --> ', var_export($m ?? "no", true), PHP_EOL;
実行結果
var_export($n, true) --> 70
var_export($m, true) --> 12
$m = null;
var_export($m, true) --> NULL
var_export(isset($m), true) --> false
var_export(isset($n), true) --> true
var_export($n ?? "no", true) --> 70
var_export($m ?? "no", true) --> 'no'
var_export()は与えられた式を、PHPのリテラルの形式にします。
nullは、大文字小文字を区別しません。
??は、NULL合体演算子です。式1 ?? 式2は、isset(式1) ? 式1 : 式2と同じ意味です。v7.0から導入されました。

未定義変数

編集

2022年12月現在の最新の安定バージョン v8.1.13 では、未定義(Undefined)の変数参照すると警告( Warning )が出ます。

<?php
echo $w; // 未定義変数の参照
$d = $w;
print "hello";
?>
実行結果
PHP Warning:  Undefined variable $w in /workspace/Main.php on line 2
PHP Warning:  Undefined variable $w in /workspace/Main.php on line 3
hello
意図的に未定義変数を参照することはありえません。
未定義変数の参照が起こるときは、概ねミススペルが原因です。
未定義変数の参照に関する警告を、エラー制御演算子(@)で回避するのは間違いです。未定義となった原因を排除するか、適切な初期値を与えましょう。

エラー制御演算子(@)

編集

エラー制御演算子(@)は、名前と機能がやや乖離していて、「メッセージ抑制演算子」程度の機能しかありません。 エラー制御演算子には、トラブルシューティングに役立つエラーや警告を表示させないという害悪があるので、他の方法(例えば例外処理)を検討するべきです。

<?php
$body = file ('/noexist') or print __LINE__ . ":" . error_get_last()['message'] . PHP_EOL;
$body = @file ('/noexist') or print __LINE__ . ":" . error_get_last()['message'] . PHP_EOL;
実行結果
PHP Warning:  file(/noexist): Failed to open stream: No such file or directory in /workspace/Main.php on line 2
2:file(/noexist): Failed to open stream: No such file or directory 
3:file(/noexist): Failed to open stream: No such file or directory
  1. ^ Magic constants