PHPコーディング

セッション導入で更にセキュアに

2013年07月03日
セッションを導入し、システムをよりセキュアにすることにしました。

セッションの役割、機能を学ぶには、こちらのサイトが役立ちます。
セッション管理 - PHP入門
セッション管理
基本的な使い方がわかります。

PHPのセッションの問題点を認識し、よりセキュアにするには、PHPのセッション管理の注意点メモ が参考になります。

PHPのセッション管理の注意点メモ で少し触れられているだけのセッションクッキーについては、クッキーとセッション管理を読むとわかります。

これらを踏まえて、システムにログイン後、セッションを使ってユーザ認証するソースをコーディングです。

【session_start();】をコールすると、クッキーにセッションIDがセットされます。クッキーが使えない環境もあると思いますが、URLにセッション情報を含めるのはいかがなものかと思うので、今回は使えない環境は無視です。
セットされたクッキーのセッションIDとサーバーにあるセッションIDが同じかどうかで判断します。

今まで使ってた認証も残しているので、2重3重のチェックが走ります。

PHPコーディング

PHPのおかしな動き、誤動作?

2013年04月07日
PayPal毎度ありがとうメール】をご購入いただいたことがご縁で、WordPressの改変を頼まれました。

WordPressのコメント投稿に「タイトル」を追加するっ!

WordPantsでもっと WordPress を楽しもう!
を見て改変したのだけれど、うまくいかないということでした。
追加したいのは、コメントした人の名前です。

説明を読む限り、そんなに難しくなさそうだと思い、やってみることにしました。
ところが、なぜかうまくいかないのです。
なぜか最後のコメントだけ、名前が取得できないのです。ループが最後まで回っていないのでしょうか・・・
それともデータベースがおかしい?

WordPressの件はできない旨をお伝えして、改変は取りやめにしていただきました。

少し間をおいてから思い出したのですが、【PayPal毎度ありがとうメール】も、その方のサーバーで動かなかったのです。
調べてみると、PHPが誤動作していました。
誤動作なんてありえるのか、と不思議なんですが・・・ ここではとりあえず誤動作ということで書いてみます。

他のサーバーではそんな誤動作は見たことがありません。ツールを動かすと必ず通る箇所なので、おかしければ他のユーザーさんの所でも、正常に動きません。
ところが、他の方からはそのような連絡は全くないのです。

PHPの設定の問題かなと思い調べたのですが、そのような事実は見つけられませんでした。

誤動作しているのは、
if(条件式){
 処理1
 return;
}
else{
 処理2
 return;
}
という構文です。

条件式が真なのに、処理1が行われていなかったのです。しかも、この構文があると必ず誤動作するというわけではないのです。

この時は
if(条件式){
 処理1
 return;
}
処理2
return;
と書き換えることで、正常に動くようになりました。

原因を突き止められないまま、WordPressの改変をお引き受けしたのですが、もしかすると何らかの関係があるのかもしれません。

ずっと以前、別のサーバーでも、PHPがおかしかったことがありました。その時は【curl_setopt】関数でした。
同じサーバー会社の別サーバー(おそらくハードが違う)では正常に動くのに、どうしても動かないアカウントがあったのです。
サーバー管理会社に問い合わせたのですが、サーバー管理会社も原因がわからないとのことでした。このときは、サーバー(おそらくハードそのもの)を変えてもらったら、すんなり動きました。

自分が契約しているサーバーではないので、とことん調べられませんが、何か不穏なものを感じてしまいます。

PHPコーディング

変数の型と内容をファイルへ

2013年03月17日
PHPでコーディングしていて、変数の中身を確かめたい場合、【ver_dump】関数で変数の型と内容をブラウザへ表示することができます。

しかし、ファイルへ書き出したい場合も多々あります。

以前から何か方法があるはずだと思っていましたが、今回調べてみました。

ob_start();
var_dump($val);
$result = ob_get_contents();
ob_end_clean();

$handle = fopen("tmp.txt", "w" );
fputs($handle, $result);
fclose($handle);

【$val】は配列でもOKです。

[PHP]var_dump関数の出力結果をファイルに保存する « Codaholic
に詳しい説明があります。

PHPコーディング

PHPでワーニングの非表示

2013年01月22日
PHPでエラー、ワーニング、ノーティスを出さない方法です。

【.htaccess】に【php_flag display_errors off】と加えればOKです。

あるいは【php.ini】に【display_errors=off】あるいは【error_reporting=0】とすれば表示されなくなります。

ソースから設定する場合、
ini_set("error_reporting", 0);】
ini_set("display_errors", "Off");】
あるいは
error_reporting(0);】
を加えます。

ソースへ記述すると、【php.ini】を値を上書きします。

不特定多数に販売するツールには、ソースへ記述しておくのが便利です。
ついでにテスト環境ではエラーやワーニングが表示され、配布時には非表示に、自動でなれば更に便利なんですが。
ソースをテストとリリースで変更するのは、忘すれそうで怖いし、やりたくないです。

滅多に変更しないクラスファイルへ記述、配布時に上書きしないようにする、
テスト環境にのみダミーフィルを含め、ファイルがあるときのみエラーを表示する、
なにか便利でミスの起こりにくい方法をさがさなければ。

また、【error_reporting】は定義済み定数を使って、様々な設定が可能です。
error_reporting
定義済み定数

人力検索はてな
シングスブログ
が参考になるかもしれません。

PHPコーディング

PHPで参照渡し

2013年01月21日
PHPの関数で参照渡しするには、
//関数
function func(&$val){
  ・
  ・
}
//呼び出し元
$callval = "abc";
func($callval);
とします。

関数の引数に【&】を付けると、参照引渡しというお約束です。
そして呼び足し元の変数には【&】は付きません。

知ってはいたのですが、ついうっかり呼び出し元の変数に【&】を付けてしまっていた箇所があったことがあります。関数をコピーして、呼び出し元へペースト、【&】を取り忘れていたのです。

販売しているツールのソースは、かなり前に修正したのですが、本当に全て直っているか、新たに取り忘れていないか、少し心配になってきました。

取り忘れていると、サーバーの設定によってはワーニングが出るのです。
エラーと違いワーニングなので、動作には問題ありません。開発者には見えて、ユーザーには見えない設定が望ましいです。
メジャーなサーバーではワーニングが出ない設定になっているので、問題はありません。
今までは、ワーニングが出る場合、ユーザー様の【php.ini】に【allow_call_time_pass_reference=off】を付け加えて貰っていました。

これをソースのほうから設定するように変更しようとと思い、【allow_call_time_pass_reference】につてい調べてみみました。
すると、バージョン5.4.0で削除されていました。

動いているバージョンは5.4.0より低いところも多々ありますが、バージョンアップされたときエラーが出るようになってしまう可能性もあるということです。使わないほうがいいでしょう。

そもそも消し忘れ、付けるつもりがないのに付いているので、リリース前にチェックできるのが一番です。

自分のサーバーで、今度は【allow_call_time_pass_reference=on】に設定し、チェックする方針にしました。

PHPコーディング

オーバーライドと親クラスのメソッド実行

2013年01月18日
親クラス
 子クラス1 親クラスを継承
 子クラス2 親クラスを継承
があるとします。

子クラス1
 日時取得
 ファイル名に子クラス1と設定
子クラス2
 日時取得
 ファイル名に子クラス2と設定
と実行したいとします。

この場合、共通している日時取得は親クラスでやりたくなります。

これらの処理を、クラス読み込み時に行いたかったので、
親クラスの【__construct】関数に日時処理を、
それぞれの子クラスの【__construct】にファイル名の設定処理を
書きました。

ところが、関数が同じなので、親クラスの【__construct】は子クラスの【__construct】で上書きされてしまい、親クラスの【__construct】に書いた日時処理は実行されません。
これをオーバーライドと言います。

では、親クラスの【__construct】に書いた日時取得処理を実行するにはどうすればよいか。
子クラスの【__construct】に【parent::__construct();】の処理を書けば実行されます。

親クラスの__construct
 日時取得
   子クラス1の__construct
    parent::__construct();
    ファイル名に子クラス1と設定
   子クラス2 __construct
    parent::__construct();
    ファイル名に子クラス1と設定

これで目的が達成されます。子クラスが2つくらいなら、子クラスの方へ書いてしまったほうが分かりやすいかもしれません。
ですが、子の数が増えると親クラスで共通に処理しておかないと、メンテナンスが大変になります。

PHPのオブジェクト指向入門
にとても分かりやすい記事があります。

PHPコーディング

配列キー重複チェック

2013年01月06日
PHPでコーディングしていて、配列キーが重複しているかどうか、調べたくなる状況に出くわしました。

自分で書こうかなと思ったのですが、PHPは関数がたくさん用意されているので、ありそうな気がして調べてみました。たくあんありすぎて迷うことが多いですが。

調べてみると案の定ありました。【array_count_values】関数です。
array_count_values(重複を調べたい配列);
でキーとその数が配列で返ってきます。

重複を調べたい配列が、
$array = array("No1", "No2", "No3", "No1", "No3");
の場合、
 [No1] => 2
 [No2] => 1
 [No3] => 2
という配列が返ってきます。

PHPコーディング

ファイルアップロード数

2013年01月04日
PayPal毎度ありがとうメール】のユーザーさんから、一部のファイルがブラウザからアップロードできないとの連絡がありました。
調べてみると、最後のファイルがアップロードできなくなっていました。
以前はできていたので、途中からできなくなったということです。

【$_FILES】を書き出してみました。最後のファイルだけ表示されません。認識していないということです。
バージョンを上げたとき、機能を追加しました。機能追加に伴い、アップロードしなければならないファイルの数が増えました。
これが原因ではないかと中りをつけます。

ネットで検索すると、PHPでは
<input type="file" name="body" />
でアップロードできるファイルの数が20までだということが分かりました。
20を超えると無視されるそうです。
これに違いありません。

対処法は簡単で、【php.ini】に、
【max_file_uploads=30】(30には上限数を入れます。)
と追記するだけです。

ですが、多くの人が使うツールの場合、【php.ini】が使えないサーバーで使われることもありますから、他の対処法が必要です。

ちょっと不便ですが、1つのファイルをアップロードしたら次の入力欄が表示されるように変更しました。これで入力欄が20を超えなくなりました。

ファイルアップロード数に上限があったんですね。今まで出あったことがありませんでした。勉強、勉強。

PHPコーディング

wwwの不思議?

2013年01月03日
各ツールでインストールURLに【www】があろうがなかろうが、動作するようにコーディングしているのですが、なぜか【www】があるとエラーになり、ないと動くという事例が出てきてしまいました。
ユーザーさんの連絡でわかりました。

ところが、こちらで再現しようとしても、再現しません。
なぜ・・・?

ソースを見ても、あってもなくても動くように書かれています。
強制的に【www】を付けてテストをしても、ちゃんと動きます。

ますます分からない・・・
何か勘違いしているか、サーバーが思ってもみない値を返してきているのでしょう。

念には念を入れ、一部修正したので、しばらく様子見です。

PHPコーディング

切り上げ、切り捨て、四捨五入

2012年12月07日
PHPで小数点以下切り捨てる処理が必要になったので、関数はないか調べてみました。

切り捨て:ceil
切り上げ:floor
四捨五入:round
で実現できます。

【round】関数は、丸める桁も指定できます。

【ceil】、【floor】は桁の指定はできないようです。桁を指定したい場合は、10の何乗かを掛けて、関数を通し、その後掛けた数で割ればいいでしょう。

PayPalオートメール

SSL接続のみになったようです

2012年09月07日
PayPalオートメール】では、cURL関数がインストールされていない場合にのみ、補助的に非SSL接続するように書いています。

昨日cURL関数のことを調べているとき、非SSL接続でもエラーが起こっていることに気が付きました。
今日はこのことを突っ込んで調べてみました。

PayPalオートメール】をリリースしたときは、非SSL接続も正常に動いていたので、おそらくその後PayPalの仕様が変わったのでしょう。

返ってくるメッセージを調べたら、http 302が返ってきていました。

調べると、世界中で(?)困っている人がいたらしく、解決法が出てきました。
PayPal IPN _notify-validate throws Error 302 in Sandbox - Stack Overflow
Mark's Tech Stuff: HTTP/1.0 302 Found - PayPal IPN in PHP

どうやら、SSL接続のみになったのではないかと思われます。
PayPalのマニュアルでもSSLを強く推奨していますし、まぁそうでしょう。
非SSL接続だと、PayPalが接続を拒否しています。

全てのユーザーさんにサーバーでSSLを使えるようにして貰うのは酷なので、【PayPalオートメール】では、cURL関数を使っています。
メジャーなサーバーにはインストールされていることは確認済みです。

一応cURL関数がインストールされていない場合は、fsockopenでSSL接続するように書き換えました。

PayPalオートメール

cURL関数がおかしい?

2012年09月06日
PayPalオートメール】のユーザーさんから、どうしても動かないとの連絡がありました。
外からあれこれ調べてみたのですが、どうしても原因がわかりませんでした。

結局そのユーザーさんのサーバーアカウントをお借りして調べることになりました。

色々な情報を吐かせながら、どこが原因で動かないのか切り分けをしていくと、どうやら【PayPalオートメール】からPayPalへ接続するところで躓いていることがわかりました。

PayPalオートメール】はPHPのcURL関数を使って、PayPalにSSL接続をしています。
どうも【curl_setopt】関数がおかしい動きをしているようなのです。

PayPalからサンプルとして提供されているソースを基盤に作っているんですがね・・・

まず、
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
を走らせるとエラーで止まります。
仕方ないのでコメントアウトして走らせてみました。

他の色々なオプションを設定して、
curl_exec($ch);
を実行します。エラーが起こった場合、falseが返ってくるそうなのですが、何も返ってきません。
curl_errno($ch);
でエラー番号が、
curl_error($ch);
でエラーメッセージが取得できるので、出力させてみました。
すると、
Unknown SSL protocol error in connection to www.sandbox.paypal.com:443
のメッセージが・・・
何じゃ?

そのサーバーの別のアカウントで全く同じソースを走らせてみたのですが、何の問題もなく、あっけないほどあっさりと、走りました。

ということは、動かないそのアカウントが何かおかしくなってる?
PHPのマニュアルを見ると、【CURLOPT_SSL_VERIFYPEER】は【7.10 以降、デフォルトでインストールされています。】とあります。もしかして、バージョンが違う???
でもバージョンの違いで【Unknown SSL protocol・・・】というのもおかしいかな。
バージョンによって設定しなければいけないオプションが違う??

【CURLOPT_SSL_VERIFYPEER】オプションは動かさなくても、【Unknown SSL protocol・・・】エラーが出るので、今のところどうにもならないか・・・

まぁこれ以上は私には無理と判断したので、サーバーの管理者も巻き込んでもらうことにしました。

ユーザーさんからも、他のサーバーにインストールしてみたら、すんなり動いたと連絡がありました。

一体何なのか。すっきりしないです。

PHPコーディング

タイムゾーンの指定

2012年06月26日
PayPalで楽アフィリエイト】のユーザーさんから、ワーニングが出ていると連絡がありました。

内容を見てみると、『タイムゾーンの指定がないので、date関数の使用は安全ではない』というものでした。
【date.timezone】を設定するか【date_default_timezone_set】関数を使用して設定する方法があるようです。

調べてみると、【date.timezone】は【php.ini】で設定すればよいようです。
【date_default_timezone_set】関数は当然ソース中に書いて設定します。

とりあえず【php.ini】の設定をしてみていただくことにしました。

【php.ini】ファイルに、
[Date]
date.timezone = "Asia/Tokyo"
(タイムゾーンを東京に設定)を加えていただきました。
ワーニングは出なくなったようです。

サーバー

これで自動認識?・・・でも不安

2012年05月04日
ぶつぶつ書いてしまったqmailのバグですが、これでMTAを自動認識できるようになるかもしれません。

以前、何のMTAを使っているか判別する方法を探したとき、PHPのiniファイルの設定を読む方法しか見つけられなかったのですが、今回別の視点から検索していたら、見つけました。
ちょびろぐ
system関数を使って調べるようです。
設定ファイルを辿っていけば、どこかにMTAがわかる記述があるはずだとは思っていましたが、system関数で調べられるんだぁ。勉強、勉強。

しかし、どうやら重たいらしいです。まいったなぁ。
私としては、こんな苦労はしなくても、SMTPポートを直接開いて送信してしまったほうが後腐れなくて、すっきりして、爽快なんですが・・・
ポートを開くとなると、IDやPWをどこかに保存しておかなければいけません。(もしかしたら自動取得できるのかもしれませんが、どうせ面倒でしょうし、そうなるとどっちが良い方法かわからなくなってきます。)
ユーザーさんにも設定してもらわなければいけませんし、極力ファイルに保存しておきたくありません。
もちろんステップメールやメールマガジンのように大量配信する場合は、PHPのmail関数は不適切ですから、SMTPポートを開きます。ですが、一度に送信するのが1通や2通の場合は、ユーザーさんの設定を軽くしたいな、と思います。

Qdmailに組み込まれているソースを使いやすいようにアレンジして、試してみました。
動いているっぽいです。確かに、qmailのときはステータス111を返し、sendmailでは1を返してきます。
でもソースにある100はどんなときに返ってくるんだろう?

まぁ今の状態より、より正確な自動判定ができると思うので、導入しようと思います。
でも、今の手動で設定する方法も残しておくつもりです。
世の中思わぬ動きをするサーバーがあるかもしれません。自動判定できない場合に備えて手動も残しておいたほうが安心です。せっかく今機能があるんですから。

重たいらしいし、ツールの管理画面を開いたとき判定して、設定に反映したほうがいいかな?
送信前に一度だけチェックすれば、さほど負荷はかからないかな?

PHPコーディング

人のバグに・・・

2012年05月01日
知る人ぞ知る、qmailのバグに振り回されてます。
いえ、振り回されているのは私ではなく、ツールのユーザーさんと言うべきかもしれません。

Eメールには、メール送信形式に事実上の世界標準というのがあります。まぁ当たり前ですが。
ネットビジネス便利ツールでは、この形式でメールを送信しています。まぁこれも当たり前ですが。
ところが、qmailにはせっかく世界標準形式で送信しても、わざわざその形式を崩して送信する、という厄介なバグがあります。
大抵のメーラーの作者は、qmailのバグを吸収するように作ってくださっています。
なのでqmailがおかしな形式のメールを送信しても、受信側のメーラがなんとかしてくれているのです。
ただ、一部のメーラーでは吸収しれくれないので、qmail+吸収しないメーラーの組み合わせになったとき、メールがきちんと表示されない現象に見舞われてしまいます。

しかし、これはメーラー側が悪いわけではなく、そもそも標準形式に従っていない方が悪いのです。

ネットビジネス便利ツールでも、qmailを使っている場合無理やりバグを打ち消すという機能を付けています。使っているMTAがqmailの場合、オンにしてもらうとバグを打ち消してくれます。本当は自動で判別できればよいのですが、以前調iniファイルを調べる以外方法が見つかりませんでしたiniファイルだけでは100%判定できないのです。

本来は他者の作ったバグは、対応する責任も義務もありません。qmailのバグに関して言えば、ベストはqmailの修正、次はパッチを当てるのがいいでしょうか。
でも、そんなことを言っていては世の中回らないこのもままあるので、対応しているわけです。ユーザーさんも困られるのは分かってますし。メーラーの作者も同様でしょう。
これに関して文句を言われる筋合いは全くないわけです。こちらでできる最大限をやっているのですから。
システム関係に明るくない人はなかなか理解しがたく、こちらに文句を言いたくなるのだとは思いますが・・・

この件に関して、ほぼ同時に二人のユーザーさんから連絡がありました。
一人の方はすぐに理解されたようですが、もう一方が・・・
まぁ私も困っておられるだろうと慌ててメールを返信したため、もう一方のメールの内容を見落としてしまったのも悪いのですが(書き方が引用に見えてしまい、スルーしてしまいました。反省。)、まるで理解できていない(しようとしていない)様子です。やれやれ、困った人です。
この方以前にも、自分が良く理解できていないことが原因なのに、ワーワー言って来られて、迷惑をかけられたことがあります。前任者との引継ぎがうまくできていないないことが原因だったのです。あなたの会社の引継ぎなんて、こっちにはなんの関係もないでしょう・・・ まずはきちんと引継ぎしてくださいな。
あちらの会社内部の問題に付き合わされたのに、最後にお礼の一言すらありませんでした。自分が客だから、こちらが何でもすべきとでも思っているのかしら。一体どういう教育している会社なんでしょう???

qmailのバグを打ち消す機能をお教えしたのですが、確認の方法がないとか、自分のお客さんに魅惑がかかっているとか言ってきました。
そうでしょう、迷惑をかけているのでしょう。でも迷惑をかけているのは私ではなく、その方のほうです。
qmailのバグですから、qmailが迷惑をかけているのでしょうけど、そんなことを言い始めるとバグのあるMTAをインストールしユーザーに使わせているサーバー会社、もっと言えばそのサーバーを選んだその方の責任もあるわけですよ。正直言って、こちらも迷惑かけられてます。
確認の方法? もちろんありますが、まぁ自分で調べてくださいな。こういう人には、協力しようという気がおきないですよね。私の責任の範囲でしたら、もちろん嫌でも協力しますが、他者のこしらえたバグですしねぇ。
今回のことを受け、マニュアルをもう少し注意を促す書き方に変更しました。
こちらにできるのはそれくらいです。
qmailを使わない方法もありますが、設定を少しでも簡単にしたいツールに関しては、この方法は見送ってました。導入したほうがいいかなぁ。でも、導入しても注意事項を読まない人は、どんな状況でも読みません。そしてそういう人が、後で困ったことになり、周りに毒を撒き散らすんだよなぁ・・・

いつも思うのですが、自分の責任の範囲を超えてでもこの人には協力しよう!と思える人、淡々と必要なことのみを連絡する人、必要最低限の話もしたくなく早く縁を切りたい人がいますね。実社会だけでなく、メールだけのお付き合いでも全く同じです。私もいい意味で気持ちよく人を巻き込めるようになりたいものです。

そして、ほぼ同時に言ってこられたお二方、よく見るとまず間違いなく同じ会社の方です。同じドメイン、同じ会社名です。隣の席の方が別々に言ってこられたとも考えられます。引継ぎがちゃんとできないのに、外部にワーワー言って迷惑をかけ、お礼も言わないような会社です。ありうる・・・

しかも、少し落ち着いてから調べてみたのですが、どうしても購入履歴が確認できないんです。この人たち、一体・・・? 別人が購入したと考えたいのですが、嫌な考えも浮かんできてしまいます。

今回のことがだけが原因ではないのですが、ネットビジネス便利ツールでは、前々から考えていたライセンスを、順次導入していきます。
正規のユーザーさんとこちらの権利の両方を守るためです。
購入履歴が確認できないお問い合わせに関しては、対応もしません。今後は徹底していきます。まぁ当たり前ですが。

PHPコーディング

クッキーが有効かどうか

2012年04月01日
Webページにアクセスしてきたブラウザで、クッキーが受け入れられたかどうかチェックできないか調べてみました。
PHPより、JavaScriptの方が適切なのかもしれませんが、色々な端末・ブラウザが出てきているので、できればサーバー側で行いたいです。

クッキーをセットするのは、【setcookie】関数ですが、この関数の戻り値では、クッキーが受け入れられたかどうかは分かりません。
調べるには、
 クッキーをセット、
 いずれかのページへ遷移、
 セットしたクッキーがあるかどうかチェックするしかないようです。
クッキーセット後、ページを遷移しなくても確かめられれば楽なんですが。

function chk_cookie(){
 $strMyself = basename($_SERVER[’SCRIPT_NAME’]);
 if(isset($_GET[’mode’]) === false){
  $strGet = "?mode=check";
  foreach($_GET as $strCol => $objColVal){
   $strGet.= "&".$strCol."=".$objColVal;
  }
  @setcookie("DUMMY", "OK");
  header("Location: ".$strMyself.$strGet);
 }
 else{
  if($_COOKIE[’DUMMY’] === "OK"){
   $blnResult = true;
  }
  else{
   $blnResult = false;
  }
  @setcookie("DUMMY", "", time() - 3600);
 }
 return $blnResult;
}

$_GET[’mode’]が存在しなければ、クッキー(キー:DUMMY、値:OK)をセット、
自分自身のURLに【?mode=check】を付加して遷移(他のクエリがある場合、後ろに付加)、
$_GET[’mode’]が存在していれば、クッキーの値をチェック、キー:DUMMY=値:OKなら、クッキーを削除し、trueを返す、
クッキーがセットされていないければ、falseを返す。

SQL

MySQLの文字化け

2012年01月31日
今回の開発はMySQLでやているのですが、どうもDBへの入出力で文字化けが起こります。
今まで、サイトはEUC-JPでコーディングしていたのですが、MySQLはUTF-8ですし、UTF-8だからMySQLで動かしたいので、なんとかUTF-8の文字化けを解消したいです。

調べると、解決方法が見つかりました。
mb_language("uni");
mb_internal_encoding("UTF-8");
mb_http_input("auto");
mb_http_output("UTF-8");
$objMySQLConID = @mysql_connect(ホスト名, ユーザーID, パスワード);
mysql_query("SET NAMES UTF8", $objMySQLConID);
$blnSelDB = @mysql_select_db(サーバー名, $objMySQLConID);
これでOKなようです。

早速変更してみました。化けなくなりました。

PHPコーディング

配列の特定項目削除

2012年01月29日
突如、PHPで配列の特定の項目(フィールド)を削除したくなって、そんな関数はないか調べてみました。

やはりありました。【unset】です。

【unset】は配列の項目を削除するだけでなく、変数の割り当てを破棄する関数です。

変数を破棄する場合は、
 unset(破棄する変数);
でOKです。

配列の特定の項目を破棄する場合は、
 unset(破棄したい項目のある配列[’破棄したい項目’]);
となります。まぁ当たり前ですが。

例えば、配列【$arrHairetsu】の【haishitai】という項目を廃棄したい場合、
 unset($arrHairetsu[’haishitai’]); です。

これで【$arrHairetsu】の【haishitai】以外の項目はそのままに、【haishitai】だけ破棄されます。

PHPコーディング

【.htaccess】の設定を使ってPHPを実行

2011年12月25日
変数の定義で書いたユーザーさんから動いたとの連絡をいただきました。

【php.ini】のパスを指定してcronやコマンドからPHPを動かしてみてください、という内容のメールをお送りしていました。
ですがそのユーザーさんのサーバーでは【php.ini】が使えないので、【.htaccess】にPHPの設定をしておられました。
cronからの実行時に、【.htaccess】の設定を読み込んで実行していなかったため、設定が有効にならず【Notice】が出ていたとのことでした。
【wget】を利用したら、動くようになったとのことでした。

とても詳しいユーザーさんで、私も勉強になりました。

PHPコーディング

変数の定義

2011年12月23日
タイマーメール】のユーザーさんから、動かないとの連絡がありました。
こちらではちゃんと動いていますし、他のユーザーさんからはそのような連絡はないし・・・ おそらく、サーバーの設定を少し変えたら動くのだろうと思います。

で、出ているエラーから、対処法を探してみました。
おそらく原因はこれでしょう。
http://www.sound-uz.jp/php/note/errorsの【Notice: Undefined variable】

対処法は
http://www.sakura-pc.jp/php/02020000.shtmlの【補足】

どうやらPHP5から変数を初期化するようになったようです。
そうかぁ。やっとPHPも変数の初期化を意識するようになったのか。でも型は宣言しない様子。

私のサーバーではエラーは出ない設定になっているけれど、ユーザーさんのサーバーは出る設定になっているようです。
ユーザーさんに対処法をお知らせしました。これで動けばよいのですが。

変数も初期化したほうがセキュリティ上良いようなので、ボチボチ(とても一気にはできない・・・)変更していくことにします。

変数を定義というか初期化することになったのはよいことですが、その変更を知らなかったのは不覚でした!

PHPの基礎体力
ここ面白そうです。じっくり読んでみよう。

PHPコーディング

404エラー

2011年11月22日
PHPで404エラーを起こすのに、
 Header("HTTP/1.0 404 Not Found");
を使っていたのですが、アクセスカウントしてみると、なぜか2回踏んでいることがわかりました。
ちなみに、403エラー
 Header("HTTP/1.0 403 Forbidden");
の場合は1回しか踏まないようです。

調べると、
 Header("HTTP", false, 404);
という書き方もあるようです。
こちらはアクセスカウントはされません。
アクセスしようにもURLが存在しないのでアクセスできない、つまりカウントもされないということのようです。
 Header("HTTP", false, 403);
も同様です。

PHPコーディング

【.forward】から転送されたメールの処理

2011年06月02日
【.forward】から転送されたメールは、
if(($objMail = @fopen("php://stdin", "rb")) == true ){
 while(!feof($objMail)){
  $strLine.= fgets($objMail, 4096);
 }
}
@fclose($objMail);
で読み込むことができます。

上のソースだと、$strLine にはメールヘッダも本文も、全てが取り込まれるので、ここから必要な情報を抜き出していくことになります。

ヘッダと本文は、【¥n¥n】と改行コードが2回続くことで区切られています。
【¥n¥n】より前がヘッダ、後ろが本文です。
ちなみに、ヘッダも本文も、メールの改行コードは【¥n】です。

更にヘッダは
 ・
 ・
To: 送信先メールアドレス
Subject: 件名
From: 差出人名 <差出人メールアドレス>
 ・
 ・
のようになっています。1行ずつチェックして、上の形式から必要な情報を取り出します。

メールの差出人を会員などに登録する場合、【差出人メールアドレス】を抜き出し、登録処理を行えばOKです。

【To: 】と【From: 】以降は、
 名前 <メールアドレス>
の場合もあれば、
 <メールアドレス>
の場合も、
 メールアドレス
の場合もあります。

名前、件名などに、日本語が含まれる場合、エンコードされてますから、【mb_decode_mimeheader】でデコードしてください。

メール受信と同時にCGIを実行と合わせれば、【ビジネス羅針盤 ステップメール】を空メール登録に対応させることができます。

サーバー

メール受信と同時にCGIを実行

2011年06月01日
以前からあったほうがいいのかなと思っていた、【ビジネス羅針盤 ステップメール】への空メール登録へ対応してみることにしました。
空メールだけでなく、本文に姓名が書いてあれば、名前も登録されるようにしたいと思います。

これを実現するためには、メールを受信したらCGIを実行する、という設定がサーバーに必要になります。
この設定ができるものに、【aliases】、【procmail】、【.forward】ファイルがあるようです。
レンタルサーバーで一番使われているのが、【.forward】ファイルのようなので、ここでは【.forward】ファイルで試してみることにします。
しかしサーバーによっては、セキュリティ上設定を許可していないものもあるようです。

設定は簡単で、
【"| phpのパス -q 実行したいCGIのパス"】
となります。

【"| /usr/local/bin/php -q /usr/home/xxx/yyy/run.php"】
のようになります。
ご自分のサーバーの環境に合わせてパスは変更してください。
これでメール受信と同時に【run.php】が実行されます。

【.forward】ファイルはメールの転送を設定するファイルですが、転送先にCGIファイルを指定すると、プログラムを実行できるようになります。

【.forward】で転送を設定する場合は、無限転送にならないように十分注意してください。永遠にメールが転送され続け、サーバーに無駄で過大な負荷がかかることになります。

PHPコーディング

ファイルダウンロードでページのhtmlまで出力

2011年04月26日
PayPalオートメール】のユーザーさんからの連絡でもう1点おかしなところが判明しました。

ファイルダウンロードの部分ですが、どうやらダウンロードファイルだけでなく、ダウンロードを行っているWebページのhtmlまで出力されてしまってます。

私のサーバーでは見られない現象。
サーバーによって少し動きが違うのでしょう。
ダウンロードの前に、キャッシュかhttpをクリアしておかなければいけないのでしょう。
自分のサーバーではクリアしなくても大丈夫だったので、気が付かずそのままになっている、ということです。

ネットで調べてみると、これかな、というものが見当たりました。
PHPマニュアルや当時参考にしたサイトには書かれていなかったので、思いもしませんでした。

対処法は簡単。
【ob_start】関数をDL前にコールするだけ。
そしてDL後に【ob_clean】関数をDL前にコール。

つまり、
 ob_start();
 header('Content-Description: File Transfer');
 header('Content-Type: application/octet-stream');
 header('Content-Disposition: attachment; filename='.basename($DLFilePath));
 header('Content-Transfer-Encoding: binary');
 header('Expires: 0');
 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
 header('Pragma: public');
 header('Content-Length: ' . filesize($DLFilePath));
 ob_clean();
 flush();
 readfile($DLFilePath);
 clearstatcache();
となります。
これ、どこかのサイトから拝借したんですが、出所が分からなくなってしまいました。
ブラウザのお気に入りにうっかり残し忘れたようです。

PHPコーディング

qmailのバグにはまる

2011年04月19日
PayPalオートメール】のユーザーさんから、メールの本文にヘッダが出力される、と連絡がありました。
そんな現象見たことない!
こちらにもテストメールを送っていただきました。

Becky!、Liveメール、Outlook、Outlook Expressで表示してみます。
ちゃんと表示されます。

ちゃんと表示できてますよ、と連絡すると、『そうなんです。友人のメーラでは正しく表示できるのですが、私のOutlook Expressだけおかしいんです。』とのお返事。

では、Outlook Expressのバグ?ということでその線で調べてみることに。
http://support.microsoft.com/kb/973459/ja
『こんなのありました。』とユーザーさんから連絡が。これだと思ったのですが、違っていたようです。

じゃあ、一体何???

私のサーバーから送ったメールは、みんなのところでちゃんと見えている。
でもユーザーさんのサーバーから送ったメールは、ユーザーさんのメーラーでだけ表示がおかしい。
ということは、ユーザーさんのサーバー・・・?
送られたメールが規則に反していて、Outlook Express以外のメーラーはそのバグを吸収してくれている、と解釈すればいいの?

今度はその視点で調べてみます。
もしかして・・・

PHPのmail関数のマニュアルにある
『・・・Unix の MTA の中には、自動的に LF を CRLF に変換してしまう ものがあります・・・』
に当てはまる?

更に調べると、qmailがこれに当たるようです。
qmailにバグが多いのは結構有名な話なんですね。

で、【ini_get('sendmail_path')】で何が出力されるか調べてみました。
すると、【sendmail】の文字が・・・
【qmail】じゃあないの?

でも、一か八かで、改行コードを変更してテストしてもらいました。
ちゃんと表示され始めたとのこと。
となると、隠れ【qmail】か?

ユーザーさんのサーバーのWebサイトを見てみると、『qmailを使用しています』の文字が!
こいつか!! 原因は!!!

で、一件落着。
どうしても【qmail】を利用しているかどうかのチェックの方法が見つからなかったので、どうしてもうまく送信できない場合は、管理画面から改行コードを指定してもらう仕様にちょっと修正。

これにて終了。

mail関数(PHP)のハマりどころ
PHP mail() Header Injection Through Subject and To Parameters

この辺りが参考になります。

PHPコーディング

マルチバイト文字列置換

2011年04月03日
PHPの【str_replace】関数は、マルチバイト文字列に対応していないようです。
今まで気が付かなかった・・・
なんてこった。そういえは、マルチバイトに対応した関数は【mb_】というプレフィックスが付いています。だけと【mb_str_replace】という関数は見当たりません。

ないなら自分で作らなければ、ということで、探してみました。
マルチバイトを考慮したstr_replace関数
が参考になります。

これでちゃんと動くようになりました。

PHPコーディング

MySQLのトランザクション

2011年01月09日
MySQLのトランザクションです。
 pg_query($Connect, 'begin');
 pg_query($Connect, 'commit');
 pg_query($Connect, 'rollback');
が、
 mysql_query("set autocommit = 0", $Connect);
 mysql_query("begin", $Connect);
 mysql_query("commit", $Connect);
 mysql_query("rollback", $Connect);
となります。

PostgreSQLでは【set autocommit = 0】は見たことがありません。
トランザクションを切らずにSQLを発行した場合、自動でコミットされるのをoffにするようです。
なくても、きちんとトランザクションを切れば大丈夫なようです。

PHPコーディング

MySQLのクエリ

2011年01月08日
今日もPostgreSQLのソースとインターネットを参考にMySQLのコーディングです。

クエリの発行は
 $result = pg_query($Connect, $sql);
が、
 $result = mysql_query($sql, $Connect);
となります。

どうやら、概ねPostgreSQLは【pg_xxx】だったのが、MySQLでは【mysql_xxx】になるようです。
ですが、クエリの発行は、SQLとリソース(DBへのリンク)が、PostgreSQLとMySQLでは逆に(引数の順序)なります。
ここをうっかり変更し忘れて、あれ?を何度かやってしまいました。

処理件数の取得は、
 pg_affected_rows($result);
が、
 mysql_num_rows($result);
へ、
リソースの開放は
 pg_free_result($result);
から
 mysql_free_result($result);
になります。

もう一点、気をつける点が。クエリの結果取得です。
PostgreSQLでは、【pg_fetch_assoc】以外に、【pg_fetch_object】も使えました。
 $rsData = pg_fetch_object($result, $cnt);
 $data[$cnt]['id'] = $rsData->id;
とか、
 pg_fetch_result($result, 0, 'MAX');
です。MySQLでは使えないようです。【mysql_fetch_assoc】を使います。
 $rsData = mysql_fetch_assoc($result);
 $data[$cnt] = $rsData;
とやれば、1レコードずつ一気に取得できます。

$rsData['id']でそのレコードのidフィールドが取得できます。

PHPコーディング

PHPからMySQLへ接続

2011年01月07日
今日はPHPからMySQLへ接続、テーブルへの読み書きをしてみました。
PostgreSQLのソースとインターネットを参考にコーディングしていきます。

DBへの接続ですが、PostgreSQLの場合は
 $Connect = pg_connect("host=ホスト名 dbname=DB名 user=ユーザーID password=パスワード");
でしたが、
MySQLでは
 $Connect = @mysql_connect(ホスト名, ユーザーID, パスワード);
でDB接続、
 $blnSelDB = @mysql_select_db(DB名, $Connect);
でDB選択となります。

切断は
 pg_close($Connect);

 mysql_close($Connect);
となります。

PHPコーディング

オブジェクト指向で書いてみる

2010年11月17日
いよいよオブジェクト指向で書いてみました。
PHPのオブジェクト指向入門 | オブジェクト指向PHP.NETとにらめっこしながら、自分のサイトに当てはめてみます。

まずは各ページで共通にある処理を洗い出してみます。
DB接続、DB切断などがありました。

abstract class BaseProcess{
  abstract protected function Main();
  public function strExec(){
    DB接続
    //メイン処理
    $this->strHTML = $this->Main();
    DB切断
  }
}

こんな感じで共通クラスを作ります。
各ページの処理はこのクラスを継承します。

それぞれのページを見ると、いくつかのパターンに分類できます。
・左メニューとメインメニューのある2カラムページ
・左ではなくページの上にメニューがあり、その下にマニュアルのあるマニュアルページ
・商品を売っているセールスページ
などです。

次に、例えば、2カラムページを処理するクラスを作ります。

abstract class TwoColum extends BaseProcess{
  public function Main(){
    DBからデータ取得
    左メニュー作成
    ヘッダ作成
    コンテンツ作成
    htmlタグ作成
    return $html
  }
}

これでBaseProcessが継承されます。
DBからデータを取得する部分は、表ページの表示にも使いますが、管理画面でも使うので、これもクラス化しておきます。

TwoColumを実行したときの実際の処理は、
 1. DB接続
 2. DBからデータ取得
 3. 左メニュー作成
 4. ヘッダ作成
 5. コンテンツ作成
 6. htmlタグ作成
 7. DB切断
となります。

TwoColumを呼び出すPHPでは、

//外部ファイル読み込み
 require_once('・・・');
//インスタンス作成
 $proc = new ContentProcess();
//実行&htmlタグ受取
 $html = $proc->strExec();
//出力
 echo $html;

これだけです。
なんか物足りない。ちょっと不完全燃焼。
でも不完全燃焼ってことは、コーディングが楽ってこと?

しかも、最終呼び出しPHPはどのファイルもほとんど同じになります。
最終PHPでhtml作成をする方法ももちろんあります。

以前にも感じていましたが、オブジェクト指向でコーディングするのって、手続型より設計が大切です。
ちゃんと構造を把握しておかないと、自分でわけがわからなくなりそうです。
だから他人が作ったオブジェクトってわかりにくいんだ・・・

PHPコーディング

オブジェクト指向の実際

2010年11月14日
数日前から始めた、オブジェクト指向の勉強ですが、やっぱり理屈は分かってもなかなかどうコーディングすれば便利がよくなるのかわかりません。

色々やってみて勉強しようと思っていたのですが、やっぱり効率が悪いので、なにかよい参考サイトはないかと探してみました。

すると、さすがはインターネット、ありました。
PHPのオブジェクト指向入門 | オブジェクト指向PHP.NET
理屈だけでなく、実際にどう作ったらよいかがわかります。

まぁ本当に自分のものにするためには、作ってみないといけないことにはかわりませんが、かなり試行錯誤を減らしてくれそうなサイトです。

PHPコーディング

変数の参照

2010年11月12日
PHPでオブジェクト指向を勉強し始めましたが、やっぱり自分でやってみないと実感がわきません。
理屈は書かれていることを読めばわかります。オブジェクト指向の利点がいくつか書かれていますが、実際どうやって使ったら便利がいいのか、どう手間が省けるのかがさっぱり・・・

ということで、とにかく書いて書いて書いてみることにしました。

まずは、htmlのヘッダや左メニューなど、各ページで共通なhtmlを関数化して使っていたのですが、これをいじることに。
今まではオブジェクトのようで不完全なオブジェクトもどきだったので、もうちょっとちゃんとしたオブジェクトにすることに挑戦です。

左メニューはDBに登録されています。
まずはDBから必要なデータを取得して、階層メニューに編集します。

DBからデータを引っ張る部分は今まで手続き型の関数でした。
これをクラスのメソッドに変更。
DBのデータはクラス内の変数(言い方があるのだけど、何だっけ)に格納しておきます。

同じように階層メニューも手続き型関数だったのをメソッドに変更。

手続き型の関数では、変数を引数でやり取りするか、global宣言しないといけませんでしたが、オブジェクト指向でコーディングするとそんな必要はなく、クラス内で変数は保持されています。

これが、手続き型関数では長々と引数を設定しなければいけない地獄だ、ということなんでしょうか。
確かに楽なのかな。
手続き型に慣れている私としては、ちょっと不安に感じてしまいます。
データはどこだ!!?って感じです。

とりあえずコーディングしてみたので、まだまだ便利に組めてないのでしょうけれど、ちょっとやっただけでも違いがわかりました。

PHPコーディング

オブジェクト指向を勉強しよう

2010年11月07日
以前から勉強しようと思っていた、オブジェクト指向。
ソフトのバージョンアップも落ち着いたし、サンプルソースもオブジェクト指向でかかれたものもあるし、SOHOで仕事を探し始めたし、これを機に勉強しようと思い立ちました。

おおよその概念は、何年か前に派遣の仕事でVB.NETを使ったとき理解しています。

しかしその後派遣先の会社が変わり、Webの開発でPHPを使うようになりました。
その会社ではPHP4を使っていたので、中途半端なオブジェクト指向で書かれていたようです。このときから先に進んでいないので、この辺りで新しい知識を入れようと思います。

PHP5は完全オブジェクト指向言語だそうです。
主流がPHP5になった今、勉強しどきです。

手続指向に慣れているとオブジェクト指向は難しいようですが、オブジェクト指向に慣れると手放せなくなるほど便利に感じるという記事を励みに頑張りたいと思います。
とは言っても、概念を思い出しながら、そしてPHP特有の仕様と戦いながらの勉強です。
派遣でVB.NETを使っていたときは、作ってあるクラスを使う側だったので、クラスを作るのは初めてです。

独学PHP はじめよう、PHPでオブジェクト指向
この辺りが初めは参考になると思われます。

まずは自分のサイトのPHPをオブジェクト指向に書き換えて勉強しようと思います。

PHPコーディング

file_get_contents関数は負荷が大きいのか?

2010年09月30日
PHPver.4.3から追加された【file_get_contents】関数。
ファイルの全内容を変数に取得する関数です。

どうやらこの関数のサーバーへの負荷が大きいのか、サーバーによってはファイルの全内容が取得できず、途中切れになっているようなのです。

PayPalオートメール】のユーザーさんから、メール本文のファイルが全て読み込まれていない、との連絡を受けました。

私のサーバーではそんなことは全く起こりません。
ということは、サーバーの問題と思われます。
メール文を短くしたら、全て読み込まれたそうなので、おそらく関数のせいでしょう。

インターネットであちこち調べたのですが、【file_get_contents】がサーバーに大きな負荷を与えるという、はっきりとした記述は見つけられませんでしたが、そうかもしれないね、という記述はありました。

そんなに大きなファイルではないのですが、安いプランの場合、サーバーへの負荷が大きく制限されてい場合もあると思われます。

ユーザーさんには、関数を書き換えてみたので、落ち着いたら試してみていただきたいと連絡しておきました。

ということは、【file_put_contents】関数もかな。

やっぱり【fopen】【fgets】【fwrite】【fclose】などの関数を使った従来の方式の方がいいのでしょうか。

サーバーへの負荷の具合を調べられるはずなので、方法を調べてみようかな。

PHPコーディング

テキストをブラウザに表示

2010年07月23日
PayPalで楽アフィリエイト】をUTF-8のメール送信に対応させました。

それに伴って他の部分のソースをいじったついでに、もっとよいものに書き換えました。

テキストファイルへのリンクを開いたとき、ブラウザへファイルの内容を表示させるソースです。

通常テキストファイルへリンクをはるだけでブラウザにファイル内容が表示されますが、表示する前にちょとプログラムを実行した後、表示させたいので普通にタグでリンクさせたのでは表示されません。

$file_path = 表示したいファイルのパス;
header("Content-type: text/plain");
header("Content-Disposition: inline; filename=".$file_path);
header("Content-length: " . filesize($file_path));
readfile($file_path);
でOKです。

【inline】を【attachment】にすると、ダウンロードダイアログが表示されます。

header("Content-type: text/plain");
header("Content-Disposition: attachment; filename=".$file_path);
header("Content-length: " . filesize($file_path));
readfile($file_path);

PHPコーディング

fgetcsv関数の不思議

2010年06月03日
作成したツールで文字化けが起こっているとの連絡を受けました。
私が契約しているサーバーでは、全く問題なく動作しています。

色々調べると、あるサーバーでその現象が起こっていることがわかりました。
さらに詳しく調べると、【fgetcsv】関数がファイルから文字を読み込む際、文字化けを起こしていることがわかりました。

ネットで調べると、この問題に引っかかっている方もたくさんいらっしゃるようです。
あちこちに書き込みがあります。

そうやら、PHPのバージョンアップの際、関数の仕様が変更になったようです。
こういう基本的な関数の仕様を変更されると、痛いです。

しかし、更に調べると、更にバージョンの高いPHPではこの現象は起こっていません。
ということは、仕様がまた変更になったということのようです。
要は特定のバージョンのPHPでのみ起こる現象、ということです。

csvで囲み文字を使っている場合、問題なく動作するようです。

でも、csvの本来の形式って確か、項目の値を囲まないはずでは???

回避方法としては、ファイルからはシンプルに文字列を読み込んで、あとは自分で処理すればいいようです。

前からいつか書き直そうと思っていた部分なので、ついでに書き直します。

正規表現を使う方法や、ファイル全体をそのまま読み込む方法もありますが、私が取った方法です。

【fgets】関数で、ファイルを1行ずつ読み込み。
改行文字列を削除。
区切り文字列で分割。

です。
今回は囲み文字列はないので、飛ばします。区切り文字列がタブの場合です。

while (($line = fgets($handle)) !== false) {
 $line = str_replace("\n", "", $line);
 $line = str_replace("\r", "", $line);
 $csv_data = explode("\t", $line);
}

これで問題なく動き始めました。

PHPコーディング

【exit】関数でも処理した結果を【fsockopen】で受け取れる

2010年04月12日
昨日の記事処理した結果を【fsockopen】で受け取るでは、【echo】関数を使いましたが、【exit】関数でもOKです。
【exit(("result=false" )】
という具合です。

ようは文字を吐き出せばOKのようです。
試してませんが、【print_r】なども大丈夫ということなのでしょう。

PHPコーディング

処理した結果を【fsockopen】で受け取る

2010年04月11日
データをpostして、処理を行い、その結果を取得したい場合、【echo】で返したい文字列を吐き出すと返すことができます。

例えば、【http://sample.com/syori.php】で処理を行うとします。
postする方法は、フォームを介さずPOSTするを参照してください。

【http://sample.com/syori.php】で処理を行い、エラーの場合【result=false】を、成功した場合【result=true】を返すとします。

【syori.php】でエラーの場合、
【echo("result=false"\n);】を実行、
成功の場合、
【echo("result=true"\n);】を実行します。

postした側では、この結果を
while (!feof($sock)) {
  $res = @fgets($sock, 1024);
  if(stristr($res, "result=") !== false){
    $res = str_replace("\n", "", $res);
    $kekka = explode("=", $res);
  }
}
で($kekka)受け取れます。

PHPコーディング

PHPの変数型

2010年02月17日
PHPは変数を使うとき、型を宣言しません。
文字列を代入すれば文字型の変数に、数値を代入すれば数値型の変数になります。
そう、PHPが自動的に型を決めてくれるのです。

同様に、変数の値の比較の際も、自動で型を変換してしまいます。
(文字型と数値型の比較もできてしまうのです。)
しかし、この文字型と数値型の比較で驚くことがおこります。

$test = 0;
if($test == ""){
  通ります。
}

$test = "1a";
if($test == 1){
  通ります。
}

$test = "a1";
if($test == 0){
  通ります。
}

恐ろしいことです。

文字型と数値型を比較するときは、文字型変数の値が数値に(勝手に)変換されてから比較されるので、こんな摩訶不思議なことが起こってしまうのです。

http://www.php.net/manual/ja/language.operators.comparison.php
http://www.php.net/manual/ja/language.types.string.php#language.types.string.conversion

更なる自動変換の数々
http://d.hatena.ne.jp/gallu/20061108/p1
http://techblog.ecstudio.jp/tech-tips/php-string-compare.html

こうした自動変換による不思議な世界は【switch】にも広がっています。


文字列比較のときは、【===】か【strcmp】を使うようにとあります。

$test = 0;
if($test === ""){
  通りません。
}

$test = "1a";
if($test === 1){
  通りません。
}

$test = "a1";
if($test === 0){
  通りません。
}


ここで、【===】と【strcmp】の速度について調べてみました。

for($cnt=0; $cnt<10000000; $cnt++){
  $test1 = "abc";
  $test2 = "def";
  if(strcmp($a, $b) === 0){
  }
}

for($cnt=0; $cnt<10000000; $cnt++){
  $test1 = "abc";
  $test2 = "def";
  if($a === $b){
  }
}

この2つのループにかかる時間を計ったところ、【===】判定のほうがはるかに早かったです(何回か実行しました)。
【===】演算子は型変換を行わないので、処理が早いのだそうです。

しかし、【switch】では【if】のような演算子は使えません。
変数の比較(特に文字列)では、【if】文で【===】と【!==】を用いる、のが安全なようです。

PHPコーディング

ソケットをオープンしてファイル操作をする(put)

2010年02月09日
今回はファイルのアップロード(put)です。

get同様ftpサーバーへログインした接続とは別の接続をオープンして行います。

ローカルファイルの内容を読み込みます
$local = @fopen($localfile, "r");
ファイル内容を全て読み込んでおきます
$write = @fread($local, 100000);

@fclose($local);

ファイル転送モードを指定します。
バイナリならば、【I】、アスキーなら【A】です。
@fputs($sock, "TYPE I
");
$result = @fgets($sock, 512);

パッシブモードをオンにします。
@fputs($sock, "PASV
");
$result = @fgets($sock, 512);

データ用の接続をオープンします。
ポートの計算方法は、ソケットをオープンしてファイル操作をする(get)を参照してください。
$ftp = @fsockopen($ftp_server, $data_port);

リモートにファイルを作成します。
@fputs($sock, "STOR ".$remotefile."
");
$result = @fgets($sock, 512);
成功すれば【150 ・・・】と返ってきます。

リモートにファイルを書き込みます。
$result = @fwrite($ftp, $write);

ファイルサイズを照合をする場合、【fwrite】の戻り値とstrlen($write)をチェックすればOKです。

get同様、バックアップ機能を備えれば安心です。

PHPコーディング

ソケットをオープンしてファイル操作をする(get)

2010年02月08日
今度は、ファイル情報取得やファイル削除よりちょっと複雑なファイルのアップロード(put)やダウンロード(get)などです。

put・getはftpサーバーへログインした接続とは別の接続をオープンして行います。

ローカルへファイルを作成します。
$localfile = "local.txt";
$local = @fopen($localfile, "w");

ファイル転送モードを指定します。
バイナリならば、【I】、アスキーなら【A】です。
@fputs($sock, "TYPE I\r\n");
$result = @fgets($sock, 512);

パッシブモードをオンにします。
@fputs($sock, "PASV\r\n");
$result = @fgets($sock, 512);
成功すれば、【227 Entering Passive Mode (200,0,0,1,100,32)】という具合に、メッセージの後にサーバのIPアドレスとデータ接続用のポート番号が返ってきます。
【200,0,0,1,100,32】がそれです。初めの4つがIPアドレス、後ろの2つがデータ用のポート番号です。
ここから接続用のポート番号を取り出します。

ポート番号は
$data_port = 後ろから2番目の数値 * 256 + 一番後ろの数値
で計算できます。
100 * 256 + 32 = 25632

データ用の接続をオープンします。
$ftp = @fsockopen($ftp_server, $data_port);

リモートのファイルを読み込みます。
$remotefile = "remote.txt";
@fputs($sock, "RETR ".$remotefile."\r\n");

ローカルのファイルへ書き込みます。
$result = @fwrite($local, @fread($ftp, 100000));

念のため、書き込んだファイルサイズを照合したほうがいいでしょう。
ローカルへ書き込んだファイルサイズは、【fwrite】の戻り値でチェックできます。

一方、リモート側のサイズは、
@fputs($sock, "SIZE ".$remotefile."\r\n");
$result = @fgets($sock, 512);
成功すれば、【213】に続き、サイズが戻ってきます。

データ用の接続をクローズします。
@fclose($ftp);

念のため一時バックアップファイルを作成しておき、書き込みに失敗したときに書き戻す機能をつけておけば更に安心でしょう。


ローカルのファイルに追記したい場合、ファイルオープンのとき、モードパラメータで【a】を指定すればOKです。
$local = @fopen($localfile, "a");

PHPコーディング

ソケットをオープンしてファイル操作をする(ファイル削除)

2010年02月07日
ftpサーバーに接続できたら、まずは簡単なファイル削除から。

@fputs($sock, "DELE ".$file."\r\n");
$result = @fgets($sock, 512);

接続に成功していれば、
250 ・・・・・
と返ってきます。

このほかにも、リネーム、ファイルリスト取得、ディレクトリ移動など同じような方法で行えます。

PHPコーディング

ソケットをオープンしてファイル操作をする(ftp接続)

2010年02月06日
PHPでftpを利用するには、ftp関数をインストールすれば一番簡単なのですが、レンタルサーバーの場合、自分でインストールできないことが多々あります。

そういった場合、ソケットをオープンしてファイル操作をしなければいけません。

まずはftpサーバーへの接続です。

$ftp_server = "ftp.domain.xxx";
$ftp_uid = "user_id";
$ftp_pw = "password";
$ftp_port = 21;

$sock = @pfsockopen($ftp_server, $ftp_port);
$result = @fgets($sock, 512);

@fputs($sock, "USER ".$ftp_uid."\r\n");
$result = @fgets($sock, 512);

@fputs($sock, "PASS ".$ftp_pw."\r\n");
$result = @fgets($sock, 512);

接続に成功していれば、
230 ・・・・・
と返ってきます。

PHPコーディング

SMTPソケットをオープンしてメール送信(メールDATA部)

2010年01月27日
前回に続き、今度はDATA部です。いわゆるメールのボディ部です。

ボディ部は、【DATA】コマンドから、【.】のみの行が送信されるまでとなります。


$from_name = mb_encode_mimeheader("差出人名", "EUC", "B");
$from_add = "from@domain.xxx";
$to = "sendto@yyyy.zzz";
$reply-to = "reply@domain.xxx";
$subject = mb_encode_mimeheader("smtpメール送信", "EUC", "B");
$body = "メール本文です。\n";
$body = mb_convert_encoding($body, "ISO-2022-JP", "ASCII,JIS,UTF-8,EUC-JP,SJIS");

$result = @fputs($sock, "DATA\r\n");

$data.= "From: ".$from_name."<".$from_add.">\r\n";
$data.= "To: ".$to."\r\n";
$data.= "Subject: ".$subject."\r\n";
$data.= "Reply-To:".$reply-to."\r\n";

$data.= "MIME-version: 1.0\r\n";
$data.= "Content-Type: text/plain; charset=ISO-2022-JP\r\n";
$data.= "Content-Transfer-Encoding: 7bit\r\n";
$data.= "\r\n";
$data.= $body

@fputs($sock, $data."\r\n");
$result = @fgets($sock, 128);

//最後を示す【.】を送信
@fputs($sock, "\r\n.\r\n");
$result = @fgets($sock);

上の例は、内部文字コードは【EUC】、【ISO-2022-JP】のプレーンテキストメールを送信する場合です。

$data.= "MIME-version: 1.0\r\n";
以降を変えることで、もちろんthmlメールや添付ファイル付きのメールを送信することもできます。

この辺りは、【mail】関数で添付なしプレーンテキスト形式メールを送信【mail】関数で添付ありプレーンテキスト形式メールを送信【mail】関数で添付なしhtml形式メールを送信(代替本文なし)【mail】関数で添付ありhtml形式メールを送信(代替本文なし)【mail】関数で添付なしhtml形式メールを送信(代替本文あり)【mail】関数で添付ありhtml形式メールを送信(代替本文あり)とほぼ同じです。
違うのは、行の最後を表す文字がきっちり【\r\n】になることです。


送信し終えたら、
@fputs($sock, "QUIT\r\n");
@fclose($sock);

PHPコーディング

SMTPソケットをオープンしてメール送信(メールヘッダ)

2010年01月26日
本当は【DIGEST-MD5】もコーディングしたかったのですが、どうしても【DIGEST-MD5】ではどのように認証すればいいのかの記述が見つかりません。英語で書いてある仕様書はあるようなのですが、日本語で書かれているページがみつかりません。

それよりも前に進まないといけないので、とりあえずメール送信に進みます。

$return-path = "return@domain.xxx";
$to = "sendto@yyyy.zzz";
$bcc1 = "bcc1@domain.xxx";
$bcc2 = "bcc2@domain.xxx";

まずはヘッダ部からです。

送信元メールアドレスをサーバーに知らせます。
この送信元はメーラーでは送信者としては表示されません。smtpサーバーによりreturn-pathに設定されます。メーラーに送信者として表示されるのはDATA部のfromです。
@fputs($sock, "MAIL FROM:<".$return-path.">\r\n");

送信先メールアドレスをサーバーに知らせます。
@fputs($sock, "RCPT TO:<".$to.">\r\n");

BCCがある場合はBCC分【RCPT TO】コマンドを送信します。
@fputs($sock, "RCPT TO:<".$bcc1.">\r\n");
@fputs($sock, "RCPT TO:<".$bcc2.">\r\n");

次回はDATA部です。

PHPコーディング

SMTPソケットをオープンしてメール送信(CRAM-MD5認証)

2010年01月24日
今日はsmtp認証の中の【CRAM-MD5】方式です。

まずは、サーバーに【CRAM-MD5】方式で認証を行うことを通知します。
@fputs($sock, "AUTH CRAM-MD5\r\n");

成功すると、サーバーは認証に使うキーを返してきます。
$line = @fgets($sock, 128);
キーは、【334 キー】の形式で返ってくるので、【334 】を取り除きます。
$key = str_replace("334 ", "", $line);

この方式は
Base64エンコード(ユーザ名+空白1文字+サーバーからのキーをパスワードをキーとしてMD5ダイジェストした値)
を認証に用います。

$ninsyo = base64_encode($id.' '.hash_hmac('md5', $key ,$pw));
で認証用文字列が得られます。


次に、得られた文字列で認証です。
@fputs($sock, $ninsyo."\r\n");
$result = @fgets($sock, 128);
成功していたら、【235 ・・・】が返ってきます。


この方式は、IDやパスワードがデコードできなきので、セキュリティが高い方式です。

PHPコーディング

SMTPソケットをオープンしてメール送信(LOGIN認証)

2010年01月22日
今日はsmtp認証の中の【LOGIN】方式です。

まずは、サーバーに【LOGIN】方式で認証を行うことを通知します。
@fputs($sock, "AUTH LOGIN\r\n");

成功すると、サーバーはIDを入力するように求めてきます。
IDをBase64エンコードして送信します。
@fputs($sock, "base64_encode($id)."\r\n");

成功すると今度はパスワードを入力するように求めてきます。
パスワードをBase64エンコードして送信します。
@fputs($sock, "base64_encode($pw)."\r\n");

成功していたら、【235 ・・・】が返ってきます。

この方式は、base64でエンコードしていますが、簡単にIDやパスワードをデコードできるので、セキュリティが高い方式ではありません。

PHPコーディング

SMTPソケットをオープンしてメール送信(PLAIN認証)

2010年01月21日
昨日の続きです。
昨日はソケットオープンまで成功しました。

今日はsmtp認証です。
smtp認証にも色々ありますが、まずは【PLAIN】方式についてです。

この方式は、【PLAIN】方式で認証を行うことをサーバーに通知する文字列に続いて、認証文字列をサーバーに送信します。

認証文字列に使うのは、サーバーによって
$id."\0".$id."\0".$pw

$id."\0".$pw
をbase64でエンコードした文字列です。

$ninsyo = base64_encode($id."\0".$id."\0".$pw);
@fputs($sock, "AUTH PLAIN ".$ninsyo."\r\n");
$result = @fgets($sock, 128);
成功してれば、【235 ・・・】と先頭が【235】の文字列がサーバーから返ってきます。

もしエラーなら、
$ninsyo = base64_encode($id."\0".$pw);
@fputs($sock, "AUTH PLAIN ".$ninsyo."\r\n");
を送ってみます。

この方式は、base64でエンコードしていますが、簡単にIDやパスワードをデコードできるので、セキュリティが高い方式ではありません。

PHPコーディング

SMTPソケットをオープンしてメール送信(ソケットオープン)

2010年01月20日
PHPからメールを送信するには色々な方法があります。

手っ取り早く日本語メールを送信するには【mb_send_mail】があります。
添付ファイルやhtmlメールを送信するには、【mail】を使ってヘッダなど書かけば可能です。
しかし、この【mail】関数、いちいちSMTPソケットを開いたり閉じたりするので、メールマガジン、ステップメールを配信するのには不向きのようです。
その上、sendmailやqmailなどがインストールされていて、PHPから使えないとメールが送信できないそうです。
更に更に、処理が重たいのです。

ソケットのオープン・クローズも制御するには直接ソケットをオープンしてメールを送信しなければいけないようです。

もちろんその辺りの無料のツールも出回ってます。有名どころではPEAR::Mail_Mime

利用してもいいのですが、なにかあったとき自分でちゃっちゃと直せないと困るので、自分で書いてみることにしました。

大まかな流れは、
1. ソケットオープン
2. メール送信
3. ソケットクローズ
です。

メールマガジンやステップメール配信の場合は2を繰り返します。

まずはソケットオープンです。

$smtp = "smtp.domain.xxx";
$port = 587;
メール送信時にも認証が必要になってから、ポート番号は587のことが多くなってます。
$id = "mail_id";
$pw = "smtp_pw";

$sock = @fsockopen($smtp, $port);
でオープンできます。

成功したら、
@fputs($sock, "EHLO ".$port."\r\n");
認証が必要になってからコマンドはEHLO。

ここでサーバーが色々な情報を返してきます。
そのうちの1つが、利用可能な認証方式。
250-AUTH PLAIN LOGIN DIGEST-MD5 CRAM-MD5 NTLM
の形式で返ってきます。

利用できる認証方式は、PLAIN、LOGIN、DIGEST-MD5、CRAM-MD5、NTLM。
それぞれの認証方式は後にして、ここで問題が発生です。

while (!feof($sock)) {
  $result = @fgets($sock, 128);
  $num = preg_match("/^250-AUTH /i", $result);
  if(0 < $num){
    $login_method = str_replace("250-AUTH ", "", $result);
  }
}
として、サーバーから返された文字列を取得しようとしたのですが、どうやら無限ループに陥っているようで、タイムアウトになってしまいます。

調べていくと、【feof($sock)】でfalseが返ってきていないようです。【fsockopen】でオープンしたストリームでも使えるのに・・・なぜ?PHPのバグ??それとも終了しないとeofが返ってこないのか???

仕方ないので、別の方法に切替です。

while ($result = @fgets($sock, 128)) {
  $num = preg_match("/^250-AUTH /i", $result);
  if(0 < $num){
    $login_method = str_replace("250-AUTH ", "", $result);
  }
}
で一応解決です。
でも、ものすごく時間がかかってしまいます。
時間がかかっているのは、【$result = @fgets($sock, 128)】の部分です。
文字列が返ってくる場合は早いのですが、falseが返ってくるのに時間がかかっています。

PHPのマニュアルを読むと、【feof】関数はタイムアウトになったときもfalseを返すとのこと。

そこで、タイムアウトまでの時間を短くし、処理時間の短縮を試みました。

【stream_set_timeout】でタイムアウトまでの時間を設定できます。
タイムアウトになったら【stream_get_meta_data】で値が取得できます。

stream_set_timeout ($sock, 5);    //タイムアウトまでの時間を5秒に設定
while (!feof($sock)) {
  $result = @fgets($sock, 128);
  $info = stream_get_meta_data($sock);
  if($info['timed_out'] == true){  //タイムアウトになったらこの値がtrueになる
    break;
  }
  $num = preg_match("/^250-AUTH /i", $result);
  if(0 < $num){
    $login_method = str_replace("250-AUTH ", "", $result);
  }
}

5秒ではなくもっと小さな値にすればもっと早くなります。

これで解決!のようです。動かしているうちまた不都合が見つかるかもしれませんが。

長くなったので、smtp認証はまた次回。

PHPコーディング

パーミッションの変更

2009年11月21日
PHPからファイルのパーミッションを変更する方法です。

まずは、現在の8進形式のパーミッションの取得から。
$file_path = "パーミッションを取得したいファイルパス";
$now = substr(sprintf('%o', fileperms($file_path)), -4);
これで、【0755】や【0644】などが返ってきます。

次に、パーミッションの設定です。
$permission = 0600;
$file_path = "パーミッションを変更したいファイルパス";
$result = @chmod($file_path, intval($permission, 8));

本当に設定できたか確認。
$after = substr(sprintf('%o', fileperms($file_path)), -4);

しかし、この流れだと問題が起こります。
ファイル情報がキャッシュされるので、パーミッションが無事変更できたとしても、$afterには$nowと同じ値が返ってきます。

そこで、キャッシュ情報をクリアします。関数【clearstatcache】をコールするだけです。

$file_path = "パーミッションを取得したいファイルパス";
$now = substr(sprintf('%o', fileperms($file_path)), -4);

clearstatcache();

$permission = 0600;
$file_path = "パーミッションを変更したいファイルパス";
$result = @chmod($file_path, intval($permission, 8));

$after = substr(sprintf('%o', fileperms($file_path)), -4);

これでOKです。
2017年08月
  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 31
先月
来月
カテゴリ
検索
関連サイト
プロフィール
【ネットネーム】
みゅー

キャラクター by ツカエルサイト


【趣味】
・プログラミング
・写真
・楽器演奏
・手芸
・日曜大工
・家の中の不便を便利に
・考える
・自然科学
・猫と戯れる
・ジグソーパズル

【仕事】
・派遣社員でSE・PGやっていました。次の良い仕事がみつからないので、ビジネスへ注力しようと思っています。
・経験言語:VB6、VB.NET、PHP、Perl、JAVA、Oracle、SQLServer、PostgreSQL



【名前】
アン(足)

2014年4月22日生まれ 女の子
あだ名は くノ一
普段は普通にしゃべりますが、興奮すると『ニャ』を連発します

【趣味】
・狩
・ご飯を食べる
・ママのお手伝い

【仕事】
ママのビジネスの看板招き猫
ママの代わりにPC入力
仕事中のママの右腕を温める
ママに心配をかける
いろんな事をしでかしてママを退屈させない

RSSフィード