プログラマー幸福論

プログラム関係の雑記やcodicの開発日誌

lessphp と mod_rewrite を使って快適なless開発環境を作る

f:id:namba0219:20130701095653j:plain

Photo by [ changó ] via photopin

たまには、開発系のことも書きたくなったので、今開発で使っている lessphp のことについて書いてみます。今回は、lessphp という less の PHP版と mod_rewriteを連携してみました。

1. Less とは

Lessは、CSSの拡張構文を使えるようになるCSSの拡張メタ言語です。ネストや変数、ミックスインなどが使えるようになるので、効率的にCSSを記述、そして管理できるようになります。必ずlessで記述しなければならないというわけではく、CSSの記述も普通に使えるので(一部を除き)、lessの記述をしたい場合だけless構文を使うといったボトムアップのアプローチができるのもいいところです。

以下は、lessの構文の一例です。詳しく知りたい方は、むゆうさんの記事などで詳しく解説されていますので、そちらを見てください。

/* 静的なインクルード */
@import "common.less";
/* mixinの定義 (関数のようなもの)*/
.box-shadow (@args...) {
    -webkit-box-shadow: @args;
	   -moz-box-shadow: @args;
	        box-shadow: @args;
}
// “//”もコメント行になります
/* 変数が定義できます */
@base-width : 200px;
.header {
  /* ネストにできます */
  .navbar {
    /* 計算式が使えるようになります */
    width: @base-width – 10px;
    /* mixinの展開 */
    .box-shadow(1px 1px 1px #ccc);
  }
} 

コンパイルがめんどい

ただ、cssを更新する度にコマンドラインから変換するのは、めんどいので mod_rewriteを使って動的にコンパイルするような仕掛けを考えてみます。

本家の less はjavascript で実装されており、サーバーサイドで実行するには、node.js が必要とのこと、node.js をインストールするのも手間だったので、lessをPHPで実装した lessphp を使うことにしました。

2. lessphpとmod_rewrite

xxx.css へリクエストがあったら、内部的に xxx.less を CSSにコンパイルして、出力するようにしたら、CSS編集してもブラウザのリロードだけで更新されるよね?ってのがヤリたい。

まず、mod_rewrite を使い/css/ 以下のリクエストをPHPファイルで横取りして、CSSファイルを動的に生成します。毎回コンパイルすると重いので、ファイルのタイムスタンプを比較して、.lessファイルが更新されている場合のみコンパイルして出力するようにします。

本番サイトなどでは、動的コンパイルが負荷となる可能性があるので、動的コンパイルは使用せず、開発環境で変換されたCSSファイルをアップする方針とします。

ディレクトリ構成はこんな感じです。あくまで例ですので、それぞれの環境に合わせて変更してください。lessphpは、こちらのサイトからダウンロードして解凍、lib ディレクトリに入れてください。

ディレクトリ構成

[+] htdocs
|- [+] lib
| - [+] lessphp ※ ダウンロードして解凍したもの
| - lessc.inc.php
|- [+] less
| - sample.less  ※ lessファイルを格納
|- [+] css 
| - sample.css   ※ cssファイルが生成される
|- .htaccess 
|- _lessc.php
|- sample.html 

-- .htaccess

/css/以下のリクエストを横取りし、_lessc.php を呼び出します。

RewriteEngine On
RewriteBase /
RewriteRule ^css/(.*\.css)$ /_lessc.php?src=$1 [L]

 

-- _lessc.php

lessphp のライブラリは、キャッシュする仕組みを持たないので、ファイルのタイムスタンプを見て、更新されていたらコンパイルし、更新されていなかったらキャッシュ(生成されたCSS)を返すようにします。

<?php
require "lib/lessphp/lessc.inc.php";
define('LESS_DIR', dirname(__FILE__) . '/less');
define('CSS_DIR', dirname(__FILE__).'/css');

$path = CSS_DIR . '/' . $_GET['src'];
$forcedCompile = (strpos(@$_SERVER["HTTP_REFERER"], 'lessc=on') !== false);
$lessFile = LESS_DIR . '/' . preg_replace('/([^\/]+)\.css$/', '\1.less', $_GET['src']);

// lessファイルがなくてcssファイルがあればcssファイルを出力
if (!$forcedCompile && !file_exists($lessFile) && file_exists($path)) {
header('Content-Type: text/css'); readfile($path); exit; // lessもcssもなければエラー } else if (!file_exists($lessFile)) { header('Content-Type: text/css'); echo "/* ERROR : Less not found */"; exit; } // lessファイルとcssファイルのタイムスタンプを比較 // 更新されていればコンパイル $ts = filemtime($lessFile); if ($forcedCompile || !file_exists($path) || $ts !== filemtime($path)) {
$lessc = new Lessc(); //$lessc->setFormatter('compressed'); // 圧縮する場合はコメントを外す $lessc->compileFile($lessFile, $path); touch($path, $ts); } header('Content-Type: text/css'); readfile($path); 

強制コンパイル

上記のPHPだと、1つ問題があって、less ファイルが import しているフlessァイルが更新された場合、import先のファイルが更新されているかチェックしていないので、コンパイルが走りません。そういった時のために、以下のようにlessc=on のパラメータを設定すると、強制コンパイルが走るように仕掛けが入っています。なぜかcssファイルが更新されないといった現象が起きたらためしてみてください。

/sample.html?lessc=on

3. まとめ

冒頭でも書きましたが、lessは CSS の記述も使えるので、CSSの記述めんどくさいなぁーと思ったときに less の拡張記述を使ってみるという感じで、ボトムアップ的にcssを改善していくことができます。なので、ちょっとずつ lessに慣れていくという意味でも、暇なときに環境だけでも作ってみるといいと思います。

less以外の選択肢や他の構成パターンもあるので、いろいろやってみると面白いと思います。一度lessやsassを覚えるとやめつきになりますよw。そういえば、phpsass というのもありますね。これも機会があれば試してみたいと思います。