前したらば削除スクリプトを公開しましたが、2009/08初頭に行われた管理画面改修のせいで動かなくなっていました。
というわけで新管理画面に対応した版を作成しました。
もし万一使っていた人がいたら差し替えてください。
ダウンロード(zip)
したらば管理画面について所感。
前の管理画面はどこかに移動するたびにパスワードをhiddenで持ち回すという有り得ない作りだったのですが、新管理画面になってようやくログインとセッション機構が導入されました。
まあログインのセッションを無期限にしているとかこれまたちょっとどうなのよという感じなのですが、それでも前のよりはよっぽどマシです。
ただ、よくわかんないのが各管理画面の遷移。
何故か知らんが何かを送信するたびに一旦302でリダイレクトするんだよね。
おかげで標準関数での実装に手間取り、Zend_Httpを使用することになりました。
ZendFrameworkが入ってないという人は、ここらへんかを参考に入れてください。
まあ、インストールせずとも、たぶんZend/Httpフォルダ丸ごとアップロードで動かなくもない気もしないでもないです。
前回の続き。
テンプレを作成してバリデータと合わせました。
index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
//=========================================================================
//準備
//根本のフォルダ
$base_dir=dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR ;
define('BASE_DIR',$base_dir);
//必要なファイルをインクルード
require_once('../../smarty/libs/Smarty.class.php');
//=========================================================================
//クラス
class HtmlForm{
//-------------------------------------------------------------------------
//インスタンス変数等
//Smartyテンプレ、キャッシュ置き場
private $smarty_template_dir='../templates/';
private $smarty_compile_dir='../templates_c/';
//エラーメッセージ入れ
protected $error_message_array=array(
'error_name_null'=>'氏名が入力されていません'
,'error_age_null'=>'年齢が入力されていません'
,'error_age_int'=>'年齢が正しくありません'
);
//-------------------------------------------------------------------------
//コンストラクタ
public function __construct(){
//Smarty
$this->smarty=new Smarty();
$this->smarty->template_dir = $this->smarty_template_dir;
$this->smarty->compile_dir = $this->smarty_compile_dir;
//実行メソッドを呼ぶ
$this->init();
exit();
}
//-------------------------------------------------------------------------
//実行
public function init(){
//実行するメソッド選択
if(isset($_REQUEST['form_input'])){
$this->FormInput();
}elseif(isset($_REQUEST['form_complete'])){
$this->FormComplete();
}else{
$this->FormIndex();
}
}
//-------------------------------------------------------------------------
//表示
public function view($template_file='index.html'){
//変数アサイン
//$this->dataに突っ込めば$dataで表示できる
$this->smarty->assign('data',$this->data);
//$_REQUESTは常時参照可能
$this->smarty->assign('request',$_REQUEST);
//表示
$this->smarty->display($template_file);
exit();
}
//-------------------------------------------------------------------------
//投稿内容一覧画面
public function FormIndex(){
//表示内容取得
$data_array=array();
$tmp_array=file('data.txt',
FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
foreach($tmp_array as $data){
$data_array[]=explode("\t",$data);
}
$this->data['data_array']=$data_array;
//表示
$this->view('form_index.html');
}
//-------------------------------------------------------------------------
//フォーム入力画面
public function FormInput(){
//表示
$this->view('form_input.html');
}
//-------------------------------------------------------------------------
//フォーム入力完了画面
public function FormComplete(){
//入力値のバリデート
$ret=$this->_FormValidate($_REQUEST);
if(!$ret){
$this->FormInput();
}elseif($ret['error']){
$this->data['error_message']=
$this->_FormSetErrorCode($ret['error']);
$this->FormInput();
}
//保存
$data=implode("\t",$_REQUEST)."\r\n";
file_put_contents('data.txt',$data,FILE_APPEND);
//表示
$this->view('form_complete.html');
}
//=========================================================================
//-------------------------------------------------------------------------
//サブルーチン//入力値チェック
public function _FormValidate($request_array){
//引数
if(!$request_array){return false;}
$ret=array();
//バリデート
if(!$request_array['name']){
$ret['error'][]='error_name_null';
}
if(!$request_array['age']){
$ret['error'][]='error_age_null';
}elseif(!is_numeric($request_array['age'])){
$ret['error'][]='error_age_int';
}
//返却
if($ret['error']){
return $ret;
}else{
return true;
}
}
//-------------------------------------------------------------------------
//サブルーチン//エラーメッセージを入れておく
public function _FormSetErrorCode($error_code_array=array()){
//引数
if(!is_array($error_code_array)){return false;}
//返り値
$ret=array();
//各エラーコードに対してエラーメッセージを対応させる
foreach($error_code_array as $error_code_val){
$ret[]=$this->error_message_array[$error_code_val];
}
//返却
return $ret;
}//クラスのおわり
}
|
templates/form_index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>投稿フォーム</title>
</head>
<body>
<a href="./?form_input=1">発言する</a><br />
{if $data.data_array}
<table border=1>
<tr>
<th>投稿日</th><th>氏名</th><th>年齢</th><th>発言</th>
</tr>
{foreach from=$data.data_array item=data_item }
<tr style="background:{cycle values='lightcyan,peachpuff'};">
<td>
{$data_item.0|date_format:"%Y-%m-%d %H:%M:%S"}
</td>
<td>
{$data_item.1|escape}
</td>
<td>
{$data_item.2|escape}
</td>
<td>
{$data_item.3|escape}
</td>
</tr>
{/foreach}
</table>
{/if}
</body>
</html>
|
templates/form_input.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>投稿フォーム</title>
</head>
<body>
{if $data.error_message}
<div style="color:red;">
{foreach from=$data.error_message item=error_message_val}
{$error_message_val|escape}<br />
{/foreach}
</div>
{/if}
<form method="POST" action="./?form_complete=1">
氏名:
<input type="text" name="name" size="20"
maxlength="20" value="{$request.name|escape}" /><br />
年齢:
<input type="text" name="age" size="10"
maxlength="4" value="{$request.age|escape}" /><br />
発言:
<input type="text" name="val" size="10"
maxlength="4" value="{$request.age|escape}" /><br />
<input type="submit" value="投稿する">
</form>
</body>
</html>
|
templates/form_complete.html
1
2
3
4
5
6
7
8
9
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>投稿フォーム</title>
</head>
<body>
投稿されました
</body>
</html>
|
保存読み込み部分がアバウトすぎて、改行やタブを送信されたら死ぬというどうにもならないフォームです。
まあそこらへんどうにかしようとすればどうにでもなるのですが、面倒なので今回はパス。
DBに突っ込めば面倒なことを考える必要もないので、特に理由のない限り余計なことはせずにDBにしてしまいましょう。
それに保存用の関数などは直接書かず、それ用のクラスを作ってそちらに投げた方が色々と便利です。
バリデータの部分はもうちょっとerror_name_nullみたいなのを直接書かずにHtmlForm::error_message_arrayあたりをうまく利用して定義するべきなんですが、これもまあ面倒なのでいいや。
やりすぎてZend_Formみたいな意味不明状態になってもなんですし。
本当は投稿完了画面なんて要らなくて、インデックスに投稿完了した旨表示するだけでかまわないと思うのですが、何故か皆付けたがるので作っています。
まあそこらへんの遷移は好きなようにしてください。
こんなかんじで昔のよりちょっとだけソースが綺麗なような気がしないでもないフォームが完成しました。
見た目を変えたい場合は各HTMLファイルだけを触ればいいのでPHPの知識は不要となり、ロジックを触る場合はphpファイルを触ればいいのでデザインを壊す心配をする必要もありません。
いいことづくめですね。
まあ、といいつつプログラマがデザインの修正もさせられるのが世の常なんですが…
前回形は作ってしまったので、後はこりこり書いていくだけです。
投稿内容は後でどうとでも修正できるのでとりあえず氏名、年齢、発言という簡単なフォームで、テンプレ以外はまあこんな感じでしょう、ということで途中まで作ったの図。
/form/lib/index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
//=========================================================================
//準備
//根本のフォルダ
$base_dir=dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR ;
define('BASE_DIR',$base_dir);
//必要なファイルをインクルード
require_once('../../smarty/libs/Smarty.class.php');
//=========================================================================
//クラスclass HtmlForm{
//-------------------------------------------------------------------------
//インスタンス変数等
//Smartyテンプレ、キャッシュ置き場
private $smarty_template_dir='../templates/';
private $smarty_compile_dir='../templates_c/';
//エラーメッセージ入れ
protected $error_message_array=array(
'error_name_null'=>'氏名が入力されていません'
,'error_age_null'=>'年齢が入力されていません'
,'error_age_int'=>'年齢が正しくありません'
);
//-------------------------------------------------------------------------
//コンストラクタ
public function __construct(){
//Smarty
$this->smarty=new Smarty();
$this->smarty->template_dir = $this->smarty_template_dir;
$this->smarty->compile_dir = $this->smarty_compile_dir;
//実行メソッドを呼ぶ
$this->init();
exit();
}
//-------------------------------------------------------------------------
//実行
public function init(){
//引数によって実行するメソッドを分岐
if(isset($_REQUEST['form_input'])){
$this->FormInput();
}elseif(isset($_REQUEST['form_complete'])){
$this->FormComplete();
}else{
$this->FormIndex();
}
}
//-------------------------------------------------------------------------
//表示
public function view($template_file='index.html'){
//変数アサイン
//$this->dataに突っ込めば$dataで表示できるようにします
$this->smarty->assign('data',$this->data);
//$_REQUESTは常時参照可能
$this->smarty->assign('request',$_REQUEST);
//表示
$this->smarty->display($template_file);
exit();
}
//-------------------------------------------------------------------------
//投稿内容一覧画面
public function FormIndex(){
//表示内容取得
$this->data['applied_form']=file('data.txt',FILE_IGNORE_NEW_LINES);
//表示
$this->view('form_index.html');
}
//-------------------------------------------------------------------------
//フォーム入力画面
public function FormInput(){
//表示
$this->view('form_input.html');
}
//-------------------------------------------------------------------------
//フォーム入力完了画面
public function FormComplete(){
//入力値のバリデート
$ret=$this->_FormValidate($_REQUEST);
if(!$ret){
$this->FormInput();
}elseif($ret['error']){
$this->data['error_message']=
$this->_FormSetErrorCode($ret['error']);
$this->FormInput();
}
//保存
file_put_contents('data.txt',$ret['data'],FILE_APPEND);
//表示
$this->view('form_complete.html');
}
//=========================================================================
//-------------------------------------------------------------------------
//サブルーチン//入力値チェック
public function _FormValidate($request_array){
//引数
if(!$request_array){return false;}
//バリデート
//返却
return $ret;
}
//-------------------------------------------------------------------------
//サブルーチン//エラーメッセージを入れておく
public function _FormSetErrorCode($error_code_array=array()){
//引数
if(!is_array($error_code_array)){return false;}
//返り値
$ret=array();
//各エラーコードに対してエラーメッセージを対応させる
foreach($error_code_array as $error_code_val){
$ret[]=$this->error_message_array[$error_code_val];
}
//返却
return $ret;
}
//クラスのおわり
}
|
フォームデータを保存する部分は非常にやる気がないので超適当です。
まともに作るときはDBに入れた方が面倒もないでしょう。
$error_message_arrayと_FormSetErrorCode()あたりはもうちょっとどうにかならなかったのだろうか、という気がひしひしとしますが気にしない。
というわけで、あとはテンプレを書いて_FormValidate()と合わせれば完成といったところです。
ところで、私はだいたいいつも今回のようにテンプレを後回しにして作成するのですが、どのような順番で作成するのがいいんでしょうかね?
昔ずいぶん適当な投稿フォームを作りましたが、昨今流行りのクラスを使ってたらりんと書き換えていきましょう。
フォルダ構成はこんなかんじ
/Smarty/(Smarty構成ファイル)
/form/htdocs/index.php
/lib/index.php
/templates/(テンプレ)
/templates_c/(テンプレのキャッシュ置き場)
/form/htdocs/index.php
1
2
3
4
5
6
7
|
//必要なファイルをインクルード
require_once('../lib/index.php');
//実行
$form=new HtmlForm();
|
htdocs/index.phpはDocumentRoot外に出ている唯一のファイルで、万一サーバの設定ミスとかでPHPが処理できない場合でもソースの流出を最小限に抑えます。
あとclassの上にnewを書くとうまく動かないことがあったりなかったりするので、分かれている方が安心。
というわけでlib/index.phpに実際の中身を書いていきます。
/form/lib/index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
//=========================================================================
//準備
//根本のフォルダ
$base_dir=dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR ;
define('BASE_DIR',$base_dir);
//必要なファイルをインクルード
require_once('../../smarty/libs/Smarty.class.php');
//=========================================================================
//クラスclass HtmlForm{
//-------------------------------------------------------------------------
//インスタンス変数等
//Smartyテンプレ、キャッシュ置き場
private $smarty_template_dir='../templates/';
private $smarty_compile_dir='../templates_c/';
//-------------------------------------------------------------------------
//コンストラクタ
public function __construct(){
//Smarty
$this->smarty=new Smarty();
$this->smarty->template_dir = $this->smarty_template_dir;
$this->smarty->compile_dir = $this->smarty_compile_dir;
//実行メソッドを呼ぶ
$this->init();
exit();
}
//-------------------------------------------------------------------------
//実行
public function init(){
}
//-------------------------------------------------------------------------
//表示
public function view($template_file='index.html'){
//変数アサイン
//$this->dataに突っ込めば$dataで表示できるようにします
$this->smarty->assign('data',$this->data);
//$_REQUESTは常時参照可能
$this->smarty->assign('request',$_REQUEST);
//表示
$this->smarty->display($template_file);
exit();
}
//-------------------------------------------------------------------------
//投稿内容一覧画面
public function FormIndex(){
}
//-------------------------------------------------------------------------
//フォーム入力画面
public function FormInput(){
}
//-------------------------------------------------------------------------
//フォーム入力完了画面
public function FormComplete(){
}
//=========================================================================
//-------------------------------------------------------------------------
//サブルーチン//入力値チェック
public function _FormValidate(){
}
//-------------------------------------------------------------------------
//サブルーチン//エラーコードを入れておく
public function _FormSetErrorCode(){
}
//クラスのおわり
}
|
別にクラスの利用法とか勉強したわけじゃなく概ね自己流なので本職から見たらこんなのクラスじゃねえとかいわれるのかもしれませんが、さくっと大まかな形だけを整えた状態です。
__construct()やview()、BASE_DIRの定義なんかは基底クラス、定義ファイルとしてどっかに置いておき、実装でそれをrequire_onceなりextendsなりして作成した方が使い回しも効いて便利なんですが、今回はまあいいや。
実際__construct()内の$this->init()なんてこの使い方では全く意味がありません。
スーパークラスにabstract function init()とか書いておいてオーバーライドするのが定石です。
具体的な実装は何も書いていませんが、とりあえずこれでもエラーは出ません。
後は個々のメソッドを埋めていけばそのうち動くようになります。
このようにアバウトな作り方で進められるのもOOPの利点の一つだと思います。
はっ、これがもしかして伝説のエクストリームプログラミング?(多分違う)
ZendFrameworkはCakePHPやEthna等と同じくフレームワークの一種ですが、特徴として各コンポーネントの独立性が高く、一部のモジュールだけを単体で使うことができます。
つうか本気で使用するにはmod_rewriteとかMVCの準備とかいろいろ必要でどうすりゃいいのかよくわからん。
まあモジュールをちょっと使ってみるだけならPear並に簡単です。
まず公式からダウンロードしてきたら適当な場所に解凍します。
で、その中のlibraryフォルダにインクルードパスを通せば後は好きなモジュールを使用することができます。
それどころかpearコマンドでもインストールでき、そうすると本気でPearと同等です。
# cd C:\xampp\php
# pear channel-discover zend.googlecode.com/svn
# pear install zend/zend
こんなかんじで実行するとC:\xampp\php\PEAR\Zendあたりに堂々とインストールされ、後はPearライブラリ呼ぶときと全く同じ使い方ができます。
まあ適当に、試しに簡単そうなZend_Dateを使ってみます。
zend_date1.php
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//Zend_Dateを使用するための初期設定
date_default_timezone_set('Asia/Tokyo');
//Zend_Date
require_once('Zend/Date.php');
$date = new Zend_Date();
//適当に使ってみる
print $date->get();
$date->set('13:00:00',Zend_Date::TIMES);
print $date->get();
|
Fatal error: Uncaught exception 'Zend_Date_Exception' with message 'Missing locale file 'C:\xampp\php\PEAR\Zend\Locale/Data/ja.xml' for 'ja' locale.' in C:\xampp\php\PEAR\Zend\Date.php:2136 Stack trace: #0 C:\xampp\php\PEAR\Zend\Date.php(989): Zend_Date->_calculate('set', '13:00:00', 'WW', NULL) #1 C:\xampp\htdocs\index.php(12): Zend_Date->set('13:00:00', 'WW') #2 {main} thrown in C:\xampp\php\PEAR\Zend\Date.php on line 2136
あれ?
C:\xampp\php\PEAR\Zend\Locale/Data/を見てみると、本来入っているべきja.xmlやja_JP.xmlなんかが一切存在しませんでした。
なぜだらう?
仕方ないのでこのフォルダだけ手動でコピペした。
zend_date2.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//Zend_Dateを使用するための初期設定
date_default_timezone_set('Asia/Tokyo');
//Zend_Date
require_once('Zend/Date.php');
$date = new Zend_Date();
//適当に使ってみる
print($date->get());
$date->set('11092210232008','mmMMHHddssyyyy');
print($date->get('yyyy年MM月dd日(E曜日) HH時mm分ss秒'));
$date->add('22:00:00',Zend_Date::TIMES);
print($date->get(Zend_Date::RFC_2822));
|
何が便利って入力値のフォーマットを指定できる!!
DBから取得した日時をstrtotime()に突っ込んだつもりが何故かVARCHAR型に変なフォーマットで入ってて理解できねーと言われた、とかいうような腐った設計でもこれで安心。
例にある'11092210232008'のような意味不明な文字列ですら、'2008年09月10日(水曜日) 22時11分23秒'と引数通りの解釈を行ってくれます。
また日付の足し引きや比較といった計算もadd()やsub()、isLater()等で自由に行うことができます。
http://framework.zend.com/manual/ja/zend.date.overview.html#id.date.basic.operations
計算の場合も第二引数で入力値の形式を指定することができるので、日時形式をいちいち分解してmktime()に突っ込んだり、使い方が微妙に不自由なDateTime::addを使用したりせずに済みます。
日時形式を設定、取得するために多数の定数が用意されており、決まった形式なら定数を使えます。
http://framework.zend.com/manual/ja/zend.date.constants.html#zend.date.constants.list
まあ、Zend_Date::DAYって書くより'dd'って書く方が早いような気もしないでもないですが。
いやあ、しかしZend_Date便利だね。
これが普及してくれれば現行の日付関数全部要らなくなるんだが。
注意点としては、西暦-99年~-01年の値を'yyyy'で取得しようとすると00-1とかいう形になってしまいます。
Zend_Date::YEARだときちんと-1になるんですがね。
古めの記事だと999年以前やZend_Date::YEARでもおかしかったみたいだけど、そこらへんは直ったみたい。
http://
http://
http://
さて本作、驚いたことにPS3への移植が決定されました。
2009/08/06発売予定です。
http://
http://
>2009 RECOM Ltd. ALL RIGHTS RESERVED.
さて先日、SimpleシリーズがついにPS3でも登場することが発表されました。
ダウンロードタイトルのSIMPLE500シリーズとなります。
第一弾は2009/07/31発売の『Vol.1 THE 麻雀』
http://
http://
>©2009 RECOM CO., LTD.
>2009 RECOM Ltd. ALL RIGHTS RESERVED.
>©2009 RECOM CO., LTD.
(つд⊂)ゴシゴシ
_,._
(;゚ Д゚)ホォォーー…?!
前カスタム修正子を作成しましたが、同じフォルダにfunction.html_select_date.phpといったファイルがあるのに気付いた人もいるかもしれません。
言うまでもなく{html_select_date}です。
というわけでカスタム関数も簡単に作成することができます。
今回は試しに{now}を作ってみます。
Smartyは{$smarty.now}でタイムスタンプを返してくれるのですが、マニュアルにあるとおり実行した時点でのタイムスタンプが返ってきます。
{$smarty.now}{1|sleep}{$smarty.now}
とすると1ずれた時間になってしまいます。
一度参照すれば以降は同じタイムスタンプが返ってきた方が便利でしょう。
プラグインフォルダにfunction.now.phpを作成してsmarty_function_nowという名前の関数を作成すれば、Smartyから{now}で呼び出すことができます。
smarty/libs/plugins/function.now.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
function smarty_function_now($params, &$smarty){
//初期値に関数とかは使えない
static $timestamp=false;
//まだ定義されてない時だけ定義
if(!$timestamp){
$timestamp=time();
}
//返却
if($params['format']){
return strftime($params['format'],$timestamp);
}else{
return $timestamp;
}
}
|
{now}{1|sleep}{now}
無事に同じタイムスタンプが取得できました。めでたし。
ついでに引数を指定することでフォーマットも指定できるようになっています。
{now format='%Y-%m-%d %H:%M:%S'}
というふうに使えます。
複数箇所で同じ時刻を参照したい場合に便利かもしれません。
Smartyには、変数を格納して表示する以外に、テンプレートを作成する上で便利な機能がいくつも存在します。
試しに{html_select_date}と書いてみると、いきなり生年月日選択フォームができあがります。
後は適当にオプションを突っ込めば面倒な日付選択フォームがあっさり完成します。
html_select_date.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//Smarty
require_once('./../smarty/libs/Smarty.php');
//取得した日時
$request_datetime=mktime(
$_REQUEST['Time_Hour']
,$_REQUEST['Time_Minute']
,$_REQUEST['Time_Second']
,$_REQUEST['Date_Month']
,$_REQUEST['Date_Day']
,$_REQUEST['Date_Year']
);
$smarty->assign('request_datetime',$request_datetime);
//表示
$smarty->display('html_select_date.html');
|
html_select_date.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<html><body>
<form method="POST" action="html_select_date.php">
{html_select_date field_order='Y'
start_year='-10' end_year='+10' time=$request_date}年
{html_select_date field_order='M'
month_format='%m' time=$request_date}月
{html_select_date field_order='D'
time=$request_date}日
{html_select_time display_hours=true
display_minutes=false display_seconds=false time=$request_date}時
{html_select_time display_hours=false
display_minutes=true display_seconds=false time=$request_date}分
{html_select_time display_hours=false
display_minutes=false display_seconds=true time=$request_date}秒
<input type="submit" value="送信">
</form>
</body></html>
|
実行するとこのようなフォームが現れます。
年 月 日 時 分 秒
問題点としては、フィールドの区切りに任意の値を設定できないので、YYYY年MM月DD日みたいなフォーマットを作るには、上記のように<select>単位で区切らなければなりません。
しかし、うんざりするほど長大な<select><option>を書くことや(ここのソースを見てみましょう)、{foreach}なんかでややこしいループを書くことに比べれば、圧倒的に楽です。
しかし{html_select_date}と{html_select_time}のオプションが何故か違うとか、month_formatとday_formatはあるのにyaer_formatは無いとか、なんか微妙に気持ち悪い仕様なのはどうにかならないんでしょうか。
GoogleとYahooを訪れたことがある場合、下記リンクの下線が画像で表示されます。
Google
Yahoo
これだけなら単なる装飾ですが、実はこれ、Webビーコンそのものです。
前JavaScriptで訪問履歴が取得できるという話をしましたが、JavaScriptをオフにしていても訪問履歴を取得する手段があったりします。
その手段とはCSSを用いるもので、:visited疑似クラスとbackground-imageプロパティを使用します。
両者ともに元々のCSSの機能であり脆弱性でも何でもありませんが、この二つを組み合わせるだけでこんなことができてしまいます。
index.php
1
2
3
4
5
6
7
8
9
10
11
12
|
<html>
<head>
<style TYPE="text/css"><!--
a#site-google:visited{background:url("site-google.jpg");}
a#site-yahoo:visited{background:url("site-yahoo.jpg");}
--></style>
</head>
<body>
<a href="http://google.co.jp/" id="site-google">Google</a><br />
<a href="http://yahoo.co.jp/" id="site-yahoo">Yahoo</a>
</body>
</html>
|
:visited疑似クラスはそのリンクが訪問済であった場合にだけ有効化される疑似クラスです。
で、その中でbackground-imageプロパティを呼ぶと、リンクが訪問済だった場合に指定したURLにpingが飛ぶというわけです。
上記のリンクは単に画像を呼んでいるだけですが、URLを"hoge.php?site=google"みたいにすれば普通にPHPで受け取ることができます。
逆にjpgの拡張子でPHPが呼び出されるようにしておけばユーザからは画像を呼んでいるようにしか見えません。
あとは引数に適当にIDでも割り振っておけばIPアドレスとの照合も簡単にできてしまいます。
使用しているのはCSSだけなので、ユーザ側で無効化しにくいというのも問題点(利点?)です。
最近はとりあえずCSSが無いとどうしようもないサイトばっかりですのでそうそう無効化するわけにもいきません。
実はこれ、2002年から議論され続けているにもかかわらず未だに解決していない問題だったりします。
https://bugzilla.mozilla.org/show_bug.cgi?id=147777
それだけ根が深くてどうにもならない問題だということなのでしょう。
アイコンはここから拾いました。
http://www.tubumikan.com/
Smartyのテンプレートでは、表示を簡潔に行うための色々な関数があります。
{foreach}は、概要はPHPのforeachとほぼ同じで、配列の中身を並べて表示するのに役立ちます。
書式はPHPとだいぶ違い、以下のようになります。
{foreach from=$str key=str_key item=str_item name=str_name}
{$str_key}
{$str_item}
{/foreach}
foreach($str as $str_key=>$str_item){
print($str_key);
print($str_item);
}
以上の二つがほぼ同じ意味になります。
name=str_nameはPHPのforeachには存在しない特殊な内容で、ループに関する内容を取得できます。
{$smarty.foreach.str_name.iteration}で現在のループ回数が、
{$smarty.foreach.str_name.first}で最初のループか否か、
{$smarty.foreach.str_name.last}で最後のループか否かといった内容が取得できます。
{if}は、これもPHPのifと同じです。
書式もPHPのifとほぼ同じです。
{if $a==1}
{$a}
{else}
{$b}
{/if}
if($a==1){
print($a);
}else{
print($b);
}
条件演算子はそのまま使えます。
また特殊な指定として、{if $a is even}が「偶数なら」になるといった、表示するのに便利な指定が幾つかあります。
ifとforeachを組み合わせることで、配列からテーブルを作成し、最初のループだったら<table>と記入、偶数行だったら背景色を変更、といったことができるようになります。
ただfirstとかは、後述のようにループの外に書いた方がわかりやすいですが。
上記の{if}と{foreach}を使用して簡単なテーブルを作ってみました。
index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
//Smarty
require_once('./../../smarty/libs/Smarty.php');
//変数
$str=array(
array(
'familyname'=>'小町'
,'firstname'=>'つぐみ'
,'ext'=>'武'
)
,array(
'familyname'=>'茜ケ崎'
,'firstname'=>'空'
,'ext'=>'武'
)
,array(
'familyname'=>'田中'
,'firstname'=>'優美清春香菜'
)
);
//変数アサイン
$smarty->assign('str',$str);
//表示
$smarty->display('index.html');
|
index.tpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<html>
<table border="1"> {*開始/終了タグは外側に書いた方がわかりやすい*}
{*ループ開始*}
{foreach from=$str item=str_item name=str_name}
{*偶数の場合のみbgcolorを表示*}
<tr{if $smarty.foreach.str_name.iteration is even}
bgcolor="#cccccc"
{/if}>
<td>{$str_item.familyname}</td>
<td>{$str_item.firstname}</td>
<td>
{if $str_item.ext}
{*$str_item.extが存在すれば*}
{$str_item.ext}
{else}
{*$str_item.extが存在しなければ*}
{/if}
</td>
</tr>
{if $smarty.foreach.str_name.last}
</table>{*最終ループなら終了タグを表示する、が少々分かり難い*}
{/if}
{/foreach}{*ループ終了*}
</body></html>
|
出力は以下のようになります。
| 小町 | つぐみ | 武 |
| 茜ケ崎 | 空 | 武 |
| 田中 | 優美清春香菜 |
{*コメント*}はSmartyのコメントで、HTMLのソースには出力されません。
出力されるコメントとしては普通に<!--コメント-->が使えます。
今回は中身が短いので却ってソースが長くなって分かり難く見えますが、Smartyを使い慣れると非常に使いやすくて便利になって手放せなくなります。
{cycle}?
黙れ。