2014年2月20日木曜日

[PHP]グローバル変数を使っているファイルをインクルードするときの対処

チームで共有しているライブラリにグローバル変数や関数が定義されていて、それを読み込んで使う状況ってあるよね。

PHPの場合だとオブジェクト指向を意識せずに書けてしまうので、クラス化されていない外部ライブラリを読み込むなんて状況もありうるわけで、その場合にはグローバル変数や関数が使われていることもあるだろう。

で、自分が書くクラスでもそのグローバル変数や関数を使うわけだが、いくつかやり方がある。ということで、今回はグローバル変数について自分の書くクラスで使う方法をいくつか挙げてみようと思う。(グローバル関数の方はまた別の機会で)

例えばグローバル変数を次のように定義したファイルがあるとしよう。

<?php
$a = 1;

このファイルをインクルードして、クラス内部で使うことを考える。


1. global 宣言をつける

<?php
include 'inc.php';

class Foo {
  public function bar() {
    global $a;
    return $a + 2;
  }
}

$f = new Foo();
echo $f->bar();
echo "\n";

bar()メソッド内部で普通に変数$aを呼んでもグローバル変数と見なされないが、global宣言をした変数についてはグローバル変数として扱うようになる。ただ、僕個人の意見としては、これは極力使わないほうがいい。ついついグローバルスコープと意識せずに$aを変更するコードを書いてしまい、バグの原因となりかねないからだ。


2. スーパーグローバル変数 $GLOBALS を使う

<?php
include 'inc.php';

class Foo {
  public function bar() {
    $a = $GLOBALS['a'];
    return $a + 2;
  }
}

$f = new Foo();
echo $f->bar();
echo "\n";

こちらは、グローバル変数 $GLOBALS を使うパターン。$GLOBALSはグローバル変数名をキーとし、グローバル変数を要素として持つ配列である。グローバル変数であることを明示している分、1.よりはマシと思う。いちいち$GLOBALS['a']と書くには面倒だし一旦ローカル変数に代入している。phpでは配列の代入はコピーとなるため、グローバル変数を予期せず変更してしまうリスクも回避できる。
しかし、このコードも、クラスの内部にグローバル変数を直接参照するコードが入ってしまう箇所があるのはよろしくない。なぜなら実際にはクラス定義の部分だけ別ファイルにしてインクルードすることが多いし、そうするとグローバル変数が原因のバグが発生した時のデバッグが大変だ。その点については1.と変わらない。


3. コンストラクタの引数として受け取り、インスタンス変数に格納する

<?php
include 'inc.php';

class Foo {
  protected $a;

  public function __construct($a) {
    $this->a = $a;
  }

  public function bar() {
    return $this->a + 2;
  }
}

$f = new Foo($a);
echo $f->bar();
echo "\n";


ということで、コンストラクタの引数でグローバル変数を受けとるパターン。これでクラス内部にグローバル変数を直接参照するコードはなくなった。インスタンス変数に代入しているので、クラス内のメソッドからは自由に使える。僕はこのパターンを使っている。やはりクラスの定義を書いている時はそのクラス内部で色々完結させたい訳で、グローバル変数は外へ出すのが一番いいと思う。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

0 件のコメント:

コメントを投稿