<?xml version="1.0" encoding="UTF-8"?><!-- generator="wordpress/2.3.3" -->
<rss version="0.92">
<channel>
	<title>MT312 &#187; php</title>
	<link>http://www.mt312.com</link>
	<description></description>
	<lastBuildDate>Sun, 07 Sep 2008 15:33:23 +0000</lastBuildDate>
	<docs>http://backend.userland.com/rss092</docs>
	<language>ja</language>
	
	<item>
		<title>正規表現メモ</title>
		<description>パターン中のメタ文字を纏めてエスケープする
PHP4.3.3以降では、\Q～\Eで囲むとメタ文字を纏めてエスケープできます。
[php]
preg_match('/^\Q(+*%&)\E$/', '(+*%&)');
[/php]
部分的にオプションを変更する
(?オプション文字)を使うとオプションの設定と解除が可能です。
[php]
// 設定
preg_match('/abc((?i)def)ghi/', 'abcDEFghi', $matches);
print_r($matches);
// Array ( [0] => abcDEFghi [1] => DEF )

// 解除
preg_match('/abc((?-i)def)ghi/i', 'ABCdefGHI', $matches);
print_r($matches);
// Array ( [0] => ABCdefGHI [1] => def )
[/php]
部分的にキャプチャを避ける
(?:)を使うとサブパターンがキャプチャされなくなります。
[php]
preg_match('/(?:say) (hello)/', 'say hello', $matches);
print_r($matches);
// Array ( [0] => say hello [1] => hello )
[/php]
キャプチャにキーを指定する
(?P&#60;キー名&#62;パターン)でキーを指定できます。
[php]
preg_match('/say (?Phello)/', 'say hello', $matches);
print_r($matches);
// Array ( [0] => say hello ...</description>
		<link>http://www.mt312.com/php/46/</link>
			</item>
	<item>
		<title>携帯端末番号（個体識別番号）判別</title>
		<description>携帯の端末番号の取得方法を紹介します。こういった携帯にまつわる問題は各社仕様がバラバラで面倒です。とにかく簡単に済ませたいもんです。
方法
DoCoMoとSoftBankはユーザーエージェントに番号が含まれますので、これを解析すればすぐにわかります。AUはユーザーエージェントには含まれず$_SERVER['HTTP_X_UP_SUBNO'];に格納されています。この特殊な変数があれば、ほぼ端末番号であると分かる訳ですが、ログとして保存したい場合には個別に保存項目が必要になりやっかいです。そこで前の番号部分を抜いてユーザーエージェントに連結させます。これで大手三社の番号をユーザーエージェントだけで確認できます。
DoCoMo

// MOVA
DoCoMo/1.0/X503i/c10/ser12345678901
serの後に11桁英数字

// FOMA
DoCoMo/2.0 N2001(c10;ser12345678901;
icc12345678901234567890)
serの後に15桁英数字
iccの後に20桁英数字
改行が含まれます。

[php]
// ser以降で判別
$agent = $_SERVER['HTTP_USER_AGENT'];
$hasSubno = preg_match('/ser[0-9A-Za-z]{11,15}/', $agent);
[/php]
SoftBank

// Vodafone
Vodafone/1.0/V802SH/SHJ002/SN123456789012345 Browser/UP.Browser/7.0.2.1 Profile/
MIDP-2.0 Configuration/CLDC-1.1 Ext-J-Pr

// SoftBank
SoftBank/1.0/705P/PJP10/SN123456789012345 Browser/Teleca-Browser/3.1 Profile/
MIDP-2.0 Configuration/CLDC-1.1

いずれも/SNの後に15桁英数字

[php]
// /SN以降で判別
$agent = $_SERVER['HTTP_USER_AGENT'];
$hasSubno = preg_match('/\/SN[0-9A-Za-z]{15}/', $agent);
[/php]
AU
AUの場合だけ先にユーザーエージェントに番号を付けます。
[php]
// 05051234567890_ae.ezweb.ne.jp
// 4桁地域コードと10桁端末コード
$subno = $_SERVER['HTTP_X_UP_SUBNO'];
// KDDI-CA23 UP.Browser/5.1 (GUI) MMP/2.0/SUB05051234567890_ae
$_SERVER['HTTP_USER_AGENT'] .= '/SUB'.substr($subno, 0, -12);
[/php]
[php]
// 連結した末尾で判別
$agent = $_SERVER['HTTP_USER_AGENT'];
$hasSubno = preg_match('/\/SUB0\d0\d\d{10}_[a-z\d]{2}$/', $agent);
[/php]
まとめ
これで端末番号を送信しているかどうかユーザーエージェントだけで判断できるし、特定の端末番号を拒否する事もできます。今回は省略しましたが、以前の携帯判別のコードと合わせれば便利に書けるかもしれません。
ユーザーエージェント携帯判別
注意
これらの環境変数($_SERVER)は信用してはいけません。保存したり、ページへ出力させる場合にはどんな文字が入っていても問題ない様にしましょう。 </description>
		<link>http://www.mt312.com/php/45/</link>
			</item>
	<item>
		<title>$fpが権限を握るflockによるファイルロック</title>
		<description>果たして、この記事が役に立つか疑問ですが、私の様にポカをする人がいないとも限りませんし、忘備録的に書いてみます。

先日カウンターのログが頻繁に壊れると指摘があり、原因が分からず2日程考えていた時、ふと疑問が。テストしてみると、全くロックできていない。実はこう書いていました。
[php]
function lock() {
  $fp = fopen('./lock', 'w');
  return $fp && flock($fp, LOCK_EX);
}
if(!lock()) {
  // ロック失敗で終了
}
// ログ保存
$fp = fopen(LOG_FILE, 'wb');
fwrite($fp, $lines);
fclose($fp);
[/php]
自分では正しいと思っていたんですけど、実はlock()はtrueしか返さず、後にロックが外れます。どうなっているかと言うと、lcok()と同時にロックされ真が返りますが、$fpがローカル変数で既に用済みな為、ロックが外れてしまうのです。flockによるファイルロックは$fpが生きている必要があります。つまり、以下でもロックが外れます。
[php]
$fp = fopen('./lock', 'w');
flock($fp, LOCK_EX);
unset($fp);
[/php]
よって、こう書いて解決。
関数にしなきゃいいだけの話。
[php]
// ファイル生成可能で空にするコストがないaを指定
$l_fp = fopen('./lock', 'a+');
$locked = $l_fp && flock($l_fp, LOCK_EX);
if(!$locked) {
  // ロック失敗で終了
}
// ログ保存
$fp = fopen(LOG_FILE, 'wb');
fwrite($fp, $lines);
fclose($fp);
[/php]
上の例と違い、ログファイルを追記モードで扱う場合はロックファイルを用意する必要はありません。また、追記モードで書込モードの様に空にしてから書き込む事もできます。
[php]
// ファイルがなければ生成
$fp = fopen(LOG_FILE, 'a+b');
// ロックしてファイルを空にする
$prepared ...</description>
		<link>http://www.mt312.com/php/44/</link>
			</item>
	<item>
		<title>DoCoMo絵文字データ保存方法</title>
		<description>携帯から入力された絵文字の判定を簡単に行う方法があるようです。mb_substitute_character();を使って存在しない文字コードを炙り出します。

携帯端末からの投稿を保存
[php]
// 出力文字エンコーディングに文字コードが存在しない場合の代替文字を指定
mb_substitute_character('long');

// 太陽絵文字入り文字列
$str = '今日も暑いですね・';
// 強制的に絵文字部分がBAD+16進コードに変換される
$str = mb_convert_encoding($str, mb_internal_encoding());
// 絵文字を[d:絵文字コード]に変換して保存
$str = preg_replace('/BAD\+(F[89][0-F]{2})/', '[d:$1]', $str);
[/php]

こうしておけば[d:～]を探して変換すれば、PCには絵文字画像、携帯には直接絵文字を表示させる事ができる。記事の修正が必要になっても絵文字を意識しなくて済みます。

保存ログを携帯端末へ出力
[php]
$str = '今日も暑いですね[d:F8A0]';
$str = preg_replace('/\[d:(F[89][0-F]{2})\]/e', "pack('H*', $1)", $str);
print $str;
[/php]

まとめ
BAD+からF3、F4、F6、F7で始まる文字列を調査すれはAU絵文字も判別可能らしい。めちゃくちゃ楽だな、これ。

参考文献
絵文字の抽出をPHPで最も簡単にする方法 ぎじゅっやさん </description>
		<link>http://www.mt312.com/php/43/</link>
			</item>
	<item>
		<title>携帯特有の注意点</title>
		<description>ホスト名
携帯はホスト名が頻繁に変わってしまいます。
またホスト名の後半でキャリア判別すると失敗する場合があり、信用できません。正確に携帯判別するには各社サイトに掲載されているIPリストと照合する必要があります。
Content-Lengthを付ける
DoCoMoはContent-Lengthがないと表示できない場合があります。
「接続先のサーバーは現在ご利用できません」
リダイレクトは避ける
正しく処理されなかったり、エラーになる機種があります。
「無効なデータを受信しました」
POST後の二重投稿防止ボタン
[html]

  投稿完了しました
  

[/html]
POST後にこれを踏むと二重投稿を防ぐ事ができます。
GETでは駄目。
&#60;input&#62;フィールド内で改行を入力
PCブラウザでは改行できなくても携帯ではできてしまいます。
&#60;textarea&#62;フィールド内の改行コード
&#60;textarea&#62;フィールドに文章を出力する場合は改行コードをCRLFにする。
LFでは改行されません。
istyle属性
istyle="1" 全角かな
istyle="2" 半角カナ
istyle="3" 半角英字
istyle="4" 半角数字

パスワード入力欄&#60;input　type=&#34;password&#34;&#62;はDoCoMoはistyleが無視されて半角数字モードになりますが、AUでは付けていないと全角モードになるのでここにもistyleを付けておく。最近のSoftBank携帯はistyleに対応しているようです。
まとめ
携帯の機能はよりPCに近づいています。
この記事も、すぐに当てにならなくなるかもしれませんので、参考程度に。 </description>
		<link>http://www.mt312.com/php/42/</link>
			</item>
	<item>
		<title>処理速度最適化法</title>
		<description>いろいろ纏めようと思っていたんですが、すでに分かりやすいサイトが沢山あります。

PHP最適化tipsまとめサイト
基礎構文処理速度のあれこれ
require VS require_once
実際に計測してみた所、試行回数が少なかったせいか、あまり差は見られませんでした。
（未読ファイルの読み込みに関して）
一度しか読まないのが分かっていればrequire_onceを使うメリットはないと思います。
速度は気にせず以下で使い分ければOK。

複数回読まれる可能性があるが、一度しか読む必要がない。require_once
それ以外。require
== VS ===
型を含めて比較する後者の方が若干速い。
パスワードなど外部データと比較する場合にセキュリティーホールとなる可能性があるので、理由がない限り===を常用する。
is_hoge() VS ===
[php]
if(is_null($var)) {}
if($var===null) {}
[/php]
後者が速い。
count($array)>0 VS (bool)$array
配列に要素があるかどうかの判別。
後者が速い。
array_merge() VS +演算子
array_mergeは連結後に数字キーを振り直すので、連想配列の場合は+演算子を使った方が速い。
[php]
$a = array('a'=>1, 'b'=>2);
$b = array('a'=>3, 'c'=>4);

// array_merge($a, $b) == $b + $a;
[/php]
バージョン分岐
PHPのメジャーバージョンで分岐したい場合はversion_compare()を使うより各バージョン特有の定数を調べるのが良い。
[php]
// PHP_VERSIONの一文字目を調べるより速い
$isPHP5 = defined('E_STRICT');
[/php]
is_file() VS file_exists()
file_exists()はディレクトリも含まれるので、ファイルの有無を調べたいだけならis_file()の方が高速。
文字列連結速度
[php]
$str = $str1.$str2;
$str = "$str1$str2";
$str = "{$str1}{$str2}";
[/php]
上から順に高速。 
splitは使うな
文字列を分割して配列を作成する場合、splitは遅いので避ける。 
正規表現分割は、 preg_split (PHP 3.0.9, 4, 5)
固定文字分割は、 explode (PHP 3, 4, 5)
分割して一部分だけ取得したい場合は、分割数を制限する3番目のlimit引数を付けておきます。 特にループ内で繰り返し使う場合、全分割するより時間を短縮できます。 
[php]
$str ...</description>
		<link>http://www.mt312.com/php/41/</link>
			</item>
	<item>
		<title>コメントスパム対策法</title>
		<description>近年スパム被害が増えています。
誰でも投稿できるブログや掲示板も狙われています。
ここでは手間のかからない、簡単な対処法をいくつか紹介します。

	主な対処法

	禁止ワードを設定
	日本語が使えない環境からの投稿を拒否
	半角英数字のみの投稿を拒否
	投稿できるURLの数を制限
	プロキシサーバ経由の投稿を拒否
	リファラーを参照してフォームを経由したか判断
	フォームを表示してから投稿するまでの許容時間を設定する
	ダミー項目を作り入力があった場合に拒否
	フォームを解析されない様に、ロボットを排除
	データ送信先を頻繁に変更
	Captcha画像認証



検索ロボットを排除
[html]



[/html]
ロボットを完全に排除するのは難しい様ですが、少しは効果があると思います。googlebotのsnippetは検索結果に表示される該当ページからの抜粋です。これがある方がクリック率が高まるそうです。これを削除(nosnippet)するとキャッシュも削除されます。

Googleページ削除
リファラーからフォームを経由したか調べる
[php]
$from = $_SERVER['HTTP_REFERER'];
$host = $_SERVER['HTTP_HOST'];
if(!stristr($from, $host)) {
  print '不正なリファラーです。';
}
[/php]
リファラーを持たない環境は結構あります。ノートン等のセキュリティソフトによって隠蔽されている可能性もあります。ドコモなど一部携帯でアウト。
逆引き不可能なIPアドレスを拒否
[php]
$ip = $_SERVER['REMOTE_ADDR'];
$host = getHostByAddr($ip);
if($ip==$host) {
  print '逆引き不可能なIPアドレスです。';
}

// これでも可能
if(!preg_match('/[a-z]/', $host)) {
  print '逆引き不可能なIPアドレスです。';
}
[/php]
プロキシ経由を拒否
プロキシ特有の環境変数とホスト名を調べる方法です。
環境変数を吐かない匿名プロキシもあって、すべてを検出できる訳ではありません。
他にはプロキシリストを作成したり、ポートスキャンする方法もあります。
[php]
$systems = $_SERVER + $_ENV;

$ip = $systems['REMOTE_ADDR'];
$host = getHostByAddr($ip);
// ドコモの携帯はproxyが存在するので注意
// anonym=anonymous,anonymizer
// prox=proxy,proxify
$proxhost = preg_match(
'/anonym&#124;cache&#124;delegate&#124;firewall&#124;gateway&#124;httpd&#124;keeper&#124;prox&#124;squid&#124;via&#124;www/',
$host);

$bools = array(
  isset($systems['HTTP_CLIENT_IP']),
  isset($systems['HTTP_FORWARDED']),
  isset($systems['HTTP_MAX_FORWARS']),
  isset($systems['HTTP_PROXY_CONNECTION']),
  isset($systems['HTTP_SP_HOST']),
  ...</description>
		<link>http://www.mt312.com/php/40/</link>
			</item>
	<item>
		<title>Amazon Web Service</title>
		<description>Amazon Web Service(AWS)を使うと、Amazonで検索した商品データを自分のサイトに載せる事ができます。商品データはXML形式で返されますので、これをPHPで解析してHTMLに成型します。

	このサービスを利用するには登録が必要です。

	商品広告で収入を得る為のアソシエイト登録
	AWSを利用する為のSubscription Idの取得



商品検索方法
http://xml-jp.amznxslt.com/onca/xml3?local=jp
このアドレスに検索用のパラメータを連結させて問い合わせします。


オプションパラメータ


f=
XSLTを適用する場合はそのURL
f=xmlとするとそのままXMLを取得できます。


locale=
国コード 日本はjp


type=
lite 簡易版
heavy 完全版


t=
アソシエイトID


dev-t=
Subscription Id （デベロッパトークン）


page=
結果が複数ページに渡る場合のページ数


mode=
検索する商品のジャンル


和書
books-jp


洋書
books-us


音楽
music-jp


音楽クラシック
classical-jp


DVD
dvd-jp


ビデオ
vhs-jp


エレクトロニクス
electronics-jp


ソフトウェア
software-jp


ゲーム
videogames-jp


ASINSearch=
商品固有のASIN(Amazon Standard Item Number)コード、もしくは書籍に付いているISBNコードを指定。 カンマ（,）で区切って複数指定可能


KeywordSearch=
検索単語


これ以外にも詳細な検索が行えるパラメータがいくつかあります。
XMLを解析する
アマゾンから返ってきたXMLは、PHP標準のDOM関数を使って自力で解析しても良いのですが、構造が複雑になると少々大変です。そこで、PEARのXML_Serializerパッケージに含まれているXML_Unserializerクラスを使います。これを使えば、XMLテキストを瞬時に連想配列にしてくれますので、後で好きな様にHTMLに加工できます。

（注） PEARがない場合、まずPEARを導入して下さい。
http://pear.php.net/package/XML_Serializer
[php]
// XML_Unserializerクラスの読み込み
require 'XML/Unserializer.php';

class AWS_QueryString {
  var $_params;

  function AWS_QueryString($token, $assoc,　$type = 'heavy', $format = 'xml',
$locale = 'jp') {
    $this->_params = array();
    // 基本データだけ入れておく
    $this->add('dev-t', $token);
  ...</description>
		<link>http://www.mt312.com/php/39/</link>
			</item>
	<item>
		<title>テンプレートエンジンについて</title>
		<description>PHPにテンプレートエンジンは必要か？
この話題はPHP users-mlでも取り上げられました。

PHPはSSIの様にHTML内に埋め込んで使う事ができます。では、テンプレートエンジンが使われているのは何故でしょうか。いずれにせよ、他人のスクリプトを使う場合、デザイン部分だけ独立している方が使いやすいですね。

Smarty
HTML_Template_IT
テンプレートエンジンの魅力
それは、ロジックとデザインの完全な分離です。PHPを知らなくても容易にデザインを変更できるようになります。高機能なテンプレートエンジンでは、表組みや日付のフォーマット変更、文章の要約処理、文字コードの変更まで行う事ができます。

	以下に該当する場合、テンプレートエンジンは特に威力を発揮します。

	デザイン担当者がスクリプトを全く知らない場合。
	ロジックよりデザインを先行して作成する場合。
	複雑なデザインの場合。



PHPタグを上手に使おう
テンプレートエンジンは必要ないと判断した場合でも、作り方次第で上手くデザインを分離させられます。まず、PHPを含むデザインファイルは.phpとせず.htmとしておき、PHPでincludeして使うようにします。これでブラウザでデザインを確認しやすくなります。そして、どこでもPHPが書けてしまう事に甘えずに、スクリプトのみのファイルで極力データを加工しておきます。

※.htmとして良いのは画面への出力部分を極力抽出した場合に限ります。と言うのは、直接アクセスされた場合に中身が丸見えになってしまうからです。関数定義を含めるのも避けましょう。

結果データは &#60;h1&#62;&#60;?echo $arr["title"] ?&#62;&#60;/h1&#62; の様にHTMLに埋め込みます。ところが直接ブラウザで見るとデータが消えてしまっているのが分かります。これはテンプレートエンジンを使わない場合は仕方がありません。PHPタグ&#60;??&#62;はブラウザには表示されません。これが気に入らない場合はテンプレートエンジンを導入しましょう。
デザインを崩してしまう書き方

属性内のクォーテーション
&#60;a href=&#34;&#60;?echo $arr[&#34;url&#34;] ?&#62;&#34;&#62;url&#60;/a&#62; → &#34;&#62;url
これは「'」を使えば回避できます。
&#60;a href=&#34;&#60;?echo $arr['url'] ?&#62;&#34;&#62;url&#60;/a&#62; → url

比較やオブジェクトで使う &#34;&#62;&#34;
&#60;? if($n&#62;=10){ ?&#62; → =10){ ?&#62;
&#60;?echo $obj-&#62;value ?&#62; → value ?&#62;
これらは最近のWEB作成ソフトでは上手く解釈してくれますが、ブラウザでも確認できた方がより便利です。そこで、属性内のクォーテーションと&#34;&#62;&#34;は使わないようにします。テンプレート内で使う変数は予め連想配列にしておき、ループが必要な場合はそれを配列に溜めます。ループにはforeachを活用します。連想配列はクォーテーションを使うので、extract($hash);でキー文字列の変数に変換します。
[php]
< ? foreach($hashes as $hash){extract($hash); ?>
< ?echo $title ?>
written by < ?echo $name ?> on < ?echo date('m/d H:i', $timestamp) ?>
< ?echo $message ?>
 </description>
		<link>http://www.mt312.com/php/38/</link>
			</item>
	<item>
		<title>クラスとメソッドの動的使用</title>
		<description>PHPでは大胆な事ができます。
インスタンスメソッドを呼ぶ
[php]
class ClassA {
  function method() {
    print 'method';
  }
}

// 大文字小文字は区別しない
$className = 'classa';
$methodName = 'method';
$obj = new $className();// new ClassA();
$obj->$methodName();// $obj->method();
[/php]
staticメソッドを呼ぶ
インスタンスメソッドと若干異なります。
[php]
class ClassA {
  function method() {
    print 'method';
  }
}

// 大文字小文字は区別しない
$className = 'classa';
$methodName = 'method';
$className::$methodName();// parse error
eval("$className::$methodName();");// evalを使えばOK
call_user_func(array($className, $methodName));// こちらがベスト
[/php]
まとめ
いずれも非常に便利です。
クラス名や関数名を文字列で登録しておくだけで実行できます。
GDのバージョンによって、画像処理関数を切り替えたい時にも使えます。
又、登録モード、削除モード、検索モードなど、モード毎に1つの関数を呼ぶ便利な使い方もできます。わざわざ、if(登録モードなら)なんて分岐せずに済みますね。 </description>
		<link>http://www.mt312.com/php/37/</link>
			</item>
</channel>
</rss>
