CodeIgniterの学習 53 - HMVCモジュールを改造してコントローラ単位(モジュール単位)での使用・不使用制御をconfigファイルで行えるようにする

久しぶりのエントリー。やりたいことは表題の通り。

CodeIgniterのモジュール単位(コントローラ単位)での使用可能・不可能の制御を組み込んで、
環境に応じて使用できるモジュールを変更できるようにする。

用途としては、

  • リリース後に一時的に一部機能のみ停止したい。
  • ソース構成は同じだが複数の環境にソースを設置し(フロントエンド用・バックエンド用等)、使用できるモジュールを分けたい。(ただし、フロント・バックエンド用でソース構成を分けた方が安全と言えば安全だが)

といった用途。


なお、ユーザー権限毎や、グループ権限毎の制御は組み入れていない。
もう一つ上位レベルでの使用制御。



尚前提として、
http://d.hatena.ne.jp/dix3/20081128/1227802844
http://d.hatena.ne.jp/dix3/20081129/1227964872
http://d.hatena.ne.jp/dix3/20081201/1228113261
http://d.hatena.ne.jp/dix3/20081203/1228237872
が済んでいること。


ソース

改造ソースを貼っておく。いつもながら無保証。

HMVCを使ってかつ拡張しようという人は、あまり居ないと思うので、画面は貼らない。
興味のある人だけ試してね。


ソース1新設:application/config/route_active.php

各自の環境によって異なるだろう。
使い方は見れば分かると思うが、apache.htaccessみたいな書き方だと思ってください。
正規表現は使えない、あとtasklist/editのみallowとかいう制御は出来ない。

allならば全て許可、または拒否
default_routeは、拒否された時の呼びだし先。指定無しの時はshow_404。

<?php  
if ( ! defined('BASEPATH')) exit('No direct script access allowed');

//ルートの使用、不使用制御
$active_group = "default";
//$active_group = "develop";

$route_active['default']['order_by'] = array("deny","allow");
$route_active['default']['deny_from'] = array("all");
$route_active['default']['allow_from'] = array("tasklist","flickr","auth");
$route_active['default']['default_route'] = array("flickr");

$route_active['develop']['order_by'] = array("deny","allow");
$route_active['develop']['deny_from'] = array("all");
$route_active['develop']['allow_from'] = array("all");
$route_active['develop']['default_route'] = array();



/* End of file route_active.php */
/* Location: ./application/config/route_active.php */
 ?>

こんな感じで、任意のactive_groupを切り替えて使えるようにしてみた。



ソース2:application/libraries/MY_Router.php

HMVCソースを拡張

<?php (defined('BASEPATH')) OR exit('No direct script access allowed');

/* define the modules base path */
define('MODBASE', APPPATH.'modules/');

/* define the offset from application/controllers */
define('MODOFFSET', '../modules/');

/**
 * Modular Extensions - PHP5
 *
 * Adapted from the CodeIgniter Core Classes
 * @copyright Copyright (c) 2006, EllisLab, Inc.
 * @link    http://codeigniter.com
 *
 * Description:
 * This library extends the CodeIgniter router class.
 *
 * Install this file as application/libraries/MY_Router.php
 *
 * @copyright   Copyright (c) Wiredesignz 2008-11-20
 * @version   5.1.40
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 **/

// @modify: dix3
// @purpose: モジュール単位(コントローラ単位)での使用可能・不可能制御の組み込み
// @link:http://d.hatena.ne.jp/dix3/20081209/1228816805

class MY_Router extends CI_Router
{
  var $default_route = array();
  var $redirect_count = 0;
  public function _validate_request($segments)
  {
    if(isset($segments[0]) && preg_match('#^mdl_#iu',$segments[0])){
      // /mdl_ から始まるURLはルーティングの対象外とするようにした(URLで直接呼べないように)
      show_404($this->fetch_directory().$segments[0]);
    }elseif(isset($segments[0]) && !$this->_route_active_check($segments[0])){
      //モジュール単位(コントローラ単位)での使用可能・不可能制御
      if($this->default_route && 0===$this->redirect_count){
        $this->redirect_count++;
        return $this->_validate_request($this->default_route);
      }else{
        show_404($this->fetch_directory().$segments[0]);
      }
    }else{
    //end
      (isset($segments[1])) OR $segments[1] = NULL;

      /* locate the module controller */
      list($module, $controller) = Router::locate($segments);

      /* no controller found */
      ($module === FALSE) AND show_404($controller);

      /* set the module directory */
      Router::$path = ($controller) ? $module : NULL ;

      /* set the module path */
      $path = ($controller) ? MODOFFSET.$module.'/controllers/' : NULL;

      $this->set_directory($path);

      /* remove the directory segment */
      if ($controller != $module AND $controller != NULL)
        $segments = array_slice($segments, 1);

      return $segments;
    // /mdl_ から始まるURLはルーティングの対象外とするようにした(URLで直接呼べないように)
    }
    //end
  }
  //呼び出したコントローラを使うかどうかのチェック
  function _route_active_check($controller_name)
  {
    $ret = false;
    if(defined('CRON') && CRON ){//バッチ起動時は常にtrue
      return true;
    }
    if(@include(APPPATH.'config/route_active'.EXT)){
      $route_active = ( ! isset($route_active) OR ! is_array($route_active)) ? array() : $route_active;
      $active_group = isset($active_group) ? $active_group : "";
      if($active_group){
        $order_by = isset($route_active[$active_group]['order_by']) ? $route_active[$active_group]['order_by'] : "deny";
        $allow_from = isset($route_active[$active_group]['allow_from']) ? $route_active[$active_group]['allow_from'] : "";
        $deny_from = isset($route_active[$active_group]['deny_from']) ? $route_active[$active_group]['deny_from'] : "";
        $this->default_route = isset($route_active[$active_group]['default_route']) ? $route_active[$active_group]['default_route'] : "";
        foreach((array) $order_by as $v){
          foreach((array) ${"{$v}_from"} as $v2){
            if("all" === strtolower($v2) || $controller_name === strtolower($v2)){
              if($v === "allow"){
                $ret = true;
              }
              if($v === "deny"){
                $ret = false;
              }
            }
          }
        }
      }
    }
    return $ret;
  }
}

class Router
{
  public static $path;

  /** Locate the controller **/
  public static function locate($segments)
  {
    list($module, $controller) = $segments;

    /* a module? */
    if ($module AND is_dir(MODBASE.$module))
    {
      ($controller == NULL) AND $controller = $module;

      /* a module sub-controller? */
      if(is_file(MODBASE.$module.'/controllers/'.$controller.EXT))
        return array($module, $controller);

      /* a module controller? */
      return array($module, $module);
    }

    /* an application controller? */
    if (is_file(APPPATH.'controllers/'.$module.EXT))
      return array($module, NULL);

    /* no controller found */
    return array(FALSE, NULL);
  }
}
/* End of file MY_Router.php */
/* Location: ./application/libraries/MY_Router.php */
 ?>

ソースはindex.phpと、configファイル以外同一にして複数環境を設置する一つの方法。
フレームワーク標準で、drupal等のCMSみたいなモジュール管理機構があると本当はいいんだけどね。



今日はここまで。