Smartyを使った小規模WEBアプリケーション用フレームワークを考える

2018-08-23

ここ数年はCakePHPやSymfonyといったフレームワークを使って(或いは指定され)WEBアプリケーション制作を行う案件が多く、そう言えばネイティブコードで書くことが極端に少なくなったなぁと思いまして、昔よく使っていたSmartyで簡単なフレームワークを書いてみようかと思います。3~4日で書き終わる小さな案件でCake等のフレームワークを使うのは重たすぎる感が有りますので。

https://studio-key.com/Sample2/Smarty/basic/page1.html

とりあえず基本中の基本だけなのでファイルはこれだけです。www2はさくらサーバーのパス名と被ってしまったので。

 

構築概念

Smartyやその他の設定ファイルなど、可能ならばサーバーのWEB公開ディレクトリより上に置きたいところですが、クライアントさんが既に契約済みのサーバーですと権限がないこともしばしば。aliasの変更など出来れば良いのですが、既にホームページファイルが置いて有り叶わぬケースも。そこでmod_rewriteを使ってWEB用のディレクトリを設定するという方法を取ることにします。

各ページ名はcontroller.phpに定義されたControllerクラスのメソッド名と同じとすることで、プログラムの何処で処理されているのか判りやすくします。多くのフレームワークで使わているものですね。また、Smartyテンプレートもページ名と同じ名称で作成します。上図のpage1.tplがそれにあたります。

 

.htaccessに関して

設置ルート

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteRule    ^$    www2/    [L]
    RewriteRule    (.*) www2/$1    [L]
</IfModule>

WEB公開ルート

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>

サーバーによってはRewriteBaseを書かないとダメな場合も有りますが、基本的にはこんな感じで。www2にマップさせ、あとはmod rewriteでURLをフレンドリーにします。

 

core.php

Smartyの読み込みやサニタイズなど、基本的なプログラムの集合体としています。

<?php
require '../smarty/libs/Smarty.class.php';
require '../config/config.php';

class Core{
	
	static function settingSmarty(){
		$libsUrl = $str = str_replace("www2","",wwwroot);
		
		//$smarty = new Smarty;
		$smarty = new SmartyBC(); //phpコードを使うかも?なのでBCで
		$smarty->php_handling     = Smarty::PHP_ALLOW; // PHP_ALLOW or SMARTY_PHP_REMOVE
		$smarty-> error_reporting = E_ALL & ~E_NOTICE;
		$smarty-> template_dir    = $libsUrl.'smarty/templates/';
		$smarty-> compile_dir     = $libsUrl.'smarty/templates_c/';
		$smarty-> cache_dir       = $libsUrl.'smarty/cache/';
		$smarty->debugging        = false; //debug表示
		$smarty->caching          = true;
		$smarty->cache_lifetime   = 120;
		
		return $smarty;
	}
	
/*
 * 文字のHTMLエンティティや置換等、sqlインジェクション対策
 * 
 */
	public function sanitize($array) {
		if( !is_array($array) ) {
			$array = $this->change_replace($array);
			return $array;
		} else {
			$p=0;
			foreach($array AS $key=>$str){
				if($key) $p++;
			}
			if( $p === 0) return null;
		}
		$sub_Array = array();
			foreach($array as $key=>$str){
				if( is_array($str) ){
					$sub_Array[$key] = $this->sanitize($str);
				}else{
					$str = $this->change_replace($str);
					$sub_Array[$key] = $str;
				}
		}
		return $sub_Array;
	}

全文では有りませんが、こんな感じです。ちなみにこの下の方に都道府県配列などをずら~と書いています。必要に応じてここにデータベース接続やPDFやCSV書き出し、画像アップロード等のプログラムを追加していく感じですね。

 

controller.php

ブラウザ要求に対してページ表示を制御します。

<?php
require 'core.php';

class Controller extends Core{
	public $page;
	public $action;
	
	function __construct() {
		$script_name = parent::sanitize($_SERVER['SCRIPT_NAME']);
		$request_url = parent::sanitize($_SERVER['REQUEST_URI']);
		
		$ex = explode('/www', $script_name); //webrootがwwwなので、/wwwをdelimiterとして配列へ
		$ex = explode('/', $ex[0]);          //最初の配列を更にスラッシュで配列へ
		$ex = array_reverse($ex);            //逆順にした最初の文字列が設置URL
		$router_base = $ex[0];
		
		$ex = explode($router_base.'/', $request_url); //REQUEST_URIの方を設置URLをdelimiterとして配列へ
		$ex = explode('/', $ex[1]); //スラッシュをdelimiterとして配列へ
		
		if(isset($ex[0]) && $ex[0]){
			$string = $ex[0];
			if( preg_match("/\./",$string) ){
				$ex2 = explode(".", $string); // xxxx.htmlなど拡張子は抜く
				$string = $ex2[0];
			}
			$this->page = $string;
			unset($ex[0]);
		}
		
		if(isset($ex[1]) && $ex[1]){
			$string = $ex[1];
			if( preg_match("/\./",$string) ){
				$ex2 = explode(".", $string); // xxxx.htmlなど拡張子は抜く
				$string = $ex2[0];
			}
			$this->action = $string;
			unset($ex[1]);
		}
		$data = array_merge($ex);
		
		if($this->page){
			if (method_exists('Controller',$this->page)) {
				$page = $this->page;
				$tpl  = $this->page.'.tpl';
				$this->$page($this->action,$tpl,$data);
			} else {
				$this->pageError();
			}
		}
	}
	
/*
 * 非定義メソッドを表示しようとした場合
 */
	public function pageError(){
		$smarty = parent::settingSmarty();
		$smarty->display('404.tpl');
	}

/*****************************************************
 * ここから下へ作成したページのメソッドを記述していく
 *****************************************************/
	
	
	public function page1($action=null,$tpl=null,$param=array()){
		$smarty = parent::settingSmarty();
		$smarty->display($tpl);
	}
	
	public function param($action=null,$tpl=null,$param=array()){
		$smarty = parent::settingSmarty();
		
		$smarty->assign('action', $action);
		$smarty->assign('param', $param);
		$smarty->display($tpl);
	}
	
}

コンストラクタでは表示しているURLを分解し、メソッドやテンプレート名を定義しています。

https://xxx.xx/Smarty/page/action/array[0]/array[1]/array[2]…

このようなURLの場合は、pageというメソッドとpage.tplというテンプレートを作成するということになります。残りは配列としてメソッドに渡します。

https://studio-key.com/Sample2/Smarty/basic/param/hoge1/hoge2/hoge3/hoge4

これが実際の例です。

	public function param($action=null,$tpl=null,$param=array()){
		$smarty = parent::settingSmarty();
		
		$smarty->assign('action', $action);
		$smarty->assign('param', $param);
		$smarty->display($tpl);
	}

テンプレート

{include file='common/header.tpl'}
<div style="padding: 30px; text-align: center">
  <h1>param : パラーメーター確認</h1>

$action : {$action}
<hr />
$param : 
{$param|@debug_print_var}
</div>

{include file='common/footer.tpl'}

あとはメソッドの存在を確認し、無ければ404.tplを表示します。

 

あとがき

データベースを使わないようなものでも、変数を少し使いたいってことは有るかと思います。もちろんjavascriptで対応可能なケースも多いでしょうけど好みかなと。また、URLに自由度が有るので、小さなホームページなんかにも良いと思います。例えば一定期間だけ使うキャンペーン用ページとかですね。一枚ものなら利用しているCMSにページ追加なり静的HTMLなりで良いでしょうけど、キャンペーンページ専用のヘッダー・フッターや簡単な申し込みフォームを絡めるなど、少しだけ凝っている場合にこういったものが有効かと思います。

年齢ゆえか集中力が以前より長続きせず、学習時間をあまり取れなくなって便利なものに頼りがちになっています。もちろん業務効率向上には必要ですし安定感も有ります。ただ、便利だからとマニュアル通りに書いていると、思わぬところでしっぺ返しを食らう気がします。何でもかんでも楽しようと考えず、ネイティブなコードで書くことも必要だなと改めて実感しました。

Smartyは便利なものの類じゃ?という部分はまぁ・・・ツッコミ無しで。