0
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

【PHP】class内で宣言した定数の参照|self::の有無による違い

web系エンジニアを目指し、MENTAを利用し、PHPを学習している初学者です。
日々学習していることの忘備録、モチベ維持、ちゃんとやってることの証明!の初投稿です。

対象者

PHPを学びはじめた、初学者

内容

class内で宣言した定数を参照した時、self::の有無で値がどう変化するか

動作環境

PHP 7.1.32

基本のおさらい

class内で、共有する定数・変数を宣言する


class A {
    //定数宣言(定数名は$不要、大文字がデフォ)
    const TEISU = '絶対変わらない!';
    //変数宣言(publicの他にprivate,protectedもあるが、ここではアンタッチャブル)
    public $hensu = '代入されれば変わる';
}

宣言した、定数・変数を使う


class A {
    public function output {
        echo '定数は' . self::TEISU;   //定数は絶対変わらない!
        echo '変数は' . $this->hensu;  //変数は代入されれば変わる
        $this->hensu = '簡単に変わる';
        echo '変数は' . $this->hensu;  //変数は簡単に変わる
    }

このように、
定数はself::定数名
変数は$this->変数名
で使用できます。
「じゃあ定数使うなら、self::必須じゃん。」
と思いますよね。
ところが、定数はそのままでも使えるんです!
(プログラムは動いちゃうけど、)やっぱり原則使っちゃダメ!
と、訂正します。

これが本題!(訂正前)

ダメな例

//前提:クラス内で宣言した定数を、同じクラスのメソッドで表示
const TEISU = '絶対変わらない!';
const ARRAY_TEISU = array(
        1 => TEISU
);

//参照結果
echo self::ARRAY_TEISU[1];  //TEISU
echo self::TEISU;          //絶対変わらない!
echo TEISU;                //TEISU

つまり、
self::(配列の)定数名[キー]で、その中身
self::定数名で、その中身
定数名で、定数そのもの
が参照される!
以上!今回書きたかったのは、これです。

これが本題!(訂正後)

良い例

//前提:クラス内で宣言した定数を、同じクラスのメソッドで表示
const TEISU = '絶対変わらない!';
const ARRAY_TEISU = array(
        1 => self::TEISU
);

//参照結果
echo self::ARRAY_TEISU[1];  //絶対変わらない!
echo self::TEISU;           //絶対変わらない!

つまり、
定義する時はselfいらないが、使用する時はself::必須!

なぜ訂正前で良いと思ってしまったか

警告を表示させない環境(私の場合はvim)だった為
vimが悪い訳でなく、php.iniの設定の為です。

記事をご覧いただいた先輩よりご指摘いただきました。
ありがとうございました。

解決方法

・php.iniの設定を変更する
もしくは、
error_reporting(E_ALL);を追記する

警告を表示する環境にすると、

悪い例(一部抜粋)
const ARRAY_TEISU = array(
        1 => TEISU
);

echo TEISU;                //TEISU

この2箇所で、
Notice: Use of undefined constant TEISU(定義されてない定数があるよ)
という警告が表示され
「これじゃダメなんだ。」
と気付くことが出来ます。

「constの参照にはself::!!!!!」
と思い込み、長らくエラーに苦しみました。
「そんなバカな・・」
と思った方。これが初学者です!!
それでも信じられない方の為に、どう苦しんだのか実例を晒し、終了とします。

失敗実例は、削除するのも寂しいので、今回の知識をいかした内容に訂正しました。

失敗実例(訂正後)

「メニュー番号を入力させ、機能を呼び出す」
というような課題で、クラスにたくさんの定数を宣言しました。


class Menu {
    const MENU_MAGAZINE = '雑誌の一覧を見る';
    const MENU_COMIC = '漫画の一覧を見る';
    const MENU_NOVEL = '小説の一覧を見る';
    const END_KEY = '終了'; 
    const MENU_LIST = array(
        1 => self::MENU_MAGAZINE,
        2 => self::MENU_COMIC,
        3 => self::MENU_NOVEL,
        9 => self::END_KEY
    );

$select_menu には、厳密なバリデーションチェックの末、1〜3or9が入ります。


    public function main() {

        //成功例
        if (self::MENU_LIST[$select_menu] === self::MENU_MAGAZINE) {
            //雑誌のメソッドを実行
        }
        //失敗例
        if (self::MENU_LIST[$select_menu] === MENU_COMIC) {
            //漫画のメソッドを実行
        } 

    }

$select_menu=1の時、成功例のif文に突入!
self::MENU_LIST[$select_menu] === self::MENU_MAGAZINE
が成立し(共に「雑誌の一覧を見る」)if文がtrue判定される

対して、$select_menu=2の時、失敗例のif文に突入!
self::MENU_LIST[$select_menu] は、'漫画の一覧を見る'
MENU_COMIC は、 'MENU_COMIC'
(それ以前に、定数をself::〜として使ってないから警告対象)
となり、
if文がfalse判定される
漫画が見たくて「2」を何度押しても、エラーしか出ない悲しい事態になったとさ。
ちゃんちゃん。

初投稿してみて

なかなか踏み出せなかった、Qiitaへの投稿。
「メモ帳に書いてるだけでは勿体無い!!」
と、思い切って投稿してみました!
良かったのは、考えが整理される&間違いに気付ける(指摘していただける)ことです。
投稿前は「self::の有無」ではなく、「配列かどうか」の違いだと勘違いしてた(今となっては謎)ですが、執筆している内に覚醒しました。
2時間近く掛かってしまいましたが、それだけでも儲けものです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
0
Help us understand the problem. What are the problem?