趣味は車輪の再発明♪

俺様軽量フレームワーク



発想から数ヶ月に渡ってちょこちょこ更新していた、超個人的PHPフレームワーク「俺様軽量フレームワーク|たかみんつ」が、ある程度完成っていうか、「これくらいでよいかもね」って状態に落ち着いたので、ここに根幹部分のソースを公開しておきます。

説明不足ですけど、徐々に加筆するつもり。簡単に言っちゃえば細かいことを抜きにしたSymfony1.4みたいな感じです。


{$app}/index.php

Webアプリケーションの本体。フレームワークのPageクラスをインクルードするだけなので、特に編集する必要はない。

<?php
date_default_timezone_set('Asia/Tokyo');
define('DBCONN_URL',            "127.0.0.1");   //mysqlデータベースのホスト名またはIP
define('DBCONN_USERNAME',       "database username"); //データベースに接続するユーザー名
define('DBCONN_PASSWORD',       "database password"); //データベースへの接続パスワード
define('DBCONN_DATABASE',       "database name");  //接続先データベース名
require_once '<俺様ルートディレクトリ>/sys/Page.class.php'
?>

{$app}/templates/layout.php

ウェブアプリケーションの見た目を決定するレイアウトファイルのサンプル。

<!DOCTYPE html PUBLIC
  "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
  <head>
    <!-- HEAD要素内はなるべくこのまま-->
    <?
    put_content_type();
    include_metas();
    put_title();
    ?>
    <?
    //スタイルシートを指定するならここ
    use_stylesheet('../../../css/common.css');
    //javascriptを取り込むのもここ
    use_javascript('../../../js/jquery-1.7.1.min.js');
    use_javascript('../../../js/allonload.js');
    ?>
    <?
    include_stylesheets();
    include_javascripts();
    ?>
  </head>
  <body>
    <div id="docHead">
      <div>
        <h1><?=get_title()?></h1>
      </div>
    </div>
    <div id="docBody">
      <div>
        <!-- ここにコンテンツが入ります -->
        <?=$contents?>
      </div>
    </div>
    <div id="docFoot">
      <div>
        「<?=get_title()?>」<br/>
        php version <?=phpversion()?>
        | mySQL version <?
          $r = Database::currentConnection()->fetchAll("select version() as ve");
          echo $r[0]['ve'];
        ?>
        | jQuery version <span onload="$(this).text($().jquery);"></span>
      </div>
    </div>
  </body>
</html>

{$app}/modules/home/templates/index.php

homeモジュールのindexアクションのテンプレートファイル。

<?php
set_title('サンプル');
?>
<p>{$app}/modules/home/templates/index.php</p>

実際に見るには⇒ http://bicycle.life.coocan.jp/{$app}/index.php/home/index

{$oresama_home}/sys/Page.class.php

これこそ「ご本尊」というべきクラス。これ以外はオプションといってもいいくらいな感じですな。

<?php
$oresama_home = '<俺様ルートディレクトリ>';
require_once("{$oresama_home}/sys/FileManage.class.php");
require_once("{$oresama_home}/sys/Database.class.php");
require_once("{$oresama_home}/sys/mysql.class.php");
require_once("{$oresama_home}/sys/logger.php");
class Page {
  static private $log = array();
  static public $content_type = "text/html; charset=utf-8";
  static public $metas = array();
  static public $page_title = "";
  static public $site_name = "";
  static public $javascripts = array();
  static public $javascripts_included = false;
  static public $included_javascript = array();
  static public $stylesheets = array();
  static public $stylesheets_included = false;
  static public $included_stylesheet = array();
  static public $no_template = false;
  static public $layout = "templates/layout.php";
  static public $layout_mobile = "templates/mobile.php";
  static public $base_dir = "";
  static public $request_uri = '';
  static public $default_module = 'home';
  static public $default_action = 'index';
  static public $module = 'home';   //モジュール名
  static public $action = 'index';  //アクション名
  static public $url_param = ""; //アクション名以降のURL(パラメータ部は含まない)
  static public $proc_layout = false;//レイアウト処理中フラグ
  static public $stylesheet_inspos = 0;//レイアウト処理中のスタイルシート挿入位置
  static public $javascript_inspos = 0;//レイアウト処理中のjavascript挿入位置
  //モバイルと判定するユーザーエージェント
  static public $mobile_useragents = array(
    'iPhone',   // Apple iPhone
    'iPod',    // Apple iPod touch
    'Android',   // 1.5+ Android
    'dream',   // Pre 1.5 Android
    'CUPCAKE',   // 1.5+ Android
    'blackberry9500', // Storm
    'blackberry9530', // Storm
    'blackberry9520', // Storm v2
    'blackberry9550', // Storm v2
    'blackberry9800', // Torch
    'webOS',   // Palm Pre Experimental
    'incognito',  // Other iPhone browser
    'webmate'   // Other iPhone browser
  );
  //ユーザーエージェント
  static public $user_agent = null;
  static public $is_mobile = null;
  static public function log($s) {
    Page::$log[] = date('c') . ' ' . $s;
  }
  /////////////////////////////////////////////////////////////////////////
  //サイトのメイン処理。
  //クライアントからのリクエストを処理して応答を返す。
  /////////////////////////////////////////////////////////////////////////
  static public function main() {
    global $oresama_home;
    try {
      //セッションスタート
      session_start();
      //出力バッファリング開始
      ob_start();
      //データベースとの接続
      Database::$instance = new Database();
      //オートロードを処理
      Page::includeAll("{$oresama_home}/lib");
      //リクエストURIの解析
      Page::analyze_uri();
      //ユーザーエージェントの保存
      Page::$user_agent = $_SERVER['HTTP_USER_AGENT'];
      //ログ
      debug(Page::$user_agent,$_SERVER['PHP_SELF'],0);
      //---------------------------------------------------------------
      //レイアウトファイルの決定
      //---------------------------------------------------------------
      Page::$layout = '';
      //モジュール単位のレイアウト対応
      $layout_path = 'modules/'.Page::$module.'/layout';
      if(is_dir($layout_path)) {
        $layout_pathname = $layout_path . '/layout.php';
        if(is_file($layout_pathname)) {
          Page::$layout = $layout_pathname;
        }
        $layout_pathname = $layout_path . '/mobile.php';
        if(is_mobile() && is_file($layout_pathname)) {
          Page::$layout = $layout_pathname;
        }
      }
      //アプリケーションのレイアウト
      if(Page::$layout == '') {
        $layout_path = 'templates';
        if(is_dir($layout_path)) {
          $layout_pathname = $layout_path . '/layout.php';
          if(is_file($layout_pathname)) {
            Page::$layout = $layout_pathname;
          }
          //2013-02-09(Sat) ローカルでのデフォルトレイアウト変更開始
          if(is_local()) {
            $layout_pathname = $layout_path . '/layout_new.php';
            if(is_file($layout_pathname)) {
              Page::$layout = $layout_pathname;
            }
          }
          $layout_pathname = $layout_path . '/mobile.php';
          if(is_mobile() && is_file($layout_pathname)) {
            Page::$layout = $layout_pathname;
          }
        }
      }
      //オートロードを処理
      Page::includeAll('lib');
      Page::includeAll('modules/lib');
      Page::includeAll('modules/'.Page::$module.'/lib');
      //マジッククオートをどうにかする。
      if(get_magic_quotes_gpc()) {
        foreach($_REQUEST as $key => $value) {
          $_REQUEST[$key] = stripslashes($_REQUEST[$key]);
        }
        foreach($_GET as $key => $value) {
          $_GET[$key] = stripslashes($_GET[$key]);
        }
        foreach($_POST as $key => $value) {
          $_POST[$key] = stripslashes($_POST[$key]);
        }
        foreach($_COOKIE as $key => $value) {
          $_COOKIE[$key] = stripslashes($_COOKIE[$key]);
        }
      }
      //アクション実行
      list($template,$actions) = Page::doAction();
      if(!is_null($actions)) {
        //アクションクラスのメンバ変数を再定義。
        //アクションクラスのパブリックなメンバ変数をテンプレートで直接
        //参照するため。
        $mbrvars = get_object_vars($actions);
        foreach($mbrvars as $name => $value) {
          ${$name} = $value;
        }
      }
      //リダイレクトされないならテンプレートを実行してコンテンツを得る。
      if(!Page::$no_template) {
        if(is_file($template)) {
          require ($template);
        } else {
          if(Page::$module == 'home' && Page::$action == 'index') {
            header('HTTP/1.1 500 Internal Server Error');
            set_title('500 Internal Server Error|たかみんつ');
            echo "<p>サーバー内部エラーです。" .
                "リクエストされたページ(".Page::$request_uri.")の" .
                "処理が正しく終了しませんでした。</p>" .
                "<p>存在しないテンプレートが指定されています: $template </p>" .
                "<br clear='all'>";
          } else {
            redirect_to(ActionInfo::fromModuleAction('home','index')->getUrl());
          }
        }
      }
      //ここまでにバッファされた出力を変数へ取得
      $contents = ob_get_contents();
      //出力バッファリング終了
      ob_end_clean();
      //レイアウトを使ってページを出力
      if(Page::$layout != "") {
        Page::$proc_layout = true;
        include (Page::$layout);
      } else {
        //レイアウト無し
        echo $contents;
      }
    } catch (Exception $e) {
      ?><h1>EXCEPTION</h1>
      <?php
      while(!is_null($e)) {
        $msg = $e->getMessage();
        $code = $e->getCode();
        $file = $e->getFile();
        $line = $e->getLine();
        $trace = $e->getTrace();
        ?>
        <p>MESSAGE: <?=$msg?><br/>
        <p>CODE: <?=$code?>,
        FILE: <?=$file?>,
        LINE: <?=$line?>)<br/>
        <p>STACK TRACE:
        <?php
        foreach($trace as $tracesline) {
        ?>
          <br/>
          function : <?=$tracesline['function']?>
          , file : <?=$tracesline['file']?>
          (<?=$tracesline['line']?>)
        <?php
        }
        ?>
        </p>
        <hr/>
        <?php
        $e = $e->getPrevious();
      }
    }
  }
  /**
   * ディレクトリ内のすべてのPHPファイルをincludeする。
   * 指定されたディレクトリが存在しないなら何もしない。
   */
  static private function includeAll($path) {
    if(file_exists($path) && is_dir($path)) {
      $libs = FileManage::get_files($path, '/\\.php$/');
      foreach($libs as $lib) {
        require_once($lib);
      }
    }
  }
  /**
   * リクエストURIを解析。
   * モジュール、アクションなどを識別する
   */
  static public function analyze_uri() {
    //URLからモジュールとアクションを識別
    $module_action = $_SERVER['PHP_SELF'];
    $module_action = preg_replace(
      '(^.*?/index\.php)', '',
      $module_action);
    Page::$module = Page::$default_module;
    Page::$action = Page::$default_action;
    $ma = explode('/', $module_action);
    while(count($ma) < 3) { $ma[] = ''; }
    if($ma[1] != '') {Page::$module = $ma[1];}
    if($ma[2] != '') {
      Page::$action = $ma[2];
      Page::$action = preg_replace('/\\./','_dot_', Page::$action);
    }
    for($i = 3; $i < count($ma); $i++) {
      if($i > 3) {Page::$url_param .= '/';}
      Page::$url_param .= $ma[$i];
    }
    //モジュールディレクトリをURLの基点とするため、
    //html/headで指定するbaseディレクトリを決定する
    //動作しているスクリプトのURIを取得。REQUEST_URLから、index.php以降を削除する。
    //※ REQUEST_URLはIISでは動作しないらしいので、IISで動作させるには対処が必要。
    $reqestUri = preg_replace(
      '/^(.*?\/index.php).*/', '$1',
      $_SERVER['REQUEST_URI']);
    //※ IE9では絶対パスでないと、外部ファイルの参照が正しく行えない。
    Page::$base_dir = 'http://'.$_SERVER["SERVER_NAME"];
    //ポート番号付加
    //※ 生成されるリンク先URLには反映されない(Firefox,IE9)。
    Page::$base_dir .= ':'.$_SERVER['SERVER_PORT'];
    //モジュール名付加(最後のスラッシュも必要。IE9で動作が変わる)
    Page::$base_dir .= $reqestUri.'/'.Page::$module.'/';
    //---------------------------------------------------------------------------
    //リクエストURIを再構成
    //---------------------------------------------------------------------------
    $server_port = "";
    if($_SERVER['SERVER_PORT'] != 80) {
      $server_port = ':'.$_SERVER['SERVER_PORT'];
    }
    Page::$request_uri = "http://{$_SERVER['SERVER_NAME']}{$server_port}{$_SERVER['REQUEST_URI']}";
  }
  /**
   * アクションを実行する。
   * @return multitype:NULL |multitype:unknown Ambigous <string, unknown> |multitype:NULL unknown
   */
  private static function doAction() {
    list(
      $action_class_path_name,
      $action_class,
      $method_name) = Page::getAction();
    if(!is_dir("modules/".Page::$module)) {
      if(Page::$module == 'home' && Page::$action == 'index') {
        header("HTTP/1.1 404 Not Found");
        set_title('404 Not Found|たかみんつ');
        echo "<p>リクエストされたページ(".Page::$request_uri.")が" .
            "見つかりません。<br/>" .
            "(".Page::$module.")</p>" .
            "<br clear='all'>";
        Page::$no_template = true;
      } else {
        redirect_to(ActionInfo::fromModuleAction('home','index')->getUrl());
      }
      return array(null, null);
    } else {
      //既定のテンプレートファイル名を決定
      $defaultTemplatePathname = "modules/";
      $defaultTemplatePathname .= Page::$module;
      $defaultTemplatePathname .= "/templates/";
      $defaultTemplatePathname .= Page::$action.".php";
      $template = $defaultTemplatePathname;
      //アクションクラスを生成して実行しテンプレートファイル名を決定する。
      if(!is_null($action_class_path_name)) {
        //アクションクラスの取り込みと生成
        require($action_class_path_name);
        $actions = new $action_class();
        //アクションを呼び出してテンプレートファイル名を得る
        if(method_exists($actions, $method_name)) {
          $t = $actions->{$method_name}($_REQUEST);
          if(!Page::$no_template) {
            if(!is_null($t)) {
              $template = $t;
            }
          }
          return array($template, $actions);
        } else if(method_exists($actions, "execute")) {
          $t = $actions->{"execute"}($_REQUEST);
          if(!Page::$no_template) {
            if(!is_null($t)) {
              $template = $t;
            }
          }
          return array($template, $actions);
        }
      }
      return array($template, null);
    }
  }
  /**
   * アクションクラスのファイル名、クラス名、呼び出しメソッド名を決定。
   * @return multitype:string Ambigous <NULL, string>
   */
  private static function getAction() {
    $action_class = "";
    $method_name = "";
    $action_class_path_name = null;
    //個別のアクションクラスのファイル名
    $actionClassPathname = "modules/";
    $actionClassPathname .= Page::$module;
    $actionClassPathname .= "/actions/";
    $actionClassPathname .= Page::$action;
    $actionClassPathname .= "_action.class.php";
    //まとめられたアクションクラスのファイル名
    $actionsClassPathname = "modules/";
    $actionsClassPathname .= Page::$module;
    $actionsClassPathname .= "/actions/actions.class.php";
    //アクションファイルを特定する。
    //分割されたアクションクラスのほうが優先。
    if(is_file($actionClassPathname)) {
      //個別アクションファイルを採用
      $action_class_path_name = $actionClassPathname;
      $action_class = Page::$action."_action";
      $method_name = 'execute';
    } else if(is_file($actionsClassPathname)) {
      //まとめられたアクションファイルを採用
      $action_class_path_name = $actionsClassPathname;
      $action_class = Page::$module.'_actions';
      $method_name = 'execute_'.Page::$action;
    }
        // URLに使用できない文字をアンダースコアに変換する
        $action_class = preg_replace('/\-/', '_', $action_class);
    return array(
      $action_class_path_name,
      $action_class,
      $method_name);
  }
  public static function getUserAgent($uaname = null) {
    $ua = null;
    if(is_null($uaname)) {
      $uaname = $_SERVER['HTTP_USER_AGENT'];
    }
    $uaname = preg_replace('/\sv\d+\.\d+$/', '', $uaname);
    $db = Database::currentConnection();
    $qUa = mysql::q($uaname);
    $qUaLike = mysql::q($uaname . '%');
    $sql = "SELECT id,name,robot FROM m_user_agent WHERE name like $qUaLike";
    $uaResult = $db->fetchAll($sql);
    if(!is_null($uaResult) && count($uaResult) > 0) {
      $ua = new stdClass;
      $ua->id = $uaResult[0]['id'];
      $ua->name = preg_replace('/\sv\d+\.\d+$/', '', $uaResult[0]['name']);
      $ua->robot = $uaResult[0]['robot'];
    } else {
      $robot = 0;
            if(preg_match('/bot/i', $uaname) ||
                preg_match('(^Sleipnir/.+CFNetwork/.+Darwin/.+)', $uaname))
            {
        $robot = 1;
      }
      $db->execute("INSERT INTO m_user_agent (name,robot,description) VALUES ($qUa,$robot,'')");
      $uaResult = $db->fetchAll($sql);
      if(!is_null($uaResult) && count($uaResult) > 0) {
        $ua = new stdClass;
        $ua->id = $uaResult[0]['id'];
        $ua->name = preg_replace('/\sv\d+\.\d+$/', '', $uaResult[0]['name']);
        $ua->robot = $uaResult[0]['robot'];
      }
    }
    return $ua;
  }
  public static function set_cache($expire_seconds, $last_modified_time) {
   $current_time = time();
   $expire_time = $current_time + $expire_seconds;
   $expire_time_string = gmdate("D, d M Y H:i:s", $expire_time);
   $last_modified_time_string = gmdate("D, d M Y H:i:s", $last_modified_time);
   header("Pragma:");
   header("Cache-Control:");
   header("Expires: " . $expire_time_string . " GMT");
   header("Last-Modified: " . $last_modified_time_string . " GMT");
  }
}
/////////////////////////////////////////////////////////////////////////////
//メイン処理をここで実行
/////////////////////////////////////////////////////////////////////////////
Page::main();
?>

{$oresama_home}/lib/page_helper.php

テンプレートやレイアウトから使うヘルパー関数を定義。Pageクラスと密接に関連。

<?php
/////////////////////////////////////////////////////////////////////////////
// 以下、基本的なヘルパー関数などを定義
// 通常、テンプレートやレイアウトから使用する。
/////////////////////////////////////////////////////////////////////////////
/**
 * 自宅のローカルネットワーク内で動作しているかどうか。
 * $_SERVER["SERVER_NAME"]をテストして判定。
 * @return boolean
 */
function is_local() {
  return ($_SERVER["SERVER_NAME"] == '127.0.0.1' || $_SERVER["SERVER_NAME"] == 'bm1209' ||
    preg_match('/^192\.168\.11\.\d+$/', $_SERVER["SERVER_NAME"]));
}
/**
 * METAタグの出力
 * (コンテントタイプはset_content_typeを使用する)
 */
function use_meta($name, $content) {
  Page::$metas[] = array('name' => $name, 'content' => $content);
}
/**
 * METAタグでのコンテントタイプを指定する。
 */
function set_content_type($value) {
  Page::$content_type = $value;
}
/**
 * ページのタイトルを指定する。
 */
function set_title($value) {
  Page::$page_title = $value;
}
/**
 * ページのタイトルを取得する。
 */
function get_title() {
  return Page::$page_title;
}
/**
 * 出力ページのスタイルシートを指定する。
 */
function use_stylesheet($url) {
  if(!Page::$proc_layout) {
    Page::$stylesheets[] = $url;
  } else if(Page::$stylesheets_included) {
    include_stylesheet($url);
  } else {
    array_splice(Page::$stylesheets, Page::$stylesheet_inspos, 0, $url);
    Page::$stylesheet_inspos++;
  }
}
/**
 * 出力ページでjavascriptを使えるようにする。
 */
function use_javascript($url) {
  if(!Page::$proc_layout) {
    Page::$javascripts[] = $url;
  } else if (Page::$javascripts_included) {
    include_javascript($url);
  } else {
    array_splice(Page::$javascripts, Page::$javascript_inspos, 0, $url);
    Page::$javascript_inspos++;
  }
}
function is_mobile() {
  if(is_null(Page::$is_mobile)) {
    $pattern = '/'.implode('|', Page::$mobile_useragents).'/i';
    Page::$is_mobile = preg_match($pattern, Page::$user_agent);
  }
  return Page::$is_mobile;
}
/**
 * レイアウトを使用しないことを宣言する。
 */
function no_layout() {
  Page::$layout = "";
}
/**
 * テンプレートを使用しないことを宣言する。
 * @param $no_layout
 * レイアウトを使用しないかどうかを指定する。
 * 省略時はレイアウトも使用しない
 */
function no_template($no_layout=true) {
  if($no_layout) {
    no_layout();
  }
  Page::$no_template = true;
}
/**
 * リダイレクトする。
 *
 * IE9とFirefoxでベースディレクトリに関係する扱いが違うため、
 * 相対パスが指定されたときは先頭にベースディレクトリを付加して
 * 絶対パスに変換している。
 * IEの動きがおかしいと思われる。
 */
function redirect_to($url) {
  $redirect_url = '';
  if(preg_match('/^((http|https):\/\/|\/)/', $url)) {
    //絶対パスならそのままリダイレクト。
    $redirect_url = $url;
  } else {
    //相対パスなら先頭にベースディレクトリを付加して絶対パスに変換する。
    $redirect_url = Page::$base_dir . $url;
  }
  header('Location: '. $redirect_url);
  exit;
}
/**
 * URL変更によるリダイレクトを行う。
 * ステータスコード 301(Moved Permanently) も出力する
 *
 * IE9とFirefoxでベースディレクトリに関係する扱いが違うため、
 * 相対パスが指定されたときは先頭にベースディレクトリを付加して
 * 絶対パスに変換している。
 * IEの動きがおかしいと思われる。
 */
function redirect_moved_permanently($url) {
  $redirect_url = '';
  if(preg_match('/^((http|https):\/\/|\/)/', $url)) {
    //絶対パスならそのままリダイレクト。
    $redirect_url = $url;
  } else {
    //相対パスなら先頭にベースディレクトリを付加して絶対パスに変換する。
    $redirect_url = Page::$base_dir . $url;
  }
  header('HTTP/1.1 301 Moved Permanently');
  header('Location: '. $redirect_url);
  exit;
}
/**
 * インクルードパスを追加する。
 * PEARなど外部ライブラリ使用時に必要。
 */
function add_include_path($path) {
  ini_set(
    'include_path',
    ini_get('include_path') .
    PATH_SEPARATOR .
    $path);
}
/////////////////////////////////////////////////////////////////////////////
// 以下、上記ヘルパー関数によって指定された内容を
// HTML要素として出力するヘルパ関数。
// 通常は レイアウトにて使用する
/////////////////////////////////////////////////////////////////////////////
/**
 * コンテントタイプを出力する
 */
function put_content_type() {
  ?><meta http-equiv="Content-Type" content="<?=Page::$content_type?>" /><?php
}
/**
 * METAタグを出力
 */
function include_metas() {
  if(Page::$base_dir != '') {
    ?><base href="<?=Page::$base_dir?>"/><?php
  }
  foreach(Page::$metas as $meta) {
    ?>
    <meta name="<?=$meta['name']?>" content="<?=$meta['content']?>"/>
    <?php
  }
}
/**
 * TITLEを出力
 */
function put_title() {
  if(Page::$site_name == '') {
    ?><title><?= Page::$page_title ?></title><?php
  } else {
    ?><title><?= Page::$page_title ?>|<?= Page::$site_name?></title><?php
  }
}
/**
 * LINKタグでスタイルシートを取り込む
 */
function include_stylesheets() {
  foreach(Page::$stylesheets as $filename) {
    include_stylesheet($filename);
  }
  Page::$stylesheets_included = true;
}
function include_stylesheet($filename) {
  if(!preg_match('/^(\\.\\.\\/|\\.\\/|\\/|http:|https:)/',$filename)) {
    //ドット、スラッシュ、またはhttp: などで始まらないパスが指定されているとき
    $search_dirs = array(
      '../css/',
      'css/',
      'modules/' . Page::$module . '/css/',
            // jsにcssを置く場合もあるためjsフォルダも検索する
      '../js/',
      'js/',
      'modules/' . Page::$module . '/js/'
      );
    foreach($search_dirs as $search_dir) {
      if(is_file($search_dir . $filename)) {
        $url = $_SERVER['REQUEST_URI'];
        $url = preg_replace('(/index\.php(/|\\b).*$)', '', $url);
        //$url = preg_replace('(/index\.php/.*$)', '', $url);
        $url .= '/' . $search_dir . $filename;
        include_stylesheet_($url, get_mtime($search_dir . $filename));
      }
    }
  } else {
    //パス指定されているとき
    include_stylesheet_($filename);
  }
}
function include_stylesheet_($url, $cache_id = null) {
  if(!array_key_exists($url, Page::$included_stylesheet)) {
    Page::$included_stylesheet[$url] = true;
        $url = add_url_param($url, $cache_id);
    ?>
    <link rel="stylesheet" href="<?=$url?>" type="text/css"/>
    <?php
  }
}
/**
 * SCRIPTタグでjavascriptを取り込む
 */
function include_javascripts() {
  foreach(Page::$javascripts as $filename) {
    include_javascript($filename);
  }
  Page::$javascripts_included = true;
}
function include_javascript($filename) {
  if(!preg_match('/^(\\.\\.\\/|\\.\\/|\\/|http:|https:)/',$filename)) {
    //ドット、スラッシュ、またはhttp: などで始まらないパスが指定されているとき
    $search_dirs = array(
      '../js/',
      'js/',
      'modules/' . Page::$module . '/js/'
      );
    foreach($search_dirs as $search_dir) {
      if(is_file($search_dir . $filename)) {
        $url = $_SERVER['REQUEST_URI'];
        $url = preg_replace('(/index\.php/.*$)', '', $url);
        $url .= '/' . $search_dir . $filename;
        include_javascript_($url, get_mtime($search_dir . $filename));
            }
    }
  } else {
    //パス指定されているとき
    include_javascript_($filename);
  }
}
function include_javascript_($url, $cache_id = null) {
  if(!array_key_exists($url, Page::$included_javascript)) {
    Page::$included_javascript[$url] = true;
        $url = add_url_param($url, $cache_id);
        ?>
    <script type="text/javascript" src="<?=$url?>"></script>
    <?php
  }
}
/**
 * モジュール名のCSSファイル、アクション名のCSSが、存在するならインクルードする。
 */
function include_default_stylesheet() {
  $css_files = array(
    'modules/' . Page::$module . '/css/' . Page::$module . '.css',
    'modules/' . Page::$module . '/templates/' . Page::$action . '.css');
  foreach($css_files as $css_file) {
    if(is_file($css_file)) {
      $url = $_SERVER['REQUEST_URI'];
      $url = preg_replace('(/index\.php/.*$)', '', $url);
      $url .= '/' . $css_file;
      include_stylesheet_($url, get_mtime($css_file));
    }
  }
}
/**
 * モジュール名のJS、アクション名のJSが、存在するならインクルードする。
 */
function include_default_javascript() {
  $js_files = array(
    'modules/' . Page::$module . '/js/' . Page::$module . '.js',
    'modules/' . Page::$module . '/templates/' . Page::$action . '.js');
  foreach($js_files as $js_file) {
    if(is_file($js_file)) {
      $url = $_SERVER['REQUEST_URI'];
      $url = preg_replace('(/index\.php/.*$)', '', $url);
      $url .= '/' . $js_file;
      include_javascript_($url, get_mtime($js_file));
    }
  }
}
function get_mtime($filename) {
    $stat = stat($filename);
    if($stat === FALSE) {
        return null;
    }
    return $stat[9];
}
function add_url_param($url, $cache_id) {
    if(!is_null($cache_id)) {
        if(!preg_match('/\?/', $url)) {
            $url .= '?';
        } else if(!preg_match('/\?$/', $url)) {
            $url .= '&';
        }
        $url .= $cache_id;
    }
    return $url;
}

{$oresama_home}/sys/Database.class.php

データベース(mysql)と接続するためのクラス。O-Rマッピングとか出来ません。SQLかければOK

<?php
/**
 * mysql を使うためのクラス
 * @author Takami
 */
class Database {
  //デフォルトのインスタンス
  static public $instance = null;
  /**
   * デフォルトインスタンスを取得する
   */
  static public function currentConnection() {
    return Database::$instance;
  }
  //DBコネクション
  private $con = null;
  /**
   * コンストラクタ
   */
  public function Database() {
    try {
    //DBに接続
    //データベースを選択
    //TODO:mysqli_select_dbに変更
    if(isset($NO_USE_MYSQLI)) {
      $this->con = mysql_connect(
        DBCONN_URL,
        DBCONN_USERNAME,
        DBCONN_PASSWORD)
        ;//or die("Database::Database() : mysql_connect failure ");
      $sdb = mysql_select_db(DBCONN_DATABASE, $this->con)
      ;//or die("Database::Database() : mysql_select_db failure ");
      //必ずUTF-8でアクセスする
      $this->execute('SET CHARACTER SET UTF8;');
      return;
    }
    $this->con = mysqli_connect(
        DBCONN_URL,
        DBCONN_USERNAME,
        DBCONN_PASSWORD)
        ;//or die("Database::Database() : mysqli_connect failure ");
    mysqli_select_db($this->con, DBCONN_DATABASE)
    ;//or die("Database::Database() : mysqli_select_db failure ");
    //必ずUTF-8でアクセスする
    $this->execute('SET CHARACTER SET UTF8;');
    } catch (Exception $e) {
      $this->con = null;
    }
  }
  /**
   * SELECT文を実行して結果の全件を配列に格納して返す。
   * @param string $sql
   */
  public function fetchAll($sql) {
    if(is_null($this->con)) { return null; };
    Database::logsql($sql);
    //結果セット
    $rows = array();
    //TODO:mysqliに変更
    if(isset($NO_USE_MYSQLI)) {
      $result = mysql_query($sql, $this->con)
        or die("fetchAll failure SQL:".$sql);
      $n = mysql_num_rows($result);
      for($i = 0; $i < $n; $i++) {
        $rows[] = mysql_fetch_array($result);
      }
      //リソースの開放
      mysql_free_result($result);
      return $rows;
    }
    $result = mysqli_query($this->con, $sql);
    while(!is_null($row = mysqli_fetch_array($result))) {
      $rows[] = $row;
    }
    mysqli_free_result($result);
    return $rows;
  }
  /**
   * SELECT文を実行して結果の全件を配列に格納して返す。
   * @param string $sql
   */
  public function fetch1($sql) {
    if(is_null($this->con)) { return null; };
    Database::logsql($sql);
    //結果
    $row = null;
    //TODO:mysqliに変更
    if(isset($NO_USE_MYSQLI)) {
      $result = mysql_query($sql, $this->con)
      or die("fetchAll failure SQL:".$sql);
      $n = mysql_num_rows($result);
      if($n > 0) {
        $row = mysql_fetch_array($result);
      }
      //リソースの開放
      mysql_free_result($result);
      return $row;
    }
    $result = mysqli_query($this->con, $sql);
    $row = mysqli_fetch_array($result);
    mysqli_free_result($result);
    return $row;
  }
  /**
   * SQLの実行。処理件数を返す。
   * @param unknown_type $sql
   */
  public function execute($sql) {
    if(is_null($this->con)) { return null; };
    Database::logsql($sql);
    //SQL実行
    if(isset($NO_USE_MYSQLI)) {
      $result = mysql_query($sql, $this->con)
      ;//or die("<p><strong>execute failure<strong><br/>SQL:<br/><span style='color:red'>".$sql."</span></p>");
      //処理(影響のあった)行数を返す
      return mysql_affected_rows($this->con);
    }
    $result = mysqli_query($this->con, $sql)
    ;//or die("<p><strong>execute failure<strong><br/>SQL:<br/><span style='color:red'>".$sql."</span></p>");
    //処理(影響のあった)行数を返す
    return mysqli_affected_rows($this->con);
  }
  /**
   * 最後に追加したPKを返す。
   */
  public function getLastInsertId($table) {
    if(is_null($this->con)) { return null; };
    $last_inserted_id = 0;
    $result = $this->fetchAll("SELECT DISTINCT LAST_INSERT_ID( ) AS id FROM $table");
    if(!is_null($result) && count($result) > 0) {
      $last_inserted_id = $result[0]['id'];
      debug("getLastInsertId( $table ) returns $last_inserted_id", __FILE__, __LINE__);
    }
    return $last_inserted_id;
  }
  /**
   * トランザクションを開始する。
   */
  public function startTransaction() {
    $this->execute( "SET AUTOCOMMIT = 0" );
    $this->execute( "START TRANSACTION" );
  }
  /**
   * コミットする。
   */
  public function commit() {
    $this->execute( "COMMIT" );
  }
  /**
   * トランザクションをロールバックする。
   */
  public function rollback() {
    $this->execute( "ROLLBACK" );
  }
  /**
   * 文字列を安全にクォートする。
   */
  public function q($s) {
    $partial = preg_split("/'/", $s);
    $retstr = implode("''", $partial);
    return "'" . $retstr . "'";
  }
  static private function logsql($sql) {
//   $fp = fopen("../conf/sql.log", "a+b");
//   if(!is_null($fp)) {
//    fwrite($fp, '[' . date('Y-m-d H:i:s') . ']' . $sql . "\n\n");
//   }
  }
}

{$oresama_home}/sys/FileManage.class.php

ファイル操作関連ですね。

<?php
class FileManage {
  /**
   * ディレクトリから再帰的にファイルを検索し、
   * 見つかったファイル名をすべて配列で返却
   *
   * @param string $root_dir 検索するディレクトリ
   * @param string $regexpat 検索パターン
   */
  static public function getFiles($root_dir, $regexpat = null) {
    $dir_list = FileManage::get_subdir($root_dir);
    $file_list = FileManage::get_files($root_dir, $regexpat);
    //サブディレクトリについて再帰してファイルを探す
    foreach($dir_list as $entry) {
      $subfiles = FileManage::getFiles($entry, $regexpat);
      foreach($subfiles as $fn) {
        $file_list[] = $fn;
      }
    }
    return $file_list;
  }
  /**
   * サブディレクトリの配列を返す。再帰しない。
   */
  static public function get_subdir($root_dir) {
    $dir_list = array();
    $handle = opendir($root_dir)
      or die ("FileManage::get_subdir('$rootdir') fail, ");
    while (false !== ($entry = readdir($handle))) {
      if ($entry != "." && $entry != "..") {
        $pathname = $root_dir . '/' . $entry;
        if(is_dir($pathname)) {
          $dir_list[] = $pathname;
        }
      }
    }
    closedir($handle);
    return $dir_list;
  }
  /**
   * パターンにマッチしたファイル名の配列を返す。ディレクトリは対象外。また、再帰はしない。
   * ファイル名の先頭には引数で与えられたディレクトリ名が付加されます。
   */
  static public function get_files($root_dir, $regexpat = null) {
    $file_list = array();
    $handle = opendir($root_dir)
      or die ("FileManage::get_files('$rootdir') fail, ");
    while (false !== ($entry = readdir($handle))) {
      if ($entry != "." && $entry != "..") {
        $pathname = $root_dir . '/' . $entry;
        if(!is_dir($pathname)
        && (is_null($regexpat) || $regexpat == ''
          || preg_match($regexpat, $pathname)))
        {
          $file_list[] = $pathname;
        }
      }
    }
    closedir($handle);
    return $file_list;
  }
}


RSS Feed Widget
俺様軽量フレームワーク
(http://bicycle.life.coocan.jp/takamints/index.php/techtips/page_class_php)
by Koji Takami ( email: vzg03566@gmail.com, GitHub: takamin )