カテゴリー別アーカイブ: 汎用テク覚え書き

clearfixのささやかな覚え書き

複数カラムのブログテンプレートなどを作成するとき、一般的にはCSSのfloatプロパティを使ってレイアウトを行います。たとえば2カラムで左にサイドメニュー、右に記事本文というレイアウトは、以下のようなhtml&CSSになるでしょう。

HTML部
<div class="wrapper">
<div class="sidebar">
//サイドメニューエリア本文
</div>
<div class="articlearea">
//記事本文
</div>
</div>
 
CSS
.sidebar{
float : left;
}
.articlearea{
float : right;
}

カラムを包括する親ボックスの大きさが指定されており、各カラムが親ボックスの高さの値を超えないときには、特にレイアウト上の問題は起こりません。しかしながら、ブログのようにカラムの本文が可変長で親ボックスの高さを超えるケースが考えられるときには、カラムが親ボックスをはみ出て表示されてしまい、レイアウトが崩壊してしまいます。

はみ出たカラム

これは、ボックス内でfloat指定されている要素の大きさが親ボックスの大きさを決める際に参照されないという特性によるものです。したがって最も仕様に適った解決は、floatさせた要素の後にclearプロパティでfloat解除した要素を設けることです。空のdiv要素やvisibilityプロパティをhiddenにしたhr要素、br要素がよく使われます。

HTML部
<div class="wrapper">
<div class="sidebar">
//サイドメニューエリア本文
</div>
<div class="articlearea">
//記事本文
</div>
 
//空divの場合
<div style="clear : both"></div>
 
//非表示にしたhrまたはbr
<hr class="clearfix" />
<br class="clearfix" />
</div>
 
CSS
.sidebar{
float : left;
}
.articlearea{
float : right;
}
.clearfix{
visibility : hidden;
clear : both;
}

この方法はシンプルですが、ページのセマンティック(意味)としてはデザイン修正の為だけの無意味な要素をhtmlに加える事になるので好ましくありません。
そこで、無駄な要素を加えることなくレイアウト修正を行う、名前もそのまま”clearfix”というテクニックが開発されました。

HTML部
<div class="wrapper">
<div class="sidebar">
//サイドメニューエリア本文
</div>
<div class="articlearea">
//記事本文
</div>
</div>
 
CSS
//clearfixの追加部分
.wrapper:after{
content : "";
display : block;
clear : both;
}
//IE6,7用
.wrapper{
zoom : 1;
}
 
.sidebar{
float : left;
}
.articlearea{
float : right;
}

CSSのafter疑似要素を使って、floatを解除する要素を無理矢理埋め込むという形です。この方法ならば、検索エンジンやアプリケーションがページのhtmlのみを取得する場合に、解釈の混乱をひきおこす無駄な要素が発生しません。IE用の追加部分については、独自仕様のhasLayoutというプロパティがtrueであれば子要素のfloatを親ボックスのサイズ計算に含めるという特性を利用しています。無害なレイアウト指定(zoom:1;やwidth:100%;など)であれば何でも良いのですが、ソースの意図を明確にする為にzoom:1;の指定をしている場合が多いようです。

ここまでがclearfixの説明です。先に述べた通り、floatを解除する要素を最後に加えるという解決方法ですね。

それに対して、親ボックスのoverflowプロパティを明示的にhiddenと指定することで、親ボックスのサイズ計算方法を変えるというテクニックもあります。overflowプロパティとは、ボックス内の要素がボックスのサイズを超えてしまった際の表示方法についての指定で、何も指定しない状態はvisible、つまりはみ出た部分も表示する指定になります。これをhiddenと指定した場合、はみ出す部分のみ非表示となると同時に、ボックスの高さの計算式にfloatさせた要素も含まれるようになります。

HTML部
<div class="wrapper">
<div class="sidebar">
//サイドメニューエリア本文
</div>
<div class="articlearea">
//記事本文
</div>
</div>
 
CSS
.wrapper{
overflow : hidden;
//IE6,7用
zoom : 1;
}
.sidebar{
float : left;
}
.articlearea{
float : right;
}

場当たり的な対応ではありますが、セマンティックを汚染せずさらに簡単なので、こちらをバカの一つ覚えのように使ってしまって良いかもしれません。


PHPでシンプルな文字列検索をする

PCREの場合globの場合とPHPのパターン検索方法を挙げましたが、単純に文字列中で特定文字列に完全一致する部分を探すのであれば、strpos関数やstrstr関数を使用すれば事足ります。
strpos関数は文字列中の検索文字列が最初に出現する場所のインデックスを返します。さらに一致する箇所が無い場合にFALSEを返すので、文字列中に検索文字列が存在するか/しないかを調べるPHPの関数も、実はstrposです。

<?php
$string = "abcdefgh";
$search = "def";
if(($pos = strpos($string,$search)) === FALSE){
echo "検索文字列と一致する箇所は無い";
}
else {
echo ($pos + 1)."文字目から一致";
}
?>

引数1が検索対象、引数2が探す文字列、オプションの引数3は探し始める位置です。比較演算子に===を使うのは、FALSE以外のときでも返り値で0を返す可能性があるからですね。なおこのstrposのヴァリエーションとして、大文字小文字の区別をしないstripos、最後に現れる位置を調べるstrrposなどがあります。

strstr関数は、検索文字列以降の文字列を返り値として得る際に使います。

strstr(引数1,引数2,引数3);
引数1,2strposに同じ。
引数3 検索文字列より前の部分を返すフラグ。オプション。

大文字小文字の区別をしないstristrというヴァリエーションもあります。

両方とも使いたいときには名前が出てこない影の薄い関数なので、メモっておきました。


PHPのもう一つのパターンマッチング関数glob

PHP4.3.0以降搭載されているglob関数は、パス文字列を引数に与えることでパターンマッチするパスを探して配列として返してくれる便利な関数です。パターンマッチということで、正規表現のパターンを与えるとマッチングをしてくれるPCRE関数(preg_matchなど)に近いように思われますが、globが解釈するパターンはUNIXシェルのパス名マッチのパターンであり、正規表現より煩雑にならない書き方ができるようです。
たとえば前回preg_grep関数と正規表現を用いて実装した、ディレクトリを検索してその中の拡張子が.jpgのファイルだけ抽出し、セレクトメニューの項目名にするという処理は、globを使うとこのような感じになります。

<?php
$dir = "sample";
$parentlength = strlen($dir) + 1;
echo '<form name="" method="" action=""><select name="">';
foreach(glob($dir."/*.csv") as $val){
echo '<option value="'.htmlentities(substr($val,$parentlength),ENT_QUOTES,"UTF-8").'">'
.htmlentities(substr($val,$parentlength),ENT_QUOTES,"UTF-8").'</option>';
}
echo '</select><input type="submit" value="表示"></form>';
?>

preg_grepでの例の再現のため、項目名としてファイル名のみを取り出そうとしてややこしくなっている部分はありますが、注目すべきはかの例にあったscandir関数のような、ディレクトリ内のパスを取得し配列に納めるというプロセスが省略されているという点です。glob関数はパス検索に特化しており、scandirの直接の上位互換と捉えても良いかもしれません。
さて、関数の説明です。まずパターン文字列の表記方法ですが、PCRE関数のように文字列の前後をスラッシュで囲む必要は無く、パスにワイルドカードなどの特殊記号を交えたものをそのまま与えます。以下が特殊記号です。

記号 意味 使用例 マッチするテキスト
*(アスタリスク) 0文字以上の任意の文字列(ワイルドカード) /home/*/lib /home/userA/lib,home/userB/lib
?(クエスチョンマーク) 任意の1文字 /img/img???.jpg /img/img001.jpg,/img/img002.jpg
¥(円マーク) 特殊文字のエスケープ(windows) /post.php¥?* /post.php?post=””
\(バックスラッシュ) 特殊文字のエスケープ(windows以外) /post.php\?* /post.php?post=””
[](角括弧) 括弧内に現れる文字のどれか /te[xs]t.php /text.php,/test.php
-(ハイフン)を使った応用編 /te[a-z]t.php /tebt.php,/tewt.php(aからzまで可)
-(ハイフン)を使った応用編 /te[1-9]t.php /te3t.php,/te8t.php(1から9まで可)
!(エクスクラメーション)は角括弧中で否定の意 /te[!nx]t.php /test.php(text,tentはダメ)

正規表現と細かな違いがありますね。アスタリスクがワイルドカードの役割をするという辺りは、CSSやSQLの熟練者にはわかり易くて良いのではないでしょうか。
書式と定数の説明です。

glob(引数1,引数2);
引数1 : パターン文字列
引数2 : オプションフラグ。省略可。以下の定数をとり得る。
GLOB_MARK ディレクトリがマッチした場合最後にセパレータのスラッシュを付加
GLOB_NOSORT 結果として返す配列をソートしない(デフォルトはソート)
GLOB_NOCHECK マッチする結果が無かった場合、検索パターン自体を返す
GLOB_NOESCAPE エスケープ文字(¥,\)を機能させない
GLOB_BRACE {パターン1,パターン2,パターン3}という指定で複数パターンの検索をする
GLOB_ONLYDIR マッチしたディレクトリのみを返す
GLOB_ERR エラー時に停止する(PHP5.1.0以降)

正規表現における|(パイプライン)でのOR検索は、引数にGLOB_BRACEを与えた上で{}(中括弧)で括って行う必要があります。

preg_grepを使った例では、読み込んだファイル名を一旦ユーザインターフェースに出力しユーザに選ばせるという、完全にリモートのWEBサービスを意識した構成でした。それに対してglobの強みが生きてくるのは、ディレクトリに追加されたファイルを強制的に全部読み込んで表示するといった、ユーザの了解を省略したローカルディレクトリベースのアプリケーションなのかもしれません。


PHPのPCRE関数を使ってデイレクトリ内の特定拡張子ファイルのリストを取得する

前回解説した正規表現とPCRE関数を使って、ディレクトリ内の特定の拡張子のファイルのリストを作成してみます。今回のサンプルプログラムではさらに、特定の拡張子のファイルリストを得た後それをhtmlのselect要素に整形することで、ユーザにファイルを選ばせて処理を加えるWEBアプリケーションのインターフェース部分という体裁で書いてみようと思います。

<?php
$dir = "sample";
echo '<form name="" method="" action=""><select name="">';
foreach(preg_grep("/\.jpg$/",scandir($dir)) as $val){
echo '<option value="'.htmlentities($val,ENT_QUOTES,"UTF-8").'">'
.htmlentities($val,ENT_QUOTES,"UTF-8").'</option>';
}
echo '</select></form>';
?>

ディレクトリを検索してその中の拡張子が.jpgのファイルだけ抽出し、セレクトメニューの項目名にしています。ここで使っている関数は、引数2に配列を与えると引数1に与えた正規表現文字列にマッチする要素だけを配列に格納し、返り値として返すpreg_grepという関数です。

preg_grep(引数1,引数2,引数3);
引数1 : 正規表現文字列(前後にスラッシュで囲む)
引数2 : 抽出元の配列
引数3 : 反転フラグ。PREG_GREP_INVERTを与えると、条件にマッチしない要素の配列を返す。省略可。

例では引数2にscandir関数を与えて、ディレクトリ内の全てのファイル/ディレクトリ名を配列にしています。その中からの拡張子.jpgの抽出を、preg_grep関数が担当するようになっています。
この例ではあまり問題になりませんが、preg_grep関数には気をつけなければならない点が一点あります。返り値として返す配列の添字が、引数に与えた元の配列のものを継承するということです。つまり配列の添字が必ずしも連番になっていないので、添字をカウントアップさせて要素をひとつずつ取り出すループとは上手く連携できません。そうした処理と連携する場合は、array_values関数を使って配列の要素に連番の添字をアサインし直しましょう。


PHPで正規表現による文字列のパターンマッチングを行う

文字列の中の特定のパターンを検索する方法として、正規表現というものがあります。以前、.htaccessでディレクトリ内の特定文字列を含むファイルへのアクセスを禁止する方法について書いたエントリの中で少し触れたのですが、今回は書式のおさらいと、PHPでの対応する関数の紹介を行います。

まず正規表現の書式ですが、以前紹介したエスケープ(\または¥)、行頭(^)、行末($)、または(|)の他にもいくつか記号があるので、一覧表にしてみます。

記号 意味 使用例 マッチするテキスト
.(ピリオド) 任意の一文字(ワイルドカード) te.t text,tent,te!t
*(アスタリスク) 直前の文字の0回以上の繰り返し te.*t text,texxxt,tet
+(プラス) 直前の文字の1回以上の繰り返し te.+t text,texxxt
?(クエスチョンマーク) 直前の文字が0または1回登場 te.?t text,tet
¥(円マーク) 特殊文字のエスケープ(windows) te¥.¥?t te.?t
\(バックスラッシュ) 特殊文字のエスケープ(windows以外) te\.\?t te.?t
^(カレット) 行頭 ^\.ht .htaccess,.htpasswd
$(ドル) 行末 nt$ tent,plant
[](角括弧) 括弧内に現れる文字のどれか te[xyz]t text,tezt
-(ハイフン)を使った応用編 te[a-z]t tebt,tewt(aからzまで可)
-(ハイフン)を使った応用編 te[1-9]t te3t,te8t(1から9まで可)
^(カレット)は角括弧中で否定の意 te[^nx]t test(text,tentはダメ)
()(丸括弧) グルーピング (te)*xt xt,text,tetext
|(パイプライン) または ([a-z]|[1-9])* aaa,a,9999

PHPで正規表現によるマッチングを行うには、PCRE関数というPerlでの正規表現に準拠した関数群を使います。preg_という接頭辞がついたこの関数群に正規表現のパターンを与える時は、パターン文字列の前後をスラッシュで囲むという約束事があります。基本的なpreg_match関数で例を見てみましょう。

<?php
$teststring = "Hello!";
preg_match("/He[a-z]+/",$teststring);
?>

引数1にパターン文字列、引数2に検索対象を与えています。引数3、4、5もあるのですが、省略可です。 パターン文字列にマッチしたものがあると、preg_match関数は返り値として1を返します。ちなみに返り値として検索がマッチした回数を返してくれる、preg_match_allという関数もあります。
その他、検索置換機能を備えたpreg_replaceや、配列を精査してくれるpreg_grepという関数もありますので、それらの活用例もいずれエントリでアップできればと思っています。


GDで透過ありインデックスカラー画像を合成すると

前回エントリではインデックスカラー画像の合成を試みました。サンプルプログラムのように周囲に余白が無い画像の場合には期待通りの合成ができるのですが、たとえばimagerotate関数で45度傾けた長方形の場合などには、合成する画像の周囲の余白を透明部分として処理する方法が無く暗幕を被せたようになってしまいます。これは前回も触れたとおりです。
ではGDライブラリがインデックスカラーの透過色に全く対応していないかというと、そういうことでもないようです。画像編集ソフトで透過色ありの設定で保存したPNGを読み込むと、それはそのまま透過色ありの画像として扱われ、他の画像に合成しても透過の設定は残ります。例で見てみましょう。

<?php
$img = imagecreate(200,200);
$colour1 = imagecolorallocate($img, 255, 0, 255);
$colour2 = imagecolorallocate($img, 100, 90, 100);
imagefilledrectangle($img, 0, 160, 200, 200, $colour2);
$img2 = imagecreate(200,20);
$colour4 = imagecolorallocate($img2, 255, 255, 255);
$img3 = imagecreatefrompng("test.png");
$img4 = imagerotate($img4,90,$colour4,0);
imagecopy($img,$img4,10,0,0,0,150,200);
header('Content-Type: image/png');
imagepng($img);
imagedestroy($img);
imagedestroy($img2);
imagedestroy($img3);
imagedestroy($img4);
?>

前回とほとんど変わらないプログラムです。新しく出てきた関数imagecreatefrompngはPNG画像を読み込んだキャンバスを作成するもので、他にimagecreategifなどもあるというのはご想像のとおりです。引数に同ディレクトリ内の透過色ありPNGを指定したのですが、結果このように表示されました。

(この部分の画像は000webhostに接収されてしまいました)

判りにくいのですが、元の透過PNGの透過設定が保持され、合成先画像のピンクの背景を突き抜けて透過しています。傾いた長方形の周囲の白い部分は、黒字の背景のページに設置すると黒になります。つまり合成先画像のカラーパレットと、透過色設定を含んだ合成元画像のカラーパレットを単純に結合したためこういう事態になっているということのようです。ちなみにimagerotate関数の引数4を0以外にしても見え方は変わりませんでした。
こうした仕様もあり、GDで複数の画像を合成して画像を作成する場合には、フルカラー画像を前提にしてプログラムを書いた方が予期せぬ壁に阻まれることがないだろう、という教訓も得られようというものです。


GDライブラリを用いてWEB表示用の画像を生成する

htmlコーダからのステップアップでPHPを習得したばかりという人間。クラスを活用したプログラムを書くのはまだ敷居が高いけれど、htmlをパッチワークのように切り貼りして作る練習ばかりではつまらない。そんな状況の人間には、GDライブラリを使ったグラフィックの生成にチャレンジすることをお勧めします。
何がそれほどまでにお勧めなのかというと、まず大抵のレンタルサーバにインストールされていて、ファイルの追加インストールの必要がないということ。includeする必要も無いので、サーバのphpパッケージのディレクトリパスも知っている必要がありません。そして重要なポイントとして、imgタグのsrc属性に指定したときに××.phpという指定が残るので、htmlの知識しか無い人間を当惑させることができるということ。少しだけ優越感に浸れて、PHPを勉強して良かったと思えるかもしれません。

<?php
$img = imagecreate(200,200);
$colour1 = imagecolorallocate($img, 255, 0, 255);
$colour2 = imagecolorallocate($img, 100, 90, 100);
imagefilledrectangle($img, 0, 160, 200, 200, $colour2);
$img2 = imagecreate(200,20);
$colour3 = imagecolorallocate($img2, 0, 0, 0);
$colour4 = imagecolorallocate($img2, 255, 255, 255);
$img3 = imagerotate($img2,90,$colour4,0);
imagecopy($img,$img3,10,0,0,0,20,200);
header('Content-Type: image/png');
imagepng($img);
imagedestroy($img);
imagedestroy($img2);
imagedestroy($img3);
?>

基本的にはこのような流れで画像を作っていきます。細部の説明をしましょう。

まずimagecreate関数ですが、引数に画像サイズ(x,y)を与えて、画像を一時的に保存するメモリを確保する関数です。画像を描くキャンバスをサイズを決めて用意するというイメージで理解するのが良いかもしれません。インデックスカラー画像を作成する場合にこのimagecreate関数を使い、16ビットフルカラー画像の作成の場合にはimagecreatetruecolorという関数を使います。インデックスカラーとフルカラーの違いについてはこちらのエントリを参考にして下さい。

次にimagecolorallocate関数ですが、imagecreate関数で確保したメモリ領域(変数$imgで参照してます)を引数1に、その後の3つの引数はそれぞれ赤、緑、青を0〜255までの256段階で表し、混色しています。
さて、インデックスカラーでは使用する色は必ずカラーパレットで宣言されていなければいけないということを思い出して下さい。そうした仕様のため、imagecreate関数でメモリを確保したあと、最初に呼び出されたimagecolorallocate関数の色は、優先的に背景色としてアサインされます。勿論返り値を格納した変数は、キャンバス上に別の図形を描く際に呼び出して使うことも可能です。

さて、その次のimagefilledrectangle関数では、長方形を描画しています。引数はキャンバス、長方形の左上のx,y座標、右下のx,y座標、塗りつぶしの色という順番です。

下って出てくるimagerotateという関数は、引数1に与えたキャンバスを、反時計回りに引数2の角度回転させたものを返すという関数です。この例では90度回転なのでうまく余白部分が無しになっていますが、引数3にはキャンバスを回転させた余白部分の色、引数4には透過色を無視するかどうか(0以外の値で無視。デフォルトは0。省略可)を与えます。ちなみにこの引数4の設定は残念ながらインデックスカラーのキャンバスに定義した図形には適用できないので(透明色定義のimagecolortransparentが、フルカラーのキャンバスにしか対応していない)、ここでは無視して構いません。

imagecopy関数の引数は、コピー先キャンバス、コピー元キャンバス、コピー先x,y座標(左上)、コピー元x,y座標(左上)、コピー元x,y座標(右下)という順番です。コピー元x,y座標の指定次第で、部分コピーも可能です。

header関数では出力イメージのcontent-typeを指定して下さい。imagepng関数はPNG出力を、imagegif関数ならGIF出力をします。最後にimagedestroy関数で確保したメモリの解放をして終了します。出力されたイメージを見てみましょう。

(この部分のイメージは000webhostによって接収されてしまいました)

ピンク色の背景色にimagefilledrectangleで描いた灰色の長方形が置かれ、その上にimagecopyで合成された回転後の長方形が置かれていますね。

これでファイル名は××.phpながらイメージとしてコピー/ダウンロードできる画像が出来上がります。GDではさらに複雑な画像生成もできるので、プログラマ心を程よく刺激してくれる筈でしょう。