Archive for the ‘PHP’ Category

Mac で PhpDocumentor を pear を使わずに使えるようにする

pear から PhpDocumentor をインストールしたのですが使えませんでした。
そのため以下を参考に直接ファイルをダウンロードして使えるようにします。
phpDocumentor – phpspot

好きなディレクトリに置いて、そのディレクトリに移動して以下を実行すれば生成ができます。-d はソースのディレクトリ、-t にドキュメントの生成先を指定します。

1
$ php /path/to/phpdoc -d /path/to/src/ -t /path/to/doc/

ちなみに WordPress をドキュメントとして生成しようとしたらできませんでした。ERROR と WARNING が大量に出力されて何も生成されませんでした。

単純なファイルであれば問題なく生成されました。

Ubuntu で PHP + MySQL + バーチャルホストでの開発環境を最速で作る方法

今日は外部要因で疲れていたので、休憩を兼ねて Ubuntu を弄りました。

ぽけけーと弄ってたら、とても簡単に開発環境が構築できてしまったので、もしかすると苦戦するかもしれない人たちのためにメモを残しておきたいと思います。参考にしてください。

私は以下のページを参考に作業させていただきました。
UbuntuでApache+PHP+MySQLの開発! | 不定期ソフトウェア開発

インストール

以下のコマンドでインストールができます。適当に進めてください。
終わりましたら http://localhost/ にアクセスするとページが表示されます。
ドキュメントルートは /var/www でした。

1
sudo apt-get install apache2 php5 php5-gd mysql-server php5-mysql phpmyadmin

Apache を起動すれば PHP が使えるようになります。

1
sudo apache2ctrl start

バーチャルホストの設定

http://example で /var/www/example 以下のファイルを表示されるようにします。
私が便利だと思う方法でやっていきますので、定石を知りたい方は他のサイトを参考にしてください。

追記: また記事末尾に @isidai さんからのアドバイスをいただきましたので、まとめておりますので、先にそちらをご覧いただくと良いかもしれません、私はまだ試していませんが、いつか試します。

まず /etc/apache2 に移動します。大概の設定ファイルはここにあるはずです。

1
cd /etc/apache2

次にバーチャルホストが大量に増えても編集がしやすいように、バーチャルホストの設定ファイル用のディレクトリを作成します。ここにたくさんの設定ファイルを入れていく予定です。

1
sudo mkdir /etc/apache2/vhosts

vhosts 内のバーチャルホストの設定ファイルを読み込むように httpd.conf に読み込みの設定を加えます。

1
sudo vi httpd.conf

以下のように拡張子 .conf ファイルを読み込むように書き加えてください。

#vhosts
Include /etc/apache2/vhosts/*.conf

バーチャルホストの設定ファイルを作成し、編集をします。

1
2
3
cd /etc/apache2/vhosts
sudo touch example.conf
sudo vi example.conf

以下のように記述してください。

<VirtualHost *:80>
        DocumentRoot "/var/www/example"
        ServerName example
        <Directory "/var/www/example">
                order deny,allow
                allow from All
        </Directory>
</VirtualHost>

次に hosts ファイルを編集します。

1
sudo vi /etc/hosts

以下のように記述します。

127.0.0.1 example

これで設定は一通り終わりですので、Apache を再起動します。

1
sudo apache2ctl restart

適当に /var/www/example 以下にファイルを置いて http://example からそのファイルへアクセスすると見れるはずです。何か問題がありましたらご指摘ください。

@isidai さんからのアドバイス

どうやらもっと簡単な a2ensite コマンドを使う方法もあるようです。
記事投稿後、すぐにアドバイスをいただきました。

@soraiy いやいや、全然あれでもいいし楽ですけどねー。Debianだと /etc/apache2/available-sites の中に設定ファイルを入れて、 a2ensite example.com みたいにすると有効化、 a2dissite を使うと無効化できます。

@soraiy モジュールも a2enmod / a2dismod コマンドで有効化・無効化が切り替えられます。単にシンボリックリンクを sites-enable / mods-enable に貼ったり消したりしてる仕組みですけど、それがDebianでは標準コマンドなので…

どうやら簡単に追加・削除ができるそうで、こちらが一般的な方法かもしれません。
ありがとうございました @iisidai さん、とても勉強になりました!

PHP でシリアライズしたデータが壊れてしまう場合は base64_encode をする

本日 PHP で配列にデータを格納し serialize してデータの受け渡しをしていたら IE8 で unserialize してデータを復元することができないバグに遭遇しました。具体的には以下のようなエラーが発生。

Notice: unserialize() [function.unserialize]: Error at offset 0 of 2 bytes in /var/usr/to/path

うまく値の受渡しができていないのか、途中でデータが書き換わってしまったのか、様々な問題を調査しましたがどれも該当せず3時間。Web 上で情報を集めるもそれらしい問題に遭遇してる人がおらず解決できず。

どうしようもなくなったので、30個くらいある配列すべてをひとつずつ調べてチェックしていくと、どうやら30個の中の3配列が原因だということが判明しました。日本語の文字コードがおかしいのかと思って調査しましたが、すべて UTF-8 で問題なし。

試しに問題の配列に同じ日本語文字列をいれたところなぜかデータを復元できた。
でも実際は様々なデータが入ってくるので、それぞれに応じて対処しなければいけない。
様々な方法を試みたが結局解決せず。もうダメだと思って Twitter にもう無理です…タスケテ…と書いたところ @kiyotchi さんから神の言葉を授かりました。

@soraiy よく流れを見てないけど、IEに流したシリアル化データをunserializeするとエラーがでるということですか?例えば、base64エンコードしてやり取りするとかはどうかな?

できました。送信側は以下のようにして。

1
2
$data = serialize($array);
$data = base64_encode($data);

受け取り側をこうする。

1
2
$data = base64_decode($data);
$array = unserialize($data);

本当に助かりました。シリアライズは無敵だと信じていた私のミスでした。

どうやら HTTP経路上でバイト数が変わったりゴミデータを拾ったりすることがあるらしいです。そのため適宜データをやり取りする場合はなるべくエンコードしたほうが良さそうです。もう以下のような関数作って使ったほうが良いかもしれないですね。

1
2
3
4
5
6
7
8
9
10
11
12
13
// シリアライズしつつ base64 エンコードをする関数
function serialize_base64_encode($array) {
	$data = serialize($array);
	$data = base64_encode($data);
	return $data;
}
 
// base64 デコードしつつシリアライズされたデータを復元する関数
function unserialize_base64_decode($data) {
	$data = base64_decode($data);
	$array = unserialize($data);
	return $array;
}

まさか一日も時間を取られるなんて驚きました。
今回は同じようなことに苦しんでる人の助けになればいいと思って、この記事をかきました。
途中本当に泣きそうでした。ありがとう @kiyotchi さん!

Mac に MacPorts を使って MySQL と PHPMyAdmin をインストールする方法

Mac に MySQL をインストールする。とても難しそうなことですが、なんとか終わりました。所要時間は4時間くらいです。
実際には MacPorts を使ってコマンドを叩けば良いのですが、私の脳が足りず時間がかかってしまいました。あ、ちなみに MacPorts を入れてない場合は Mac OS 10.6 に MacPorts を入れる際に役に立ったサイトを参考にして入れてくださいね。

MySQL をインストール

Twitter と MySQL / Mac OS X を参考に進めてみました。
ほぼ引用ですが、躓いたところに補足コメントしたり、順序を変えたりしています。

1
$ sudo port install mysql5 +server

あ、以下の2種類に分解してもいけます。

1
$ sudo port install mysql5
1
$ sudo port -v install mysql5-server

MySQL の初期化をします。

1
$ sudo -u mysql mysql_install_db5

MySQL を起動テスト。
上手く行けば SUCCESS とでるはずですが ERROR と出れば何か上手くいなかった可能性がありそうです。

1
$ sudo /opt/local/share/mysql5/mysql/mysql.server start

root ユーザにパスワードを設定します。ここでは password と設定します。

1
$ /opt/local/lib/mysql5/bin/mysqladmin -u root password 'password'

ログインができるかテストします。

1
/opt/local/lib/mysql5/bin/mysql -u root -ppassword

ログインの仕方はちょっと躓きましたが以下のページが参考になりました。
MySQL をインストール / Mac OS 10.4.2 | Bowz::Notebook

起動時に MySQL サーバーを自動的に起動するように設定できるみたいです。

1
$ sudo launchctl load -w /Library/LaunchDaemons/org.macports.mysql5.plist

PHPMyAdmin

MySQL はきちんと動いていることを確認したことですし、PHPMyAdmin をインストールしてみましょうか。
今回は最新バージョンの 3.3.3 を入れてみることにしました。

任意のディレクトリに PHPMyAdmin を入れてログインしようとすると以下のエラーがでる場合は PHP の方を設定する必要があります。

#2002 MySQL サーバにログインできません

php.ini を設定しましょう。ちなみに、まだ php.ini がまだ無い人は /private/etc/ に php.ini.default がありますので、それをコピーして使いましょう。

1
2
$ sudo cp /private/etc/php.ini.default /private/etc/php.ini
$ sudo vi /private/etc/php.ini

socket のパスがダメのようです。以下のサイトがとても参考になりました。最高に素晴らしいページだと思います。前々から目にしていたサイトだったので、まさかここでここまで助けられるとは。この部分だけで4時間詰まりましたもん。
Mac OS X LeopardでPHPからMySQLに接続する – このブログは証明できない。

1217 行あたりだと思うんですが、mysql.default_socket を以下のように変更してください。

mysql.default_socket = /opt/local/var/run/mysql5/mysqld.sock

1276 行あたりだと思うんですが、mysqli.default_socket を以下のように変更してください。

mysqli.default_socket = /opt/local/var/run/mysql5/mysqld.sock

これで動くはずだと思います。

以下のサイト達の記事の一部で php.ini に問題があるかもしれないと思えました。
Mac にphpMyAdmin を入れる手順 – Slow Dance
Mac OS X 10.5 と php.ini – 風ブログ

そして最後に PHPMyAdmin にログインすると以下のようなエラーが出るかもしれません。

リンクテーブルを処理するための追加機能が無効になっています

以下のサイトに解決方法がのっていますよ。素晴らしいページですよね。大好きです。
phpMyAdminのインストール

MySQL の再起動

my.cnf を書き換えた場合は再起動が必要です。結果的に今回は書き換える必要はなかったのですが、私はいろいろ弄っていたので。以下のページが参考になりましたので紹介とともに引用します。
Mac portsで入れたMySQLを再起動する方法 – [サ]ロンパスの日記

1
2
$ sudo launchctl stop org.macports.mysql5
$ sudo launchctl start org.macports.mysql5

その他の参考にしたサイト

ITO の メモ書き | RedhatES4.0 + phpMyAdmin-3.3.3 インストール
データベースの管理 | MySQL講座 [Smart]
MacPortsでMySQLをインストール — BONNOH FRACTION 13

PHP で地球が爆発するまでカレンダーを出力できるコードを書いてみた

以前PHP で変動的にシンプルなカレンダーを出力するソースコードを書いてみましたが、タイムスタンプを使っていたので、1970年から2038年までしかカレンダーを出力できませんでした。

今回はグレゴリウス暦1年1月より、地球が爆発するまでのカレンダーを出力できるコードを作りましたので、せっかくなので公開しておきます。

使い方

1
<?php echo calender(2010, 6); ?>

第1引数に年を、第2引数に月を入れると、table で組まれたカレンダーを返します。

コード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function calender($year, $month) {
 
	$jpweek = Array('日', '月', '火', '水', '木', '金', '土');
	$days = cal_days_in_month(CAL_GREGORIAN, $month, $year);
	$week = jddayofweek(cal_to_jd(CAL_GREGORIAN, $month, 1, $year), 0);
	$day = array_pad(Array(), 42, 0);
 
	for($i = $week; $i <= $week + $days - 1; $i++) $day[$i] = $i - $week + 1;
 
	$calender  = '<table>';
	$calender .= '<tr>';
 
	foreach($jpweek as $value) $calender .= '<th>' . $value . '</th>';
 
	$calender .= '</tr>';
 
	foreach($day as $key => $value) {
		if($key % 7 == 0) $calender .= '<tr>';
		$calender .= '<td>';
		$calender .= ($value) ? $value : '&nbsp';
		$calender .= '</td>';
		if($key % 7 == 6) $calender .= '</tr>';
		if($key >= $week + $days - 1 && $key % 7 == 6) break;
	}
 
	$calender .= '</table>';
 
	return $calender;
 
}

本当に地球が爆発するまでのカレンダーを出力できるのか

カレンダーを出力させ続けて、カレンダーの結果に偏りがないか調査することで、カレンダーが正常に出力されているかを確認することにしました。偏りを調査するといっても、ひとつずつ調査していると寿命の方が先に来てしまうと思うので、その月の日数と始まりの曜日の統計を取る事にしました。

コード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
set_time_limit(600);
$data['days'] = array_pad(Array(), 7, 0);
$data['week'] = array_pad(Array(), 7, 0);
for($i = 1; $i <= 1000000; $i++) {
	for($c = 1; $c <= 12;  $c++) {
		$calender = $this->Reservation->calender_check($i, $c);
		$data['days'][$calender['days']]++;
		$data['week'][$calender['week']]++;
	}
}
print_r($data);
 
function calender_check($year, $month) {
 
	$days = cal_days_in_month(CAL_GREGORIAN, $month, $year);
	$week = jddayofweek(cal_to_jd(CAL_GREGORIAN, $month, 1, $year), 0);
 
	$return['days'] = $days;
	$return['week'] = $week;
 
	return $return;
 
}

100万年の統計結果

Array
(
[days] => Array
(
[31] => 7000000
[28] => 757500
[30] => 4000000
[29] => 242500
)

[week] => Array
(
[1] => 1710000
[4] => 1712500
[0] => 1720000
[2] => 1717500
[5] => 1717500
[3] => 1712500
[6] => 1710000
)

)

PHP で機種依存文字をフィルタリングする関数を作ってみた

PHP で機種依存文字をフィルタリングするのはかなり大変でした、当初は正規表現など使って3行くらいで書けてしまうだろうと思っていたのですが、なぜかうまく引っかからなかったりとたくさんのバグや問題にぶつかり、最後消去法で辿り着いたような関数なので、改良の余地があればどんどん教えてください。

以下、格好付けて説明していきます。

説明

1
platform_dependent_characters_filter(string $text)

引数に文章を渡します。複数行でも構いません。機種依存文字が含まれていれば、true を、含まれていなければ false を返します。

例を紹介しますと。

1
2
3
4
5
if(platform_dependent_characters_filter(string $text)) {
   echo 'エラー!機種依存文字が含まれています。';
} else {
   echo '機種依存文字が含まれていませんでした。';
}

コード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function platform_dependent_characters_filter($text) {
 
	mb_regex_encoding('UTF-8');
 
	$pdc = '①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑯⑰⑱⑲⑳ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ㍉㌔㌢㍍㌘㌧㌃㌶㍑㍗㌍㌦㌣㌫㍊㌻㎜㎝㎞㎎㎏㏄㎡㍻〝〟№㏍℡㊤㊥㊦㊧㊨㈱㈲㈹㍾㍽㍼∮∟⊿纊褜鍈銈蓜俉炻昱棈鋹曻彅丨仡仼伀伃伹佖侒侊侚侔俍偀倢俿倞偆偰偂傔僴僘兊兤冝冾凬刕劜劦勀勛匀匇匤卲厓厲叝﨎咜咊咩哿喆坙坥垬埈埇﨏塚增墲夋奓奛奝奣妤妺孖寀甯寘寬尞岦岺峵崧嵓﨑嵂嵭嶸嶹巐弡弴彧德忞恝悅悊惞惕愠惲愑愷愰憘戓抦揵摠撝擎敎昀昕昻昉昮昞昤晥晗晙晴晳暙暠暲暿曺朎朗杦枻桒柀栁桄棏﨓楨﨔榘槢樰橫橆橳橾櫢櫤毖氿汜沆汯泚洄涇浯涖涬淏淸淲淼渹湜渧渼溿澈澵濵瀅瀇瀨炅炫焏焄煜煆煇凞燁燾犱犾猤猪獷玽珉珖珣珒琇珵琦琪琩琮瑢璉璟甁畯皂皜皞皛皦益睆劯砡硎硤礰礼神祥禔福禛竑竧靖竫箞精絈絜綷綠緖繒罇羡羽茁荢荿菇菶葈蒴蕓蕙蕫﨟薰蘒﨡蠇裵訒訷詹誧誾諟諸諶譓譿賰賴贒赶﨣軏﨤逸遧郞都鄕鄧釚釗釞釭釮釤釥鈆鈐鈊鈺鉀鈼鉎鉙鉑鈹鉧銧鉷鉸鋧鋗鋙鋐﨧鋕鋠鋓錥錡鋻﨨錞鋿錝錂鍰鍗鎤鏆鏞鏸鐱鑅鑈閒隆﨩隝隯霳霻靃靍靏靑靕顗顥飯飼餧館馞驎髙髜魵魲鮏鮱鮻鰀鵰鵫鶴鸙黑ⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹ¬¦'"';
	$pdc_array = Array();
	$pdc_text = str_replace(array("\r\n","\n","\r"), '', $text);
 
	while($iLen = mb_strlen($pdc, 'UTF-8')) {
		array_push($pdc_array, mb_substr($pdc, 0, 1, 'UTF-8'));
		$pdc = mb_substr($pdc, 1, $iLen, 'UTF-8');
	}
 
	foreach($pdc_array as $value) {
		if(preg_match("/(" . $value . ")/", $pdc_text)) {
			return true;
			break;
		}
	}
	return false;
}
  • 複数行に対して preg_match() で検索をかけることができなかったため、そもそも検索をかける文章の改行を7行目で除去してチェックをしています。助けて!
  • $pdc にチェックしたい文字列を入れて行きます。1文字ずつにしか対応しておりません。
  • str_split() がマルチバイトに対応していなかったので、9行目辺りで頑張ってガリガリ分割しています。
  • もしこれをコピーする場合に注意して欲しいことは、貼りつけた際にすべての文字が文字化けていないかという点です。文字化けて ? などが入ってしまうと、どんな文字でもひっかかるというバグが発生します。

参考にしたページ一覧

UTF-8 で半角・全角を数えるには一度 Shift-JIS に変換から行う

UTF-8 でマルチバイト文字は3バイトとして数えられます。PHP の strlen で数える場合に、英数字なら1バイトですが、日本語などのマルチバイト文字だと3バイトが帰ってきて、半角・全角の計算が難しくなります。

対処方法としまして、最近思いついたんですが、一度文字列を Shift-JIS に変換してから文字数の計算を行います。

1
strlen(mb_convert_encoding($bar, "sjis"));

こうすることで、マルチバイト文字は2バイト、半角英数字なら1バイトで取得できるようになり、半角・全角の計算を行えます。半角・全角なんて久しぶりに聞きましたが、拘る人は拘るようなので、ひとつ覚えておきたいところですね。

PHP ファイルが文字化けてしまう場合は .htaccess で文字コードを UTF8 にする

PHP ファイルが文字化けてしまう。ファイルの保存文字コードも meta タグにしている文字コードも UTF-8 に揃えているのに、なぜか文字化けてしまう。そういう場合は、サーバーの文字コードも疑ってみましょう。

サーバーの文字コードが UTF-8 じゃない場合は、.htaccess で指定することが可能です。ただし、php_value, php_flag が禁止されているサーバーでは少し難しいかもですね。

1
2
3
4
5
6
7
php_value mbstring.internal_encoding UTF-8
php_value default_charset UTF-8
php_value mbstring.http_output UTF-8
php_value mbstring.language UTF-8
php_value mbstring.http_input auto
php_flag mbstring.encoding_translation On
php_value output_handler mb_output_handler

.htaccess が使えない場合、PHP ファイルの方で個別に指定することも可能です。詳しくは.htaccess で PHP の表示言語を切り替えるが参考になります。

mb_strlen() 関数で文字数を取得する際は第2引数に文字エンコードを必ず指定すること

mb_strlen() 関数で文字数を取得できます。

1
echo mb_strlen('ばけものがたり');

通常はこれで7と出力されますが、場合によっては17と出力する場合があります。原因は PHP の内部エンコーディングと文字列のエンコードが違うからみたいです。

参考: mb_strlen()関数の落とし穴 – PHP TIPS:ITpro

この場合は第2引数に文字エンコードを指定することで、解決します。

1
echo mb_strlen('ばけものがたり', 'UTF-8');

マルチバイド文字をなんかするときは、文字コードには絶対気をつけなければいけませんね。
そして、化物語面白いですよね。

PHP で文字列表現の日付をタイムスタンプに変換する方法

Y-m-d H:i:s の形式の日付をタイムスタンプを取得する際は以下のようにやってはいけません。ちゃんと関数があるのです!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
function date_array($date_str = '') {
   if($date_str == '')
     $date_str = date("Y-m-d H:i:s");
   $date_str = explode(' ', $date_str);
   $date['date'] = explode('-', $date_str[0]);
   $date['time'] = explode(':', $date_str[1]);
   $date['year'] = $date['date'][0];
   $date['month'] = $date['date'][1];
   $date['day'] = $date['date'][2];
   $date['hour'] = $date['time'][0];
   $date['minute'] = $date['time'][1];
   $date['second'] = $date['time'][2];
   return $date;
}
 
$date = date_array($date('Y-m-d H:i:s');
echo mktime($date['hour'], $date['minute'], $date['second'], $date['month'], $date['day'], $date['year']);
?>

こうなのです!

1
echo strtotime($date('Y-m-d H:i:s'));

アホだ。最近まで前者の方法でやってました。
初めて PHP 勉強したときは後者だったんですが、まさか退化するとは。