小ネタ:WEBサイトの配色決めに役立つカラーピッカー

見映えの良いサイトを作成したいけれど、デザインセンスが要求されるので敷居が高い。これから時間をかけて、配色について勉強していかなくてはならないのだろうか…ええと、配色について教えてくれる場所は…美大?

さいわい、WEBデザインの仕事をしている人間が全員美大を出ているわけではない。また、デザインセンスについても、ほとんど皆無なれど仕事をしている人もいる。彼らがどのように仕事をしているのかというと、WEB上にある便利なツールを使って、悩んだりする時間を短縮しているようである。

指定したサイトでの使用色を採取するツール

デザインを決める最も短絡的な方法は、誰か他人のサイトをまねること。気に入ったサイトを片っ端からパクればよい。
ただ、パクるといっても、ページ構造からスタイルから全部のソースをそのままコピーするわけではなく、使用色の組み合わせをいただくのだ。一度は自分の気に入った配色だから、同じ色をベースにサイトを構築していくというのは、地味にモチベーションアップにも繋がる。

そしてその際に便利なのが、WEBサイトのURLを入れると配色を自動的に採取、カラーコードを表示してくれるツール。ネット上に星の数ほどもあるだろうけれど、表示の分かり易さなどから以下のサイトをおススメしたい。

Colours(http://webcolourdata.com

Coloursスクリーンショット

Coloursスクリーンショット

使用色だけでなく、サイト内で使用されている色の割合の情報も重要になる。このサイトの場合リボン状に使用割合を視覚化してくれるので、端の方のあまり使われていない色は文字やborderに使われている色だな、などと判断がつく。とっても便利。

選択色と組み合わせ相性の良い色を選んでくれるツール

どうしても使いたい色(ベースカラー)が決まっていて、その色との組み合わせで使って違和感の無い色の一覧を出してほしい。そんなケースもあるはず。そういった場合にオススメなのが、以下のサイト。

Paletton(http://paletton.com

Palettonスクリーンショット

Palettonスクリーンショット

取っ付きにくそうだけれども、いじっているうちに使い方に気付くはず。左上の小さいダイアルのようなボタンで、1〜4色までベースカラー数を選ぶ。あとはカラーピッカーをクリックしてベース色を選択ないし、”Base RGB”という欄にカラーコードを入れる。右のプレビューからマウスオーバーでカラーコードを取得可能。右下の”EXAMPLES…”タブを選ぶと、実際サイトに適用した例を表示してくれる。
このサイトが他のサイトより優れていると感じるのは、”Vision simulation”というメニューから閲覧者が色盲・色弱の場合の見え方を表示してくれるというところ。そこまで痒いところに手が届くカラーピッカーは、そうそう無い。

色の組み合わせツール(日本語)

Palettonは完璧なように思えるけれど、WEBデザイナーが使うには一点だけ不足部分がある。それは、色の組み合わせのデモがアルファベットを使ったサイトであるということ。アルファベットはそこまで複雑な形状をしておらず可読性も高いため、サイトの攻めの配色の一部として使うことも出来る。
Palettonで配色を決めて、いざ日本語サイトに適用しようとすると、吃驚するほど合わないというケースがある。それだけ漢字は複雑で、デザインの一部に組み込むのが難しいのだ。そこで、日本語に対応したカラーピッカーとして以下のサイトも紹介しよう。

ウェブ配色ツール Ver2.0(http://www.color-fortuna.com/color_scheme_genelator2/

ウェブ配色ツールVer2.0スクリーンショット

ウェブ配色ツールVer2.0スクリーンショット

Palettonに同じく、1色を決めると適当な組み合わせ色を選んでくれる。ただ注意書きに書いてあるように、ツール自体の配色の得手不得手はあるようだから、提示された色をそのままに近いデザインで使って、これなら万人が納得するはず!と思考停止してしまうのは良い結果に繋がらない。そもそもあまりベースカラーに向いていない色などもあるわけで、ツールは過信しすぎず、迷ったら無難な道に靡いていくべきだろうね。


サイトB:CSS3のbackground-size:cover;で背景画像をウィンドウにフィットさせる

最早サイトA、B、Cというカテゴリ分けはあまり意味を為していないけれど、当初のカテゴリ分け基準では、滞在感のあるサイトのノウハウをこのカテゴリに蓄積するはずだったので一応。

ブラウザのウィンドウをぴったりと埋める画像を背景として使い、その上にコンテンツを乗せるというやり方は、サイトに滞在感をもたせようと思ったときの常套手段だ。ただ、CSSのヴァージョンがまだ2.xであったころには、これをやるのがなかなか難しく、時にはJavaScriptやjQueryプラグインの力なども借りなければならなかった。

一方、CSS3に対応しているモダンなブラウザ環境を対象としたサイト作りであれば、こういったデザインも容易に実現できる。滞在感が表現できる上、ウィンドウに追随して大きさが変化するという特性からレスポンシブデザインとも相性が良い。

ウィンドウの大きさに追随する背景の指定

CSS3では、背景画像や色を指定するbackground系プロパティのひとつとして、background-sizeというプロパティが追加された。無指定の場合の初期値はautoだが、スペース区切りで値を2つ指定することで、それぞれ画像の幅と高さを決められる。たとえば指定要素に対して幅は50%、高さは200px固定というようにしたい場合の指定は以下のようになる。

background-size : 50% 200px;

また、特別な指定値としてcontainとcoverがある。containは背景画像として利用する画像をアスペクト比を保ったまま拡大・縮小し、クリッピングすることなく要素内に表示できる最大サイズにしてくれる。したがって、指定先の要素とアスペクト比が異なる場合には必ず画像の全体が表示された上で余白が生じる。

background-size : contain;

一方、coverは元画像のアスペクト比を保ちつつ拡大・縮小し、指定先要素の中に余白を生じることなく画像を表示する。したがって、アスペクト比が異なるのならば、画像は確実に一部分がクリッピングされる

background-size : cover;

したがって、ウィンドウサイズに合わせた背景画像を表示するためには、このbackground-size:cover;とmin-height:100%;をhtml要素に指定するという手順となる。

html{
min-height : 100%;
background-size : cover;
}

min-heightの指定が必要であるのは、内包するコンテンツのサイズによってhtml要素の縦の長さが変わるためだ。

ただ、background-size:cover;を指定する場合の背景画像は、抽象的画像や大写しの風景など見切れても不都合の無いものを選ぼう。人の顔などの場合には、見切れてしまうと大変格好悪い。
また、background-sizeプロパティはbackgroundショートハンド(省略指定)の対象となるので、background-sizeを指定した後あらためてbackgroundで指定しないようにする注意が必要である。


PHPテンプレートエンジンを使おう WordPress編

テンプレートエンジンの仕組みと、そのパラダイムについて何回かに分けて説明してきている(素のPHP編Smarty2.x編Smarty3.x編)。で、Smarty2.xのテンプレートエンジンのパラダイム、”閉じhead前切り”と同じく、テンプレートパーツを順番に呼び出していき最終的なhtmlとして成形するのが、かの有名なCMS、WordPressだ。WordPressのサイトがPHPプログラムからどのように生成されていくのか、入門編的な要素も絡めて解説しよう。

WordPressにおけるhtmlの切り分け方

WordPressにおけるテンプレートを確認するには、ダッシュボードの”外観”メニューから”テーマ編集”を選ぶ。初期画面で表示されているのはWordPressテーマのCSSだが、右側にある”テンプレート”と書かれたセクション、ここにあるのがテンプレートファイルということになる。ただし、このセクションはテンプレートファイルと呼び出し側のPHPスクリプトがごっちゃになっている状態(WordPressの場合、テンプレートファイルの拡張子も.phpになる)であり、また、”テーマ編集”で表示されるのは現在適用中のテーマのテンプレートファイルなので、テーマによって表示されている構成ファイルが異なるということに注意。とりあえず、本解説ではTwenty Twelveのものを基準として、どのテーマにも含まれそうなテンプレートファイルについて解説する。

テンプレートパーツ header.php

Smarty2.xの、”閉じhead前切り”では、html文書の頭の部分から、全ページ共通となる箇所を切り出してテンプレートを作成した。この頭からの部分に当たるのがheader.phpである。Smarty2.xの場合はこの部分に閉じheadタグを含めることができなかったが、これはmetaタグなどheadタグを閉じる前に挿入するページ固有の文言が必要な場合を鑑みてのことであった。
WordPressのheader.phpの場合は、呼び出し元のページ毎に必要な固有文言の挿入を、フィルターやアクションといった仕組みでキューに登録できる。登録した文言は、wp_head()というWordPress関数が呼ばれる際に一緒に呼ばれ挿入される仕組みになっている。したがって、head閉じタグの前で切らなくてもよい。大抵のテーマではヘッダー画像を内包して、mainやsidebarの直接の親要素の開始タグまでをまとめて、header.phpとしているようである(ソースコードの注釈にもそう書かれているのがわかるだろう)。

//Twenty Twelveのheader.php
 
<?php
/**
 * The Header template for our theme
 *
 * Displays all of the <head> section and everything up till <div id="main">
 *
 * @package WordPress
 * @subpackage Twenty_Twelve
 * @since Twenty Twelve 1.0
 */
?><!DOCTYPE html>
<!--[if IE 7]>
<html class="ie ie7" <?php language_attributes(); ?>>
<![endif]-->
<!--[if IE 8]>
<html class="ie ie8" <?php language_attributes(); ?>>
<![endif]-->
<!--[if !(IE 7) | !(IE 8)  ]><!-->
<html <?php language_attributes(); ?>>
<!--<![endif]-->
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>" />
.
.
.
<?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
<div id="page" class="hfeed site">
	<header id="masthead" class="site-header" role="banner">
.
.
.
	</header><!-- #masthead -->
 
	<div id="main" class="wrapper">

このように、header.phpというテンプレートの終わりはidがmainであるdivの開始タグとなっている。実際に表示されたページで見ると、以下の部分だ。

header.phpの実際の表示

header.phpの実際の表示

テンプレートパーツ sidebar.php

サイドバー部分のテンプレートがsidebar.phpになる。とは言っても、サイドバーに表示するものはウィジェットという形でユーザが自由に追加していく仕組みなので、テンプレートsidebar.phpの内容は大体dynamic_sidebar()というWordPress関数の呼び出し程度である。

sidebar.phpが担当する部分

sidebar.phpが担当する部分

テンプレートパーツ footer.php

footer.phpでは、ご想像の通りheader.phpで開いたid=”main”のdivの閉じタグから始まる。フッター部分のパーツ(クレジットなど)を表示し、bodyタグやhtmlタグも閉じる。bodyタグを閉じる前にwp_footer()という関数を呼んでいるが、これもまたこの位置で挿入されるスクリプト等のキューを参照、表示する。

//Twenty Twelveのsidebar.php
 
<?php
/**
 * The template for displaying the footer
 *
 * Contains footer content and the closing of the #main and #page div elements.
 *
 * @package WordPress
 * @subpackage Twenty_Twelve
 * @since Twenty Twelve 1.0
 */
?>
	</div><!-- #main .wrapper -->
	<footer id="colophon" role="contentinfo">
		<div class="site-info">
			<?php do_action( 'twentytwelve_credits' ); ?>
			<a href="<?php echo esc_url( __( 'http://wordpress.org/', 'twentytwelve' ) ); ?>" title="<?php esc_attr_e( 'Semantic Personal Publishing Platform', 'twentytwelve' ); ?>"><?php printf( __( 'Proudly powered by %s', 'twentytwelve' ), 'WordPress' ); ?></a>
		</div><!-- .site-info -->
	</footer><!-- #colophon -->
</div><!-- #page -->
 
<?php wp_footer(); ?>
</body>
</html>

実際に表示される部分としては、以下の部分。

footer.phpの担当部分

footer.phpの担当部分

WordPressにおけるテンプレートパーツの呼び出し方

この3種類の基本的なパーツを、個々のページファイルから呼び出して表示するわけである。ブログの表玄関である、index.phpでの呼び出され方を見てみよう。

//Twenty Twelveのindex.php
 
<?php
/**
 * The main template file
.
.
.
 */
 
get_header(); ?>
 
	<div id="primary" class="site-content">
		<div id="content" role="main">
		<?php if ( have_posts() ) : ?>
 
			<?php /* Start the Loop */ ?>
			<?php while ( have_posts() ) : the_post(); ?>
				<?php get_template_part( 'content', get_post_format() ); ?>
			<?php endwhile; ?>
 
			<?php twentytwelve_content_nav( 'nav-below' ); ?>
 
		<?php else : ?>
.
.
.
		<?php endif; // end have_posts() check ?>
 
		</div><!-- #content -->
	</div><!-- #primary -->

<?php get_sidebar(); ?>
<?php get_footer(); ?>

get_header()、get_sidebar()、get_footer()というWordPress関数で、それぞれheader.php、sidebar.php、footer.phpを呼び出して表示している。つまり最低限この3テンプレートと、3関数のコールがあればページの枠はできているのである。
あとは、get_header()とget_sidebar()の間に書かれている部分が、ページのメイン部分の内容になる。ここで、もう一種類のテンプレート読み込み関数、get_template_part()というものが登場している。

get_template_part()

get_template_part()という関数は、任意の名称のテンプレートを読み込む関数である。第1引数のみ指定すると(たとえば、”content”という文字列を与えよう)、第1引数.phpというテンプレートファイルを読み込む(例であれば、content.php)。第2引数を添えると(たとえば、”content”,”aside”という2つの文字列を与えよう)、第1引数-第2引数.phpというテンプレートファイルを読み込む(content-aside.php)。
上記のTwenty Twelveの例では、”content”とget_post_format()という2つの引数を与えている。get_post_formatは投稿のフォーマットを返してくるので、contentの後に投稿フォーマットのついた複数のテンプレートファイルにここで振り分けを行っているのだ。確かに、Twenty Twelveの構成テンプレート一覧の中に”content-XXX.php”というファイルが多数ある。

content.phpの一族

content.phpの一族

このようにして、いちいち投稿フォーマット毎にswitch文で分岐を作らなくても、適切なテンプレートを取得してくれるようになっているのである。

WordPressはテンプレートエンジン

このように、特定テンプレートパーツを呼び出すget_header()などの関数、あるいはファイル名を指定して呼び出すget_template_part()関数があるため、WordPressはテンプレートエンジンそのものであると言える。
肝心のプレースホルダについてはどうであるか。WordPressは投稿(Post)を単位として読み込み、投稿のタイトル、本文などの情報にWordPress関数(ゲッタメソッド)経由でアクセス・表示させる。たとえばタイトルと本文をテンプレート中で呼び出す場合には、以下のような具合になる。

<h1 class="title"><?php the_title(); ?></h1>
<div class="honbun"><?php the_content(); ?></div>

Smartyなどのプレースホルダと異なり、関数をそのまま実行という少し無骨な形ではあるが、WordPressサイトの制作に慣れたデザイナー・コーダであれば、大体どの関数が何を出力しているか理解するし、またこの出力部分以外のデザインを変更すればよいと理解するのである。

ということで、WordPressでのサイト制作が現在主流となっているのは、そのCMS機能に期待したものではなく、デファクトスタンダードのテンプレートエンジンとしての需要であるという見方も可能である筈。


WordPress Popular Postsが3.0になり実用に耐えるようになったというか

ブログのサイドバーなどによくある、「人気記事」といったような内容のリンク集を作るためのプラグイン。WordPress Popular Postsというものがあるのだけれど、このプラグインはとりあえずWordPressサイトを立ち上げたら必ずインストールしておく程度には重宝している。
勿論このブログにもインストールしてある。「人気記事」的リンク集は表示していないのに、このプラグインの意味があるのかという疑問が湧くかもしれないが、このプラグインをインストールしておくと、設定ページで各ページ毎のアクセス数を24時間単位、1週間単位、1ヶ月単位、累積といったように記録・表示してくれる。したがって、現在ブログでホットエントリになっている記事はどれなのかとか、それがここ1ヶ月の傾向と比べてどうなのかとかのチェックが一発で行える。
そういったことは、Google Analyticsでもインストールしてチェックすれば良いのではと思われるかもしれないが、URLとアクセス数の組ではなく、標準で記事名とアクセス数を突き合わせて表示してくれるというところが大変便利なのだ。

これまでのWordPress Popular Postsの数字は不正確

ただ、ブログの運営期間が長くなってくると、どうしてもスパムコメントが付き易くなる。そしてこれまでのWordPress Popular Postsでは、特定記事に向けてスパムコメントをつけにくるbotや、検索エンジンのbotなどもアクセス数としてカウントしてくれてしまっていた。したがって、明らかにあり得ない数の閲覧数になっている記事が出てきてしまい、そうした”ホットエントリ”以外の閲覧傾向を捉える程度にしか使えなかった。ましてや、人気記事表示機能も使うのであれば、スパムが多くついた記事が人気記事として居座ってしまっていたことだろう。使用に耐えないという評価をして外してしまった人も多いに違いない。

一見人気サイトに見える

一見人気サイトに見える

WordPress Popular Posts 3.0で、bot detectionがついた

数日程前に、これまでの2.xから3.xへとヴァージョンアップが行われたのだが、そこで新機能としてbot detectionがついた。これにより、おそらく正確であろうと思われる、現実的な数字が表示されるようになった。知らずにアップデートすると、急にアクセス数が減ったように見えて大変驚く。

やった!過疎サイトに

やった!過疎サイトに

そのため、botのカウントが嫌で一度アンインストールしてしまった人も、入れ直して良いタイミングではないかというお知らせでした。


Conceptual:WEBサービスページの共通部分をtwigテンプレート化してみる

twigの実践編というわけでもないが、これまでConceptualのページに載せていたWEBサービスページの共通部分(ヘッダー、広告、フッターなど)をtwigテンプレートとして抽出する作業を行ったので、その過程など紹介してみようと思う。

共通部分の抽出

公開しているプログラムのうち、OnlyCSViewEXIFviewerについてはページ構造がほぼ同じだ。現在の2プログラムの画面を並べてみると、下の画像のような感じになる。共通部分と書いてある所はほぼソースが同じであり、真ん中の白い部分(コントローラ部分)とスクリプトの部分だけが違うということになる。

共通部分を持つ2ページ

共通部分を持つ2ページ

テンプレートエンジンで共通部分を抽出する場合、Smarty2.xのような”閉じhead前切り”パラダイムのものだと、ヘッダー部分、メイン部分、フッター部分と必要ファイルが3つになってしまうだろう。今回はテンプレートの継承機能のあるTwigを用いるため、ヘッダーとフッターを含んだ基礎部分のテンプレート(base.html.twig)にメイン部分に対応するブロックを作り、子テンプレートでブロック部分をオーバーライドするという、2ファイルでの解決を図ることにする。

ロジックとビューの分離

加えて、今回手を入れるプログラムは相当昔に作ったものなので、PHP入門書の最初のサンプルにありがちな、ロジックとビューが混在している状態になっている。テンプレートエンジンらしく、これの分離も同時に行ってみたい。

EXIFviewer手入れ前のソース

ロジックとビューの分離の説明に丁度良かったので、今回例として出すのはEXIFviewerの方にする。

 
//手入れ前index.php(1ファイル)
 
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="Description" content="EXIFviewerはローカルのJPEGファイルをアップロードすることなくサイズ、位置情報、撮影情報等のEXIFデータを確認できるWEBサービスです。">
<meta name="Keywords" content="PHP,EXIF,WEBサービス,JPEG,画像,位置情報">
<link rel="stylesheet" href="base.css" type="text/css">
.
.
<script>
$(function(){
$("#filesel").change(function(){
var reader = new FileReader();
reader.readAsDataURL(document.getElementById("filesel").files[0]);
reader.onload = function(e){
$("#path").val(e.target.result);
}
if(window.createObjectURL){
var ourl = window.createObjectURL(document.getElementById("filesel").files[0]);
} else if(window.URL) {
var ourl = window.URL.createObjectURL(document.getElementById("filesel").files[0]);
} else {
var ourl = window.webkitURL.createObjectURL(document.getElementById("filesel").files[0]);
}
$("#viewer").empty().html('<img src="' + ourl + '">');
$("#blob").val(ourl);
});//end of fileselchange
 
//Googlemap関連コード(省略)
 
});//end of jQueryready
</script>
<title>EXIFviewer JPEGファイルのEXIF情報をチェックするWEBサービス</title>
.
.
</head>
<body>
<div id="main">
	<nav><div id="back"><a href="http://akisi.tabiyaku.net/" title="AkisiのWEB制作日記に戻る">AkisiのWEB制作日記に戻る</a></div></nav>
<div id="upperad">
<script type="text/javascript">
 
//adsenseコード
 
</script>
</div>
<div id="instruction">
<h1>セットした画像のEXIFデータを表示</h1>
</div>
<div id="controlls">
<form>
<p><input type="file" id="filesel" type="image/jpeg"></p>
</form>
<form action="index.php" method="post">
<div id = "viewer">
<?php
if(!empty($_POST["path"])){
echo '<img src="' . $_POST["path"] . '">';
}
?>
</div>
<input type="hidden" name="blob" id ="blob">
<input type="hidden" name="path" id ="path">
<p><input type="submit" value="表示" class="btn btn-primary"></p>
</form>
</div>
<div id="tablearea">
<?php
if(!empty($_POST["blob"])){
if($exif = exif_read_data($_POST["blob"],NULL,true)){
echo "<h2>EXIF情報</h2>";
echo "<table class='exiftable table table-striped'><thead><tr><th>キー名</th><th>値</th></tr></thead><tbody>";
foreach ($exif as $key => $section) {
    foreach ($section as $name => $val) {
	if(is_array($val)){
	foreach ($val as $valname => $vval) {
	echo "<tr><td>" . htmlentities($key,ENT_QUOTES,"UTF-8") . ' ' . htmlentities($name,ENT_QUOTES,"UTF-8") . ' ' .  htmlentities($valname,ENT_QUOTES,"UTF-8") . "</td><td id=" . '"' . htmlentities($name . $valname,ENT_QUOTES,"UTF-8") . '">' . "$vval</td></tr>";
	}
	} else {
	echo "<tr><td>" . htmlentities($key,ENT_QUOTES,"UTF-8") . ' ' . htmlentities($name,ENT_QUOTES,"UTF-8") . "</td><td id=" . '"' . htmlentities($name,ENT_QUOTES,"UTF-8") . '">' . htmlentities($val,ENT_QUOTES,"UTF-8") . "</td></tr>";
	}
    }
}
echo "</tbody></table>";
} else {
echo "画像のEXIFデータが見つかりませんでした。";
}
} else {
echo "画像のEXIFデータが見つかりませんでした。";
}
?>
</div>
<div id="googlemapdivarea">
<div id="googlemapdiv"></div>
</div>
<div id="lowerad">
<div class="loweradbox">
<script>
 
//adsenseコード
 
</script>
</div>
<div class="loweradbox">
<script>
 
//adsenseコード
 
</script>
</div>
</div>
</div>
</body>
</html>

いわゆる、初回アクセス時にはPOSTの値(”blob”と”path”)が空であるため例外処理として初期画面を吐いているタイプのページだ。1ファイルでまとまってくれているので扱い易いが、echo命令によってテーブルを構成するhtmlタグの細切れを吐いている部分は一見して訳が分からない。html修正時にミスも起こり易いだろう。

EXIFviewer手入れ後のソース

index.phpにはテンプレートの呼び出しとロジックを押し込み、base.html.twigとindex.html.twigの2ファイルに値だけ渡す

 
//index.php
 
require_once("Twig/Autoloader.php");
Twig_Autoloader::register();
$loader = new Twig_Loader_Filesystem("twigtmp/");
$twig = new Twig_Environment($loader, array("cache" => "cache/"));
$blob = "";
$exif = array();
if(isset($_POST["blob"])){
$blob = $_POST["blob"];
}
if(!empty($_POST["path"])){
$exif = exif_read_data($_POST["path"],NULL,true);
}
$page = array(
"description" => "EXIFviewerはローカルのJPEGファイルをアップロードすることなくサイズ、位置情報、撮影情報等のEXIFデータを確認できるWEBサービスです。",
"keywords" => "PHP,EXIF,WEBサービス,JPEG,画像,位置情報",
"title" => "EXIFviewer JPEGファイルのEXIF情報をチェックするWEBサービス",
"blob" => $blob,
"exif" => $exif
);
$template = $twig->loadTemplate("index.html.twig");
$template->display($page);

続いて呼ばれる側のテンプレート2ファイル(実際名指しされるのはindex.html.twigの方だけだけれど)。

 
<!-- base.html.twig -->
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="css/base.css" type="text/css">
{% if description is not empty %}
<meta name="description" content="{{ description }}">
{% else %}
<meta name="description" content="AkisiのWEB制作日記の提供するウェブサービス">
{% endif %}
{% if keywords is not empty %}
<meta name="keywords" content="{{ keywords }}">
{% else %}
<meta name="keywords" content="ウェブサービス,WEB">
{% endif %}
.
.
<title>{{ title }}</title>
{% block head %}
{% endblock head %}
.
.
</head>
<body>
<div id="main">
	<nav><div id="back"><a href="http://akisi.tabiyaku.net/" title="AkisiのWEB制作日記に戻る">AkisiのWEB制作日記に戻る</a></div></nav>
<div id="upperad">
<script>
 
//adsenseコード
 
</script>
</div>
{% block main %}
{% endblock main %}
<div id="lowerad">
<div class="loweradbox">
<script>
 
//adsenseコード
 
</script>
</div>
<div class="loweradbox">
<script>
 
//adsenseコード
 
</script>
</div>
</div>
</div>
</body>
</html>
 
<!-- index.html.twig -->
{% extends "base.html.twig" %}
{% block head %}
<link rel="stylesheet" href="css/index.css" type="text/css">
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
<script type="text/javascript">
$(function(){
 
$("#filesel").change(function(){
var reader = new FileReader();
reader.readAsDataURL(document.getElementById("filesel").files[0]);
reader.onload = function(e){
$("#path").val(e.target.result);
}
if(window.createObjectURL){
var ourl = window.createObjectURL(document.getElementById("filesel").files[0]);
} else if(window.URL) {
var ourl = window.URL.createObjectURL(document.getElementById("filesel").files[0]);
} else {
var ourl = window.webkitURL.createObjectURL(document.getElementById("filesel").files[0]);
}
$("#viewer").empty().html('<img src="' + ourl + '">');
$("#blob").val(ourl);
});//end of fileselchange
 
//Googlemap関連コード(省略)
 
});//end of jQueryready
</script>
{% endblock head %}
{% block main %}
<div id="instruction">
<h1>セットした画像のEXIFデータを表示</h1>
</div>
<div id="controlls">
<form>
<p><input type="file" id="filesel" type="image/jpeg"></p>
</form>
<form action="index.php" method="post">
<div id = "viewer">
{% if blob is not empty %}
<img src="{{ blob }}">
{% endif %}
</div>
<input type="hidden" name="blob" id ="blob">
<input type="hidden" name="path" id ="path">
<p>
<span><input type="submit" value="表示" class="btn btn-primary"></span>
</p>
</form>
</div>
<div id="tablearea">
{% if exif is not empty %}
<h2>EXIF情報</h2>
<table class='exiftable table table-striped'><thead><tr><th>キー名</th><th></th></tr></thead><tbody>
{% for key, section in exif %}
{% for name, val in section %}
{% if val is iterable %}
{% for valname, vval in val %}
<tr><td>{{ key }} {{ name }} {{ valname }}</td><td id="{{ name }}{{ valname }}">{{ vval }}</td></tr>
{% endfor %}
{% else %}
<tr><td>{{ key }} {{ name }}</td><td id="{{ name }}">{{ val }}</td></tr>
{% endif %}
{% endfor %}
{% endfor %}
</tbody></table>
{% else %}
画像のEXIFデータが見つかりませんでした。
{% endif %}
</div>
<div id="googlemapdivarea">
<div id="googlemapdiv"></div>
</div>
{% endblock main %}

テンプレートのcssは、共通部分のためのbase.cssと、index.cssに分ける。手入れ前のソースでは色付きハイライトされているPHP部分がなくなり、その代わりにtwigのfor inで配列を回している。テンプレート関係で新しく登場したのは、for inで回す際にキー値にもアクセスする場合の、{% for key, value in array %}という書き方。それから、配列に要素があるかどうか調べるのには{% if array is iterable %}のように書く、といったことくらいだろうか。
テンプレートの使い分け判断に使う値が変数の中にあるようであれば(あるいは変数自身の存在で判断できるのであれば)変数のみをそのままテンプレートに渡し、そうでないようならロジック側で切り替え判断のためのフラグを値で用意し、変数とフラグを合わせてテンプレートに渡す、このようにしてロジックとビューの分離をはかるわけである。

あとは、テンプレート中のJavaScriptに波括弧が含まれる場合{% raw %}{% endraw %}で囲むのを忘れずに。

TwigをLinuxの公開サーバに上げる段になってハマり易いこと

Twigで個人的にハマったこと。MAMPなどのローカル環境で動作したプログラムを公開サーバにアップロードしてみると、以下のようなエラーが出て動かないということがあった。

Class ‘Twig_Loader_Filesystem’ not found

原因は、ざっくり言ってしまうとLinuxが大文字小文字の扱いにうるさいからだった。たとえばAutoloader.phpへのパスが”twig/Twig/Autoloader.php”だった場合、”Twig/Twig/Autoloader.php”というように指定してしまうと、OSXやWindowsなどの環境では理解してくれるものの、Linuxサーバ上では理解をしてくれない。
また、Autoloader.phpの上位のディレクトリ名も、”Twig”でないといけない。思う所あって”twig”というように小文字のディレクトリにしてアップロードしていたから、”twig/Autoloader.php”という正しいパスを指定してもエラーが出てしまっていることに当惑した。”Twig”ディレクトリは勝手にリネームしてはいけない。それが今回の失敗の教訓。


PHPテンプレートエンジンを使おう Twig編

PHPテンプレートエンジンについて解説・紹介していく企画、素のPHPSmarty2.xSmarty3.xと紹介してきて、満を持してのTwig登場である。

PHPテンプレートエンジンTwigとは

そもそも、Twigとは何なのだろうか。疑問を解決するためにお手元の検索エンジンで検索しても、小枝の画像ばっかり出てくる(笑)。Twigとは、フランスのSensioLabsというソフトウェア会社にスポンサードされている、オープンソースのWEBアプリケーションフレームワークSymfonyを構成する標準バンドルパッケージの一つである(Symfony公式サイトTwig公式サイト)。
WEBアプリケーションフレームワークという言葉が出てきたが、WEBアプリケーションを作るための”概して便利な”枠組みのことで、Symfonyは企業サイトやらAPIやらのWEB上に展開するサービスを作成する際に、ヴァージョンの管理やライブラリの管理、データ構造など様々なことを、MVCの考え方に基づいて整理・管理してくれる仕組みだ。
MVCにのっとっているため、Symfonyは多人数が関わるプロジェクトなどにおいて強みを発揮する。逆に言うと、小規模で作ったら作りっぱなしのようなプロジェクトに導入する意義はそれほど無い。でもそうした小規模プロジェクトでも、Symfonyの特定機能だけ利用したいという需要があるかもしれないので、Symfonyでは機能の多くをモジュール化しており、Symfony本体を導入しなくてもかいつまんで使えるようになっている。そのモジュールの一つが、PHPテンプレートエンジンのTwigというわけだ。

Twigの利用方法

Twig単体で利用する場合、先程紹介したTwig公式サイトより本体を落としてきて、libディレクトリ内のTwigディレクトリを適当な場所に配置する。あとはキャッシュ用のディレクトリと、テンプレートファイル格納用のディレクトリを必要に応じて作っておけばよい。なおインストールにはcomposerというSymfonyのモジュールを使う方法もあり、ファイルの依存関係を管理するというcomposer自体の機能も使いたい場合には、一緒に導入しておいても良いだろう。
で、ひとまずcomposerを使用しないインストールが済んだとして、呼び出し側のPHPスクリプトではこういった感じに書く。Smarty2.xの説明で出した例と同じことをやっているので、適宜比較などしてほしい。

//index.php
 
require_once("Twig/Autoloader.php");
Twig_Autoloader::register();
$loader = new Twig_Loader_Filesystem("twigtmp/");
$twig = new Twig_Environment($loader, array("cache" => "cache/", "debug" => false));
$template = $twig->loadTemplate("default.html.twig");
$template->display(array("title" => "トップページ"));

まず、先程のTwigディレクトリにあるAutoloader.phpを読み込む。これは関連ファイルを全て読み込むためのクラスで、Twig_Autoloader::registerメソッドを呼ぶ。
次に$loaderに入れているのはテンプレートをロードするクラスのインスタンスで、Twig_Loader_Filesystemの場合コンストラクタにテンプレートファイルのあるディレクトリへのパスを引数として与える。ファイルを読み込むのでなくインラインのStringでテンプレートを用意する場合はTwig_Loader_Stringクラスなど、いくつか種類があるのだが、とりあえずTwig_Loader_Filesystemだけ覚えておけば良い。

次のTwig_Environmentというクラスのインスタンス作成時に、$loaderと配列にした各種設定を渡している。”cache”はキャッシュを入れるディレクトリへのパス、”debug”はデバッグの有無など。キャッシュを使わずデバッグモードもオフならば、第一引数のみで良い。

$templateはTwig_environmentのメソッドであるLoadTemplate()で読み込むテンプレートファイル名を与える。そして$templateのdisplay()メソッドの引数に、変数の値をラベル付き配列で与える。これで標準出力へと出力されることになる。
テンプレート側の記述はこのように。変数の先頭に$をつけず、二重波括弧({{}})で変数名を囲む。

//default.html.twigの記述
 
<!doctype html>
<html lang="ja">
<head>
<title>{{ title }} | サンプル株式会社</title>
</head>
<body>
.
.
</body>
</html>

商品一覧ページをTwigで作ってみる

それでは、Smarty3.xを用いて作った商品一覧ページを、Twigで作り直してみよう。まずはテンプレートファイル側。

 
<!-- base.html.twig -->
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="css/base.css" type="text/css">
{% if description is not empty %}
<meta name="description" content="{{ description }}">
{% else %}
<meta name="description" content="サンプル株式会社のホームページです">
{% endif %}
{% if keywords is not empty %}
<meta name="keywords" content="{{ keywords }}">
{% else %}
<meta name="keywords" content="サンプル株式会社,小売業,日本">
{% endif %}
<title>{{ title }} | サンプル株式会社</title>
{% block head %}
{% endblock head %}
</head>
<body>
<div id="wrapper">
<header>
<h1>サンプル株式会社</h1>
</header>
<div id="main">
{% block main %}
<article>
<h1>デフォルト文章</h1>
<p>mainのブロックに指定が無い場合のデフォルト文章です。</p>
.
.
</article>
{% endblock main %}
</div>
<footer>
<p>サンプル株式会社 2012-{{ "now"|date("Y") }} all rights reserved.</p>
</footer>
</div>
</body>
</html>
 
<!-- item.html.twig -->
<li>
<section>
<h1>{{ value.name }}</h1>
<p>{{ value.description }}</p>
<p>{{ value.price|number_format(0) }}円</p>
<p><a href="http://xxxxxxxx.xxx/item.php?id={{ value.id }}" title="{{ value.name }}の商品詳細">>>商品詳細へ</a></p>
</section>
</li>
 
<!-- itemlist.html.twig -->
{% extends "base.html.twig" %}
{% block main %}
<article>
<h1>サンプル株式会社の商品一覧</h1>
{% if items is empty %}
<p>現在取り扱っている商品はありません</p>
{% else %}
<p>当社が取り扱う商品の一覧です。</p>
<ul id="itemlist">
{% for value in items %}
{% include "item.html.twig" %}
{% endfor %}
</ul>
{% endif %}
</article>
{% endblock main %}

テンプレートファイル側でのSmartyとの違いは、プレースホルダの書き方は先程説明の通り。ifなどの制御構造は括弧だけでなく、end〜という閉じタグになるところや、foreachの書き方がfor inになるところ、blockのname=でなく直接block名指定になるところ、比較演算子がis notのような自然言語的な書き方になることなど、細かい違いが結構あると言える。配列のラベルへのアクセスは同じだが、Twigの場合value[“name”]のようなアクセスも可能。そして、オブジェクトのメンバへのアクセスも同じ書き方で出来る。
修飾子が”|”の後に続くのも同じ。あとは、オシャレなマニュアルでチェックしてほしい。

呼び出し側はこのように。

//itemlist.php
 
require_once("Twig/Autoloader.php");
Twig_Autoloader::register();
$loader = new Twig_Loader_Filesystem("twigtmp/");
$twig = new Twig_Environment($loader, array("cache" => "cache/", "debug" => false));
$items = array();
 
//例の如く、下記の配列が帰ってきたと仮定
 
$items = array(
array("id" => "1","name" =>"商品A","description" => "さわやかなミントの香りの牛車です(牛は食べられません)","price" => "100000"),
array("id" => "2","name" =>"商品B","description" => "官位です。これがあれば思いのまま(返品不可)","price" => "3000"),
array("id" => "10","name" =>"商品C","description" => "古今和歌集です。新版が出たので在庫一掃セールです","price" => "16000")
);
$page = array(
"description" => "サンプル株式会社の商品一覧ページです。",
"keywords" => "サンプル株式会社,ネットショップ",
"title" => "当社商品の一覧",
"items" => $items
);
$template = $twig->loadTemplate("itemlist.html.twig");
$template->display($page);

まあ既に説明した内容である。displayメソッドに持たせる引数が標準で配列であるため、テンプレート側からそのままラベルのみでアクセスしていることに注意。
あと、この例だとキャッシュを有効化しているので、いじってテストなどする際には、結果確認の前にキャッシュディレクトリの中身を廃棄しておこう(これをわざわざやらなくて良いためにデバッグモードがあるらしい。先程知った)。

SmartyとTwigはどちらが良いのか

以上、Twigについても見てきた。仕組みはそれほどSmartyと変わらないので、乗り換えは楽だろう。ただ、乗り換え後過去資産を活用しようとすると変換は面倒臭いので、そこはあまり期待しない方が良い。
良く言われるSmarty3.xとTwigの速度面の差だが、確かに体感などTwigの方がキビキビ動いている印象がある(Smarty2.xには劣るけど)。速度比較については厳密度は分からないけれど、こんなデータもある。
あとはマニュアルのオシャレさなんかを基準にどちらを使うか決めれば良いだろう。


WordPressテーマ変更に伴う記事のルート見出しレベルの変動を吸収するプラグイン WP hn Convert

随分前に愚痴った、WordPressのテーマを変更すると記事のルート見出しレベルが変動し、過去記事資産が活かせないという問題。この問題がまだ根本的解決に至っておらず、古いWordPressサイトを新しいテーマに移行する際には常に悩まされている。
公式がTwenty Twelveを出してきて以降は、テーマ制作者の暗黙の了解として記事タイトルがh1、記事内で最もレベルの高い見出しがh2というように揃えてきているように思う。これは記事に対してHTML5のセマンティックを結びつける場合、記事部分で新しくセクショニングコンテンツ(article)を始めるというのが合理的だからであろう。あるいは、SEO対応を売りにするWordPressテーマなどでは、single.phpにおける最初のh1を記事タイトルに充てているため、不都合なく記事資産を共有できるようになっているのである。

the_content()をラッピングする方法

一方、記事を出力するタイミングで見出しにテキスト処理を噛ませるという方法があり、こちらはWP hn Convertというプラグインを配布されている方がいる
このプラグインを導入し、テーマ中のthe_content()の記述部分を全てthe_hn_converted_content(数字)とすると、数字で指定した分だけ見出しレベルを移動してくれる。たとえば記事タイトルをh2にして書かれたテーマをh1からにするには、the_hn_converted_content(-1)のようにすると、以下の見出しh3をh2、h4をh3というように全体的な見出しレベルに変動を与えてくれるのである。

でも根本的な解決ではない

このプラグインで対処することも可能なのだが、そうすると記事を書く側としてはルート見出しレベルをこれまで通りのレベルにし続けなければならない。
また、試していないので何とも言えないのだが、たとえばコードスニペット中に書かれたhtmlの見出しレベルにも適用されてしまうのではという危惧もある。通常の日記ブログなどでは問題が無いだろうが、技術系のブログにとっては大きな問題となってしまうのではないか。
ということで、上手い解決法がないかと依然悩んでいる。データベースを直接たたいて置換というのも、怖い話だなあ。