PHPオブジェクト指向実践

インスタンス生成自動化

クラス読み込みクラス

ここまでの仕組みでは、ページごとに対応するクラスを作りました。
index.php用にはIndexProcessクラス、bbs.php用にはBbsProcessクラス、という具合です。
そして例えばindex.phpの頭でIndexProcessのクラスインスタンスの生成、bbs.phpの頭ではBbsProcessのクラスインスタンスの生成を行います。その後のexecメソッドの実行→getDataメソッドの実行という流れは同じのため、ページごとの違いは何のクラスのインスタンスを生成するか、という事だけになります。

折角だから、ここも完全固定のコードとしてしまいたいところです。

それを実現するには、ある規則に基づく必要があります。
例えばindex.phpであれば必ずIndexProcess、bbs.phpであれば必ずBbsProcess。
メインスクリプトのファイル名とクラス名を完全対応させるのです。
幸い、今のところその規則に則っています。

そして命名規則が守られた前提で、メインスクリプトとそれに対応するクラスをつなげる役目をするクラスが必要です。

Loader.php
<?php

require_once ROOT . '/system/SiteProcess.php';

class Loader
{
    public static function getInstance()
    {
        try {
        
            // リクエストされたURLのファイル名部分のみを取得
            $pathArray = explode('/', $_SERVER['SCRIPT_NAME']);
            $lastIndex = count($pathArray) - 1;
            $pageName = str_replace('.php', '', $pathArray[$lastIndex]);
            
            // クラスファイルが存在するか確認
            $className = $pageName . 'Process';
            $classPath = sprintf('%s/system/%s/%s.php', ROOT, $pageName, $className);
            if (false == file_exists($classPath)) {
                throw new Exception('クラスファイルが存在しません');
            }
            
            // クラスファイル読み込み
            require_once $classPath;
            
            // クラスの定義確認
            if (false == class_exists($className)) {
                throw new Exception('クラスが定義されていません');
            }

            $instance = new $className();
            return $instance;
            
        } catch (Exception $e) {
            die($e->getMessage());
        }        
    }
}

?>

Loaderと称して、クラス読み込み処理を行うクラスを作成しました。

やっていることは、
まずリクエストされたページのファイル名から拡張子を除いたものの末尾に「Process」をくっつけたものをクラス名として、その名前のクラスファイルが存在するかを確認し、存在すればインクルードします。

そして今度は同じ名前でクラスが定義されているかを確認し、存在すればそのクラスのインスタンスを生成して返す、というものです。

つまり、自動的にメインスクリプトのファイル名をもとに対応するクラスを探し、自動的にインスタンスを生成して返してくれるというわけです。なので、メインスクリプトファイル名とクラスファイル名とクラス名が完全に名前で対応している場合のみ可能な処理になります。

逆に言うと、名前規則さえ正しければ、メインスクリプトに記述する処理はページにかかわらず、完全に固定コードになるし、基本的にクラス設計に集中できることになります。

メインスクリプト

Loaderクラスを使用した場合のメインスクリプトはこんな感じになります。

<?php
define('ROOT', '・・・');
require_once ROOT . '/system/Loader.php';
$instance = @CLoader@::getInstance();
$instance->exec();
$data = $instance->getData();
?>

<html>
 ・
 ・
 ・

この方法ならページごとに一切記述を変える必要もなく、そのままコピーです。
なんなら、このたった5行のスクリプトも別ファイルにしてしまい、これをrequireするだけにしてしまってもいいです。

初期設定スクリプト

ところで最初のdefineでは、システムのルートディレクトリを定義していますが、この定数はシステム内で共通のため、ページごとに定義しているのはあまり好ましいことではありません。仮にルートディレクトリを変更するような事があれば全てのページのスクリプトに修正が必要になります。

なので、専用のスクリプトを一つ用意し、これをインクルードする形にします。
ついでにLoaderのインクルードもどんなページでも必ず必要なので、このスクリプトに含めてしまいます。

initialize.php
<?php

define('ROOT', '・・・');
define('DATA_ROOT', ROOT . '・・・');

require_once  './system/Loader.php';

?>

このように初期処理的なものは全てここに記述するようにします。

するとメインスクリプトは更にスッキリします。

index.php
<?php
require_once  './system/initialize.php';
$instance = @CLoader@::getInstance();
$instance->exec();
$data = $instance->getData();
?>

ディレクトリ・ファイル構成

オブジェクト指向では基本的にクラスごとにファイルを作成するため、ファイル数が多くなります。

そのため、ディレクトリ階層やファイルの配置を整然と構成して整理する事が重要です。

以下に今回のシステムの例を示しますので参考にしてみてください。

public_html/
  ∟system/
    ∟index/
      ∟IndexProcess.php
    ∟bbs/
      ∟BbsProcess.php
      ∟BbsCols.php
      ∟BbsLogic.php
      ∟BbsDac.php

    ∟SiteProcess.php
    ∟Counter.php
    ∟Loader.php
    ∟initialize.php

  ∟data/
    ∟bbs.dat
    ∟counter.dat

  ∟index.php
  ∟bbs.php

まずURLとして直接アクセスするindex.phpやbbs.phpなどのメインスクリプトがルート直下にあります。

クラスなどのメインスクリプトからインクルードして使用するようなスクリプトは全てsystemディレクトリの下に配置しています。そしてその中でも各スクリプトから共通で使用されるようなものはsystemディレクトリ直下、各メインスクリプトと関連付けられたページ処理クラスはメインスクリプトファイルと同名のディレクトリを作成してその下にまとめています。

データ保存ファイルは専用のディレクトリを一つつくり、そこに全てまとめます。

これは一つの例ですが、このようにある程度整理しておくとよいと思います。