[PHP]撲滅! require、require_once

PHP では使うクラスを予め宣言する、というルールがあります。
これを忘れていると、実行時にエラーが表示されます。

Fatal error: Uncaught Error: Class 'XXXXXX' not found in /Sample/Test.php:26 Stack trace: #0 {main} thrown in /Sample/Test.php on line 26

C# などを経験してると、これは非常にストレスです。(C# では宣言をする必要性も少なく、右クリックで using を自動的に挟めるなど便利な環境が揃っているため)
また、このルールに慣れ親しんだ人でも、

  • 大規模な開発案件で、1ソースに数十の require が並ぶ
  • テストログ吐くためだけに require しないといけない
  • 後で使わなくなったクラスの require がどれだかわからない -> 知らない間に実行コスト増加

このように色々としんどい要素が多い。このしんどさを解消しましょう。

ソースコード

autoLoader.php

<?php

class autoLoader
{
  protected $dirs;

  function __construct($dir = null)
  {
    if ($dir != null)
    {
      $this->dirs[] = $dir;
    }
  }

  public function Register()
  {
      spl_autoload_register(array($this, 'autoLoad'));
  }

  public function AddDirectory($dir)
  {
      $this->dirs[] = $dir;
  }

  private function autoLoad($className)
  {
    foreach ($this->dirs as $dir)
    {
      $file = $dir . '/' . $className . '.php';
      if (is_readable($file))
      {
        require $file;
        return;
      }
    }
  }
}

?>

テスト

testClass.php

<?php

class testClass
{
  public static void exec()
  {
    echo 'exec class</br>';
  }
}

?>

sample.php

トップコードに autoLoader を入れておくと、以後呼び出されるクラスファイルは全て require(_once) が必要なくなります。

<?php

require("autoLoader.php");

// sample.php と同じパスに、全ての php があるとする
$autoLoader = new autoLoader(dirname(__FILE__));
// 他にファイルを探すディレクトリがある場合、AddDirectory で追加する
// $autoLoader->AddDirectory("....");
$autoLoader->Register();

// require がなくても動く
testClass::exec();

?>

解説

spl_autoload_register によって「インスタンス生成時、require されていないクラスであれば呼ばれる関数」を定義します(autoLoad)。
autoLoad では、読み込まれていないファイルパスを自動生成し、require します。
(構造的に、検索するディレクトリが多いほど実行時間がかかります)

注意点

ファイル名=クラス名にしておく必要があります。ゆえにこういうのはダメです。

ファイル名:test_sample.cs、クラス名:TestSample

メソッド autoLoad を改造すればこういうパターンも有りにすることはできますが、実行コストが上がってしまうのでちょっと…。

実行速度

さすがに require を直接書くより単純速度は遅くなります。(サーバーのファイル検索速度に依存するでしょう)

とはいえ、無駄なファイルを require せずに済むロス回避の面もありますし、例えばこんな感じで動的にクラスを呼んでる場合、require を1つで済ますことができ、かえって速度上昇するケースもあるかもしれません。

// テーブルは50テーブルくらいある。require であれば 50 記述する必要がある
call_user_func($tablename. '::GetRecordCount', $search_id)

総じて、余程 require が絶大なパフォーマンスを誇るケースでなければ、こちらの方がいいんじゃないかと思います。

参考サイト

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA