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

PHPのechoとprintは言語構造だというけど、言語構造とは何か

細かいことが気になり出すと、調べ尽くして解答を出すまで次の事に移れないタイプで。
PHPを覚えたての頃、標準出力にテキストや変数などを書き出す際に使う命令、echoとprintがあるということを学んだ。そして、解説書に書いてある通り、これらの命令はそれほど違わないので好きな方を使えば良いと理解した。

たまたまPHPにおける三項演算子を調べていたとき、echoとprintで挙動が異なるという例に当たった。三項演算子については、まとめ直した投稿をそのうち上げる予定だけれど、”?”や”:”といった演算子で挟まれる部分は、式でなければならないらしい。そして、echoなどは式という条件を満たさない言語構造であるから、入れるとエラーが出ますよ。ちなみにprintも言語構造ですよ。でも、関数のように振る舞うからエラーが出ませんよ。といったよく分からない説明があった。

//エラーにならない(理由:式だから)
$alcohol = $age >= 20 ? "OK" : "NG";
echo $alcohol;
 
//エラーにならない(理由:式だから)
$age >= 20 ? $alcohol = "OK" : $alcohol = "NG";
echo $alcohol;
 
//エラーにならない(理由:式だから)
$alcohol = $age >= 20 ? htmlentities("OK") : htmlentities("NG");
echo $alcohol;
 
//エラーになる(理由:言語構造だから)
$age >= 20 ? echo "OK" : echo "NG";
 
//エラーにならない(理由:言語構造だけど関数のように振る舞うから)
$age >= 20 ? print "OK" : print "NG";

PHPの式とはなんだろう?

上の3つの例でエラーにならないのは、三項演算子の書式(式1)?(式2):(式3);これに即しているため。ではそもそもPHPにおける式の定義とは何だろうか。マニュアルを見るとちゃんと一項目があって式とは値があるものだそうです。
一番上の例、”OK”や”NG”というのは、値である。これはとても直感的にわかる。
上から2番目は代入式だ。ここで結構ショッキングな事実になるけれども、実は代入式では、左辺の$alcoholに右辺の値”OK”が代入されるのみならず、式自体も右辺と同じ値を持つということらしい。
したがって、代入式は値を持っているのでエラーにならない。
上から3番目。関数を呼んで実行するのも式だ。htmlentities関数はstring型を返り値として返す。また、特に値を返す設定をしていない関数(たとえば、ユーザ定義関数でreturnを行わないタイプ)でも、自動的にNULLを返す設定となっている。つまり値を返すから式だ。

言語構造とは

では、関数ではない言語構造と呼ばれるものは一体なんなのだろう。言語構造(Language Constructs)とは、英語で見れば何のこともない。言語を構成する要素である。ifとかwhileとかといったものと同じ。制御構造を作るためのifやwhileとともに、言語に最初から存在するものとしてハードコードされているため、関数のように決まった形でないと呼び出せないというような制約はない。echoはecho “文字列”;のようにもecho(“文字列”);と関数のようにも呼び出せるが、これは便宜のためたまたまどちらの書き方でも呼び出せるようにハードコードされているからで、echoの属する言語構造というカテゴリがこうした文法的制約を持っているわけではない。
関数ではないということで、当然のことながらcall_user_func(“echo”,”文字列”);こういった呼び出し方も出来ない。

echoとprintの違いとは

では、echoとprintが両方とも言語構造でありながら、異なるというところはどこだろうか。実は先程echoが便宜のため関数のように括弧を付けても呼び出せると書いたが、printはさらに返り値として常に1を返してくる。つまり、printの方が関数への擬態がうまい。そしてその分処理が重い。
そして、何らかの値を返すということは、printは式になるのである。そのため、三項演算子の中の式としても使えていたのである。

なるほどよく分かった。ちなみに違いとしてもう一つ、echoはecho”値1″,”値2″;のように使う事も出来るというものがあり、大抵の解説書にはechoとprintの違いとしてこちらが説明されているのではないかと思う。そっちの方が重要かもしれないね。

関数っぽいけど言語構造のもの一覧

最後に、一見関数のように見えて実は言語構造という例をいくつか挙げておこう。

返り値の型 名前 機能
なし echo 文字列を出力
int print 文字列を出力
array array 配列を生成
array list 配列の形式で、変数に値を代入
mixed eval 文字列をPHPコードとして評価
なし unset 変数を破棄する
bool isset 変数がセットされているか調べる
bool empty 変数が空かどうか調べる
なし exit スクリプトの実行を終了
なし die exitと同等
mixed include ファイルを読み込む
mixed require ファイルを読み込む(読み込めないと停止)

同列に扱っているけど、既に説明したとおり共通した呼び出し方の制約はない。フリーダムなやつらなので注意が必要。


BootstrapとGoogle Maps API同時使用時の表示崩れを修正する

Twitter社の提供するCSSフレームワーク、Bootstrap。これを使うと、面倒臭いWEBのGUIデザインを、要素へのクラス指定一発で解決することが出来る。以前紹介を行ったとおりCDNでの利用も可能なので、WEBサイトを作成する前にとりあえず(jQueryと一緒に)呪文のように読み込んでおくと、ページ制作中痒いところにも咄嗟に手が届き易い。

もう一つ、企業サイトであれば”会社概要”ページのアクセスマップなどで使われるであろう、Google Maps API。こちらも頻繁に利用されるものに違いないのだが、BootstrapとGoogle Maps APIを同時に利用して地図の表示を行うと、以下のスクリーンショットのように表示崩れが発生する。

Google Maps表示不具合

Google Maps表示不具合

まず由々しきこととして、左端の縮尺スライダーが表示されない。このような状態だと、スクリーンショットを貼っておいた方がまだ良かったのではないかという疑問も湧くだろう。また、右上の地図と衛星写真の切り替えメニューで、カーソルをホバーした時のドロップダウンメニューが少し見苦しい。具体的には、チェックボックスとラベルの間に改行が入ってしまうのである。

Google Maps表示不具合の原因

これらの不具合の原因は、Bootstrap側で読み込むCSSで、img属性やlabel属性など比較的影響の大きいところでスタイル指定を行っているからである。具体的には、

img{
max-width:100%;
width:auto\9;
height:auto;
vertical-align:middle;
border:0;
-ms-interpolation-mode:bicubic;
}
 
label{
display:block;
margin-bottom:5px;
}

といったような記載のある箇所である。問題となっているのは、img属性のmax-width:100%指定と、label属性のdisplay:inline-block指定である。

特に対応せずにGoogle Mapsが表示できているケース

img属性の指定によってマップのスライダーが表示されない不具合については、Bootstrap側としても対応策をとっている(どのヴァージョンからかは、生憎把握していないが)。

#map_canvas img,.google-maps img{
max-width:none;
}

このようなCSSを追加して、Google Mapsのデフォルトで包括要素として使われるdivのid=”map_canvas”以下のimg属性に特別にスタイル指定している。そのため、素直にマップを表示する領域のidを#map_canvasにしている場合、不具合は起こらないはずだ。問題は、別のid名やクラス名をしている場合。Bootstrap側の付け焼き刃的対策では問題が解決しない。

解決策

解決策は、Bootstrapの付け焼き刃解決策と同じように、包括要素以下のimg属性・label属性にスタイルを指定してやれば良い。たとえば包括要素がid=”googlemapdiv”だった場合、スタイルシートに以下のように記述する。

#googlemapdiv img{
max-width : none;
}
 
#googlemapdiv label{ 
width : auto;
display : inline; 
}

また、マップのコントロール部分の上位要素であるclass=”gm-style”以下の各属性に指定する形でも良いだろう。

.gm-style img{
max-width : none;
}
 
.gm-style label{ 
width : auto;
display : inline; 
}

これで正常に表示されるはず。

GoogleMaps正常表示

GoogleMaps正常表示

BootstrapやGoogle Mapsなどの複数WEBサービスを組み合わせて利用する場合、どこかは絶対ぶつかるものだと最初から割り切っていた方が良いのだろう。ちなみに今回の問題はEXIFviewerに機能を追加する過程で確認したもの。そろそろ新ヴァージョンを公開できるはず。


var_dumpよりログ作成に便利なvar_export

PHPで変数をダンプしたいとき

PHPでプログラムを書いていて、処理の一時点での変数の値を参照したいとき。通常であれば、var_dumpやprint_rなどの関数を使います。

$ar = array(
array(1,"愛媛県",array("県庁所在地" => "松山","名物" => "みかん")),
array(2,"香川県",array("県庁所在地" => "高松","名物" => "うどん"))
);
 
//var_dumpの場合
var_dump($ar);
//標準出力への出力結果
array(2) { [0]=> array(3) { [0]=> int(1) [1]=> string(9) "愛媛県" [2]=> array(2) { ["県庁所在地"]=> string(6) "松山" ["名物"]=> string(9) "みかん" } } [1]=> array(3) { [0]=> int(2) [1]=> string(9) "香川県" [2]=> array(2) { ["県庁所在地"]=> string(6) "高松" ["名物"]=> string(9) "うどん" } } }
 
//print_rの場合
print_r($ar);
//標準出力への出力結果
Array ( [0] => Array ( [0] => 1 [1] => 愛媛県 [2] => Array ( [県庁所在地] => 松山 [名物] => みかん ) ) [1] => Array ( [0] => 2 [1] => 香川県 [2] => Array ( [県庁所在地] => 高松 [名物] => うどん ) ) )

var_dumpとprint_rの違いについては、print_rの方が変数を見やすく出力してくれるという説明がされています。変数型のような余計なものがついていませんが、値の確認程度ならprint_rでも間に合いますね。

変数のダンプ結果を文字列として扱いたいとき

通常のダンプだけでなく、たとえばある時点での変数の値をデータベースに格納したい場合、特定のメールアドレスに送信してログを残したい場合、などは、一度標準出力をob_startとob_get_contentsなどを使ってバッファリングした上でvar_dumpなど行う必要があるように思えます。
それらを一緒くたに、変数を展開して即文字列型にしてしまうのがvar_export関数です。

var_export関数は引数を二つとり、第一引数が文字列に展開する変数、第二引数がブーリアン型で、falseならばそのまま標準出力に出力、trueならば出力はせず、返り値として文字列を返します(デフォルトはfalse)。

$ar = array(
array(1,"愛媛県",array("県庁所在地" => "松山","名物" => "みかん")),
array(2,"香川県",array("県庁所在地" => "高松","名物" => "うどん"))
);
 
//var_export
var_export($ar);
//標準出力への出力結果
array ( 0 => array ( 0 => 1, 1 => '愛媛県', 2 => array ( '県庁所在地' => '松山', '名物' => 'みかん', ), ), 1 => array ( 0 => 2, 1 => '香川県', 2 => array ( '県庁所在地' => '高松', '名物' => 'うどん', ), ), )
 
//var_exportの第二引数にtrueを指定して変数に格納
$return = var_export($ar,true);

var_exportの出力は、print_rと同じように変数型については省略のようですね。また、配列の最後には、次に続く要素が無くてもカンマをつける癖があるみたいです。細部に注意は必要ですが、これでログの作成が容易になります。


PHP、JavaScriptそれぞれにおける無名関数

PHPとJavaScriptを同時に使って開発を行う事が多いという個人的事情のため、混同しないようにする目的で書いているこの比較シリーズですが、今回は無名関数について扱います。

無名関数というのは、関数的な処理のまとまりを、名前空間を消費することなくアドホックに定義する手法で、最近ではajaxなどの通信を伴う関数の、コールバック関数として使われるのが一番身近な使用と言えるかもしれません。無名関数使用例の紹介もかねて、PHPとJavaScriptそれぞれの無名関数の書式を見てみましょう。

PHPにおける無名関数

基本的な書式

以前、spl_autoload_register関数の説明で用いた例です。

//無名関数の登録
spl_autoload_register(function($classname){
include $classname . ".class.php";
});

なにやらjQuery方面でよく見かけそうな、中括弧と小括弧の連続です。
無名関数を使っているのはspl_autoload_register関数の引数の部分で、したがってfunctionの前の”(“と最後の”)”は、無名関数の書式とは関係ありません。つまり、通常の関数定義を関数名を抜かして行えば無名関数になるわけですね。

無名関数の即時実行

なお、定義した無名関数に値を与えて即時実行する場合、可変長引数を関数内で引き渡す記事の際に使ったcall_user_func_array関数、もしくはcall_user_func関数を使います。

//無名関数の即時実行
//call_user_func(関数名,引数)という書式でしたね。
 
call_user_func(function($classname){
include $classname . ".class.php";
},"dullFunction");

PHPの無名関数の注意点としては、無名関数が使用可能なヴァージョンが5.3以降と、比較的最近実装された機能だという事です。現在稼働中のプログラムでも、5.2系のヴァージョンで作成され今まで運用されているものが結構ありますので、その場合無名関数は使えないので注意です。

JavaScriptにおける無名関数

基本的な書式

JavaScriptを使ったajax開発では、無名関数が頻繁に登場します。以下はjQueryの$.ajaxでコールバックとして無名関数を登録する例です。

//ajaxコールバックとして無名関数の登録
$.ajax({
url : "test.html",
success : function(data){
$("#contents").append(data);
}
});

successのラベル以降が無名関数の使用になります。PHPのものと、変数に$が付くか付かないか程度の違いしかありません。

無名関数の即時実行

無名関数の即時実行について、JavaScriptは専用の関数を持っていません。かわりにより簡潔な書き方で即時実行をすることが出来るようになっています。

//無名関数の即時実行
(function(data){
$("#contents").append(data);
})(anydata);

通常の無名関数の宣言との違いは、関数名にあたる部分を括弧でくくり、また関数の実行なので最後にセミコロンをつけているだけです。

即時実行の利用頻度は低いかもしれませんが、ソースコードの中に急に出現した括弧付きの無名関数宣言に、当惑してしまわぬよう頭の片隅に入れておくとよろしいでしょう。


PHP5.1.2以降のspl_autoload_registerを使う

外部PHPファイルをインポートして呼び出そうとするとき、いちいちファイルの頭で読み込む数だけincludeを行うのは面倒くさいですね。それならば、使いそうなクラス・関数をfunctions.phpのような一ファイルにまとめてしまえば呼び出しが一回で済む、ということも考えつきますが、これだと一クラス一ファイルの原則に反する上、外部ライブラリを使用する場合などは結局インクルードが必要になり、上手く行きません。

そこで、PHP5以降のヴァージョンに搭載された便利なautoload機能を使いましょう。

PHP5以降の__autoload関数を使ったautoload

PHPには、マジックメソッドと呼ばれる”__(アンダーバー2つ)”から始まる特殊なメソッドがあります。たとえばPHPのクラスについてのエントリで出てきた、__construct。これは予約されたメソッド名なので、この名前を使ってオリジナルの関数を作ってはいけません。既にこの名前のメソッドには使用方法が決められているのです。__constructであれば、クラスのインスタンスが作られたときに呼び出される処理ということでしたね。

さて、autoload機能を実現する__autoloadは、クラスに紐づけられたメソッドというわけではなく、グローバル関数です。ですが、基本的にはマジックメソッドの一類であると考えて下さい。この関数は、未定義のクラスが呼ばれた際に自動的に呼び出されます。

<?php
//未定義のクラス
$instance = new undefinedClass();
?>

もしこの呼び出しの時点で、undefinedClass.phpをincludeしていなかったら、通常はメソッドが見つからないというエラーになります。一方、__autoloadを定義しておくことで、こうしたクラスの見つからないケースでの挙動を指定できます。

<?php
__autoload($classname){
include $classname . ".php";
}
?>

__autoloadは呼び出されたクラス名を引数としてとるので、それを適当な変数名で受けて、includeするファイルネームを生成する処理を書くのです。こうすることによって、クラスを作ったらクラス名.phpという名前に保存するようにすれば、呼び出し時に自動的に探し出してincludeしてくれます。

PHP5.1.2以降のspl_autoload_register関数を使ったautoload

ただ、__autoloadにも不便な点があります。それは、一度定義してしまうと、それ以外の__autoload処理を追加していくことが出来ないということです。__constructの例と同じように、こうした特別なメソッドは一度きりのタイミングに処理を登録することが許されているのです。そこで、複数のautoload処理(たとえば、優先順をつけて探索処理を追加したい場合)を登録するにはどうすれば良いか。そのような場合には、spl_autoload_register関数で処理を追加します。

//autoloadスタックに登録したい関数その1
function searchClassPhp($classname){
include $classname . ".class.php";
}
//autoloadスタックに登録したい関数その2
function searchPhp($classname){
include $classname . ".php";
}
 
//関数を両方ともautoloadスタックに追加
spl_autoload_register("searchClassPhp");
spl_autoload_register("searchPhp");

この例では、定義されていないundefinedClassが呼ばれたとき、最初にundefinedClass.class.phpというファイルが存在するかチェックされます。もし見つからない場合は、undefinedClass.phpが探されます。

ちなみに、spl_autoload_register関数を使ってしまうと、__autoloadで定義した処理は魔法が解けて無視されるようになってしまいます。そこで、__autoloadを定義しており、spl_autoload_register関数も使いたい場合には、__autoload自体もautoloadスタックに登録して下さい。

//魔法の解けた__autoloadを普通の関数として登録
spl_autoload_register("__autoload");

spl_autoload_register関数には、無名関数も登録することが出来ます。

//無名関数の登録
spl_autoload_register(function($classname){
include $classname . ".class.php";
});

ただし無名関数については5.3.0以降のヴァージョンに限定されます。だんだん便利になっているのか、母屋を継ぎ足し継ぎ足し増築しているのか、なんともPHPらしいですね。


WebKitブラウザをXMLビューワとして使う際の落とし穴

Safari、ChromeなどのWebKitブラウザは、標準のデバッギングツールも含めほぼオールマイティな機能を備えています。その機能の一つにXMLのネスト表示というものがあり、ブラウザでXMLにアクセスした際にデバッギングツール内で折りたたみ表示してくれるという、ごく一部の人間にだけうれしい機能をもっています。

これはたとえばXMLを返却するURLを指定し、帰ってくる値を確認しつつ作業をすすめるという場面で重宝する機能です。WEBサービスの中には、XMLで値を取得できるというものがかなりありますので、値だけ取得してローカルで表示するクライアントを作成する際に便利です。

先日まさにそういった使い方をしていたのですが、ブラウザで確認したノード名をクライアント側で取得する際に、どうやっても取得できないという現象に見舞われました。ブラウザ上ではそのノード名で値が取れています。でもローカルではその値を指定しても取れない。この現象に何度も頭をひねりました。

そうして発見したのは、WebKit系のブラウザがノード名を勝手に小文字に変換して表示しているということです。

XMLでは、大文字小文字は勿論区別します。たとえば<node>という値と<Node>という値は異なりますので、<Node>という名前でセットされた値に、<node>でアクセスしようとしても、そんな名前は知らないと言われます。

ひたすら<node>でのアクセスを試みて失敗したあげく、WEBサービスの管理者に問い合わせるところでした。

ゆるいHTML(<BR>とかですね)を処理・表示できるように小文字に統一しているのかもしれませんが、ひどい落とし穴もあったものです。


クラスベース言語PHPの基本作法

オブジェクト指向のタイプ別分類によれば、JavaScriptが属するプロトタイプベース言語とは異なる、クラスベース言語に含まれるPHP。けれども、そもそもPHPという言語は場当たり的な書き方ができる言語としてブイブイ鳴らしていたもので、ゆえにPHP4以前のヴァージョンでのオブジェクト指向サポートは少し心許ないものでした。

2004年にリリースされたPHP5では、その辺りの弱さを解決し、Javaに近いクラスベースに移行しています。したがって、PHPでのクラスを使った開発の作法というのは、基本的にはJavaのそれに近いものになります。基本的な部分の書き方を見てみましょう。

クラスの宣言とアクセス制限

PHPの基本的なクラス宣言です。

//クラス宣言 someHowClass.php
class someHowClass{
 
//クラスプロパティ
public $somehow = "とにかく";
 
//メソッド
function someHowMethod(){
return "動け!";
}
 
}
 
//別ファイルからの呼び出し・インスタンス化・メソッドへのアクセス
require_once("someHowClass.php");
$somehow = new someHowClass();
echo $somehow->somehow . $somehow->someHowMethod();
 
//結果表示
とにかく動け!

クラス宣言をする場合、classという予約語を使います。クラスプロパティ・クラスメソッドの宣言は、通常の変数宣言や関数定義をclassの括弧内で行うことでできます。この際にプロパティ・メソッドへのアクセス制限を行うことも可能で、その場合に使う修飾子はJavaやSmalltalkと同じく、public、private、protectedがあります。

アクセス修飾子 アクセス可能な範囲
public(初期値) メソッド・プロパティをどこからでも呼び出し可能
private クラス内でのアクセスが可能
protected クラス内および子クラスからのアクセスが可能

コンストラクタ

インスタンスが作成される際の初期値を定義するコンストラクタは、__construct()という関数の処理として書きます。

//クラス宣言 someHowClass.php
class someHowClass{
 
//コンストラクタ
function __construct($value){
$this->value = $value;
}
 
//メソッド
function someHowGetValue(){
return $this->value;
}
 
}
 
//別ファイルからの呼び出し・インスタンス化・メソッドへのアクセス
require_once("someHowClass.php");
$somehow = new someHowClass("動け!");
echo $somehow->someHowGetValue();
 
//結果表示
動け!

コンストラクタの呼び出し時に引数を与えることで、インスタンスの初期値を設定することができます。なお、PHP4の時点でのクラスは、コンストラクタがクラス名と同名のメソッドでした。互換性の為に現在でも、__constructの宣言が無い場合には同名メソッドをコンストラクタとする仕様になっています。

クラスの継承と親クラスのメソッド・プロパティの参照

クラスの継承についても見てみましょう。

//クラス宣言 anyWayClass.php
class anyWayClass extends someHowClass{
 
//メソッドをオーバーライド
function someHowGetValue(){
return $value . "!";
}
 
//メソッドの追加
function anyWayGetValue(){
//親クラスのメソッドの参照
return parent::someHowGetValue();
}
 
}
 
//別ファイルからの呼び出し・インスタンス化
require_once("someHowClass.php");
require_once("anyWayClass.php");
$anyway = new anyWayClass("動け!");
 
//子クラスでオーバーライドしたメソッドの呼び出し
echo $anyway->someHowGetValue();
 
//結果表示
動け!!
 
//子クラスで宣言したメソッドの呼び出し
//(処理内容は親クラスのsomeHowGetValue()メソッドの呼び出し)
echo $anyway->anyWayGetValue();
 
//結果表示
動け!

継承をする場合には、extendsという予約語を使い継承元のクラスを指定します。親クラスのプロパティ・メソッドは自動的に子クラスにも実装されるため、上の例では子クラス内で宣言の無いコンストラクタ(__construct())についても処理が出来ています。
同名プロパティ・メソッドに、子クラスでは違う処理を充てたいという場合には、子クラス内で同名プロパティ・メソッドを宣言すると、そちらが呼び出し時に優先されるようになります。上の例ではsomeHowGetValue()メソッドをオーバーライド(上書き)したため、処理結果が親クラスと変わっています。

一方、子クラスで宣言したものではなく、親クラスのsomeHowGetValue()メソッドを呼び出したくなった場合には、parent::という語をつけて呼び出すことで、子クラス内から親クラスのプロパティ・メソッドの参照が出来ます。

なお、PHPの継承は多重継承を認めない仕様になっています。

PHPのクラスの基本はこのような感じです。その他にinterfaceの定義などもあるのですが、とりあえずは説明をここまでとします。

また、いままでオブジェクト指向に全く触れたことがないという人間にとってはチンプンカンプンな内容だったと思うので、オブジェクト指向の説明記事についてはいずれ改めて書かせていただこうと思います。