※ パクレゼルヴではWeb開発エンジニアを大募集中!詳細はこちら

Archive

Archive for the ‘PHP’ Category

コーディングルール  

2010/6/8 火曜日 17:35:14

皆さん、コードを書くときコーディングルールを気にしますか?
自分は気にしてませんダメな人ですね。

ということで、好きなフレームワークCodeIgniterの例にならって、まとめ的に書きとめようと思う。

◆クラス名・メソッド名
クラス名は常に最初の文字を大文字にしなければいけません。コンストラクタメソッドはクラス名と同じにします。
メソッド名は、すべて小文字にして動詞など含めてわかり易い名前にするべきです。

間違い:
class superclass
class SuperClass

正しい:
class Super_class

間違ったメソッド名と正しいメソッド名

間違い:
function fileproperties()		// 表現がわかりにくく、アンダースコアが抜けている
function fileProperties()		// 表現がわかりにくく、キャメルケースが使われている
function getfileproperties()		// ベター! しかしながら可読性に欠ける
function getFileProperties()		// キャメルケースが使われている
function get_the_file_properties_from_the_file()	// 長過ぎる

正しい:
function get_file_properties()	// 説明的、アンダースコア、全て小文字

◆変数名
変数名はクラス名とほぼ同じ。「$p」とか単語になっていないものはループ変数のみにつかいます。

◆定数
定数は変数とほぼ同じ。だけど、例外として大文字のみを使用します。

◆TRUE、FALSE、NULL
常に大文字を使用します。

◆論理演算子
「||」は解像度の低いデバイスでは良く見えなく「11」に見えてしまうことがあることから「OR」を使用する。

間違い:
if ($foo || $bar)
if ($foo AND $bar)  // 問題無いがアプリのシンタックスハイライト機能にはお勧めできない
if (!$foo)
if (! is_array($foo))

正しい:
if ($foo OR $bar)
if ($foo && $bar) // 推奨
if ( ! $foo)
if ( ! is_array($foo))

◆インデント
Allman スタイルのインデントを利用します。クラス宣言を除いて、括弧は常に1行に単独で配置し、その制御ステートメントと同じ深さにインデントします。

間違い:
function foo($bar) {
	// ...
}

foreach ($arr as $key => $val) {
	// ...
}

正しい:
function foo($bar)
{
	// ...
}

foreach ($arr as $key => $val)
{
	// ...
}

◆括弧の前後のスペース
一般的に、括弧の前後にはスペースを追加すべきではありません。
例外として、括弧内に引数を記述する PHP 制御構造(if,switch,for,while等)では、関数と区別しやすくしたり可読性を高めるために、常にスペースを配置すべきです。

間違い:
$arr[ $foo ] = 'foo';

正しい:
$arr[$foo] = 'foo'; // 配列のキーの前後には空白を入れません

間違い:
function foo ( $bar )
{

}

正しい:
function foo($bar) // 関数定義の括弧の前後には空白を入れません
{

}

間違い:
foreach( $query->result() as $row )

正しい:
foreach ($query->result() as $row) // PHPの制御構造の後ろには空白を1つ入れますが、中の括弧には入れません

◆PHPエラー
コードはエラーを起こさず、潜在的なWARNINGやNOTICEもなく動作するようにしましょう。
例えば、最初にisset()で確かめずに($_POST配列のキーのような)未定義の変数を使わないようにしてください。

あくまでチラシの裏ですので、人それぞれのコーディングルールなので、特に決まりごとが無ければやりやすい方法でやるべきかとー。

下音タヌキ PHP

簡単な入力フォームこそ気を付けよう  

2010/5/31 月曜日 22:57:17

phpで作る入力フォームでやってしまいがちな事を紹介します。

入力した情報を確認画面で表示させる、というよくあるパターン。
(ベタ書きで失礼します。)

▼form.php(入力画面)

<form method="post" action="./confirm.php">
	ニックネーム: <input type="text" name="title" value="" /><br />
	<input type="submit" value="送信" />
</form>

▼confirm.php(確認画面)

< ?php
if(isset($_POST['title']) && $_POST['title']){
	echo htmlspecialchars($_POST['title']);
}else{
	echo '未入力';
}
?>

これ、一見問題無さそうですが、ある文字を入力すると未入力と表示されてしまいます。

そうです。

0(ゼロ)です。

TRUEかFALSEかで判断しているため、post送信された0はFALSEとして見なされます。

なのでこうやりましょう。

▼confirm.php(確認画面)

< ?php
if(isset($_POST['title']) && $_POST['title'] != ''){
	echo htmlspecialchars($_POST['title']);
}else{
	echo '未入力';
}
?>

どうしても「0」という名前で登録したいんだ!というお客様がいないとも限りませんからね。。

 

さて、もう一つ紹介します。

クロスサイトスクリプティング(XSS)のお話です。

今度は同じphpファイル自身にpost送信するパターン。
▼http://example.com/test.php

< ?php
// なんらかの処理
?>
<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
…
</form>

action先に何の迷いも無く

$_SERVER['PHP_SELF']

と書くと、実はこれが結構危ないのです。

他のhtmlファイルで下記のようなソースを書き、上記のスクリプトへのリンクを貼りましょう。
そして、クリックしてみましょう。(攻撃!)

<a href="http://example.com/test.php/%22%3E%3Cscript%3Ealert('xss');%3C/script%3E/">攻撃</a>

JavaScriptが実行され、アラートが表示されるかと思います。

URLの後半部分はURLエンコードされたものです。

$_SERVER['PHP_SELF']は、渡されたURIのホスト部分の後から、
GETクエリ部分の前まで(?の前まで)をURLデコードした形で格納します。

つまり、$_SERVER ['PHP_SELF']は、

/test.php/"><script>alert('xss');</script>/

となるので、

<form method="post" action="/test.php/"><script>alert('xss');</script>/">

となり、JavaScriptが実行されてしまうわけです。

 
では結論。
このようにしましょう。

</form><form method="post" action="">
…
</form>
<form method="post" action="<?php echo $_SERVER['SCRIPT_NAME']; ?>">
…
</form>
<form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>">
…
</form>

空にしてあげるのが一番ラクかと思います。

MoriMoriMoriMori HTMLとか, PHP

fgetcsvがphp5で文字化けする  

2010/5/31 月曜日 20:45:08

仕事でCSVを使うことがあったんですけど、その時に悩んだことを書こうと思います。

fgetcsv()という便利な関数があるんですが、php5で使うと文字化けするんですよ。
なぜ文字化けが起きるのかと言うと、
「fgetcsvはマルチバイト文字の2バイト目に’\'があると、直後のダブルクォーテーションをエスケープするという、少なくともエクセルの仕様にはない独自解釈をする。」
らしい。
訳わかんない事言ってるなーと思いますが、要はShift-JIS形式のCSVをfgetcsv()関数でと文字化けするみたいです。

いろいろと調べた結果、
「PHP 5.0 の fgetcsv() はロケールの設定に依存します。」
とのことなので、

setlocale('LC_ALL', ja_JP.EUC_JP);

でロケールを設定してあげれば直ら・・・・ない。
ぱっと見た感じ直ってるように見えるんですけど、機種依存文字が化けてしまう。

それじゃあCSVをまずUTF-8とかに変換しちゃえば解決するんですけど、実はfgetcsvぽく動いて、しかもバグがない
関数があったので、それを使うことに。

function fgetcsv_reg (&$file, $length = null, $d = ',', $e = '"') {
	$d = preg_quote($d);
	$e = preg_quote($e);
	$_line = "";
	$eof = FALSE;
	while (!$eof && !feof($file)) {
		$_line .= (empty($length) ? fgets($file) : fgets($file, $length));
		$itemcnt = preg_match_all('/'.$e.'/', $_line, $dummy);
		if ($itemcnt % 2 == 0) $eof = true;
	}
	$_csv_line = preg_replace('/(?:\r\n|[\r\n])?$/', $d, trim($_line));
	$_csv_pattern = '/('.$e.'[^'.$e.']*(?:'.$e.$e.'[^'.$e.']*)*'.$e.'|[^'.$d.']*)'.$d.'/';
	preg_match_all($_csv_pattern, $_csv_line, $_csv_matches);
	$_csv_data = $_csv_matches[1];
	for($_csv_i=0;$_csv_i<count ($_csv_data);$_csv_i++){
		$_csv_data[$_csv_i]=preg_replace('/^'.$e.'(.*)'.$e.'$/s','$1',$_csv_data[$_csv_i]);
		$_csv_data[$_csv_i]=str_replace("'","\\'",$_csv_data[$_csv_i]);
		$_csv_data[$_csv_i]=str_replace($e.$e, $e, $_csv_data[$_csv_i]);
	}
	return empty($_line) ? false : $_csv_data;
}

この関数で、CSVを正規表現で切ってます。
使うときはこういう感じで

$new_data = array();
$header = array('name','zip','addr','tel','fax','manage','security','url');

$filename = 'CSVファイルパス';
$file = fopen($filename, "r");

while (($data = fgetcsv_reg($file)) !== false) {
	$_enc_to=mb_internal_encoding();
	$_enc_from=mb_detect_order();
	mb_convert_variables($_enc_to,$_enc_from,$data);
	$num = count($data);
	for ($c=0; $c < $num; $c++) {
		$new_data[$header[$c]] = $data[$c];
	}
	foreach($new_data as $key => $val){
		echo $key." : ".$val."<br />";
	}
}

こういう風に使えば、fgetcsv()関数とほぼ同じ動作をし、さらに文字化けもおきません。

こういうオリジナルの関数を自分でも作れるようになりたいですね。。

つっしー PHP, 未分類

配られたカードで勝負するしかねぇんだ  

2010/5/6 木曜日 12:06:30

自分が普段使っているレンタルサーバーはセキュリティ云々のおかげで「.forward」ファイルを設置することが出来ない。
でも、空メールの処理を入れたいので、どうしようか悩んだ結果。
無理やりPOP3に繋いで処理すりゃいいじゃんという結論に。

今回のサーバーにはNET_POP3のPearライブラリが入っているので、これを使うことに。
入ってない場合は適当にコマンド打ってインストールでもすればいいと思うよ。

pear install Net_POP3

で、簡単に処理を書く。

require 'Net/POP3.php';

$pop3 = new Net_POP3();

$ret = $pop3->connect("host", 110);
if (!$ret){
	$pop3->disconnect();
	exit;
}

$pop3->login("id", "ps", true);

$ret = $pop3->numMsg();
if (!$ret || $ret == 0){
	$pop3->disconnect();
	exit;
}

print "$ret : メッセージ件数<br />\n";

$ret = $pop3->getListing();
$i = 1;
if ($ret !== false){
	$pop3->disconnect();
	exit;
}
foreach($ret as $key => $val){
	// 中身の処理
	$pop3->deleteMsg($val['msg_id']);
}
$pop3->disconnect();

これを適当にcronで回して処理してれば「.forward」的な処理が出来るかなー。
\e

下音タヌキ PHP

うごくひと2。(初投稿)  

2010/4/30 金曜日 18:09:36

はじめまして!今年から新卒で入社したつっしーです。
のびーにょさんの後輩になります。
早く業務に慣れて活躍できるようになるのが目標ですが、会社のプログラムは、学生時代に僕が触ってきたプログラムと比べて、とても複雑・・・早く慣れたいなぁ。

今、「うごくひと2」という携帯アクセス解析ツールについて調べています。
うごくひと2は、通常版を使う場合、解析対象ページに

<img src="**********?u=********&amp;guid=ON&amp;ut=*&amp;h=********" alt="携帯アクセス解析" height="1" width="1" />

このコードを追加するだけなのですが、今回は上級版を使おうと思います。
まず、通常版コードのURLとパラメータを正規表現を使って抽出し、serialize関数を使ってシリアル化します。

//url抽出
$tag_regex = '/<img \s[^/>]*?src\s*=["\'](([^\"]*)[^>]*)[/]/i';
preg_match($tag_regex,$_POST['bic_analytics'], $matches);
//urlのみ取得
$url_regex = '/http[^\?]*/';
preg_match($url_regex,$matches[2],$matcheurl);

$param_bic = array();
$param_bic[url] = $matcheurl[0];
//URLを分解して配列で返す
$strtest = parse_url($matches[2]);
//パラメータ取得
foreach($strtest as $key => $var){
	if($key == 'query'){
		$keyword = explode('&amp;', $var);
		foreach($keyword as $vval){
			$bic = explode('=',$vval);
			if($bic[0] != 'guid'){
				$param_bic[$bic[0]] = $bic[1];
			}
		}
	}
}
$bic_analytics = serialize($param_bic);

正規表現で通常版コードの中の必要な部分を抽出してシリアル化してます。
URLのパラメータ抽出も正規表現で抽出しようかと思いましたが、parse_url関数を使ったほうがいいよ、という助言をいただいたので使っています、実際そのほうが簡単でした。

$param_bicの中はこうなってます。

Array
(
[url] => ************
[u] => *******
[ut] => *
[h] => ******
)

これをserializeするとこうなります。

a:4:{s:3:"url";s:1:"************";s:1:"u";s:1:"*******";s:*:"ut";
s:1:"*";s:1:"h";s:1:******";}

これをunserializeしたら元に戻ります。
この各要素を、上級版コードのURLに入れていきます

$ret = $_POST['bic_analytics'];
//unserializeして元の配列に戻す。
$arrBic = unserialize($ret);

//配列の要素をURLに入れる。
$UH2url  = $arrBic['url'].'?u='.$arrBic['u'].'&amp;h='.$arrBic['h'].'&amp;ut='.$arrBic['ut'].'&amp;guid=ON&amp;qM=';
$UH2url .= urlencode(isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:'').'|Az|'.(int)($_SERVER['SERVER_PORT']);
$UH2url .= '|'.urlencode($_SERVER['HTTP_HOST']).'|'.urlencode($_SERVER['REQUEST_URI']);
$UH2url .= '|H|&amp;ch=UTF-8&amp;sb='.urlencode('[page title]');
print '<img src="'.$UH2url.'" alt="携帯アクセス解析" width="1" height="1" border="0" />';

こんな感じで上級版コードを作ります。

まだまだ未熟ですが、今後もよろしくお願いします。

つっしー PHP