[PHP] SimpleXML関数を使ってXMLデータをパースする

結果をXML形式で返すウェブサービス (Web API) を使うことがある。そこで、PHPでXMLデータをパースする方法を覚え書き。

PHP5のSimpleXMLエクステンションを使うと簡単

PHP5にはSimpleXMLと呼ばれるXML操作のための拡張モジュールが含まれている。SimpleXMLの関数を使うと、XMLを簡単にオブジェクトに変換可能。

PHP: SimpleXML – Manual
https://php.net/manual/ja/book.simplexml.php

PHP: XML 操作 – Manual
https://php.net/manual/ja/refs.xml.php

サンプルコード

XMLデータにCDATAセクションが含まれていると上のコードでは読めない。その場合、以下のように変更する。

XML用語事典 [CDATAセクション]
http://www.atmarkit.co.jp/aig/01xml/cdata.html

パースしたXML要素はオブジェクトのプロパティとして参照できる。たとえば $data->section->item という感じ。開発途中でprint_r()関数などを使ってダンプしておくとXML要素の特定は楽になる。

参考サイト

SimpleXML | PHP プログラミング解説
http://www.crystal-creation.com/web-app/tech/programming/php/xml/simple-xml.htm

PHP SimpleXMLでXMLパースをするときの注意点まとめ – ウッディーの日記
http://blog.livedoor.jp/gnoot/archives/51371942.html

[PHP] Webリクエストをハッシュを使って認証する

Webリクエストをハッシュを使って認証する必要があったので覚え書き。条件はクライアントとサーバーの両者が共通の秘密鍵を保持していること。

例1: ユーザ名をハッシュ化してリクエストに付加する

ハッシュ関数としてsha1を使うと仮定する。ユーザ名をusername、秘密鍵をsecret_codeとすると、ハッシュは以下のように計算できる。

そうして得られたこのハッシュ値を以下のようにリクエストに付加する。

リクエストを受け取ったサーバー側では、リクエスト中のusernameと独自に保持する秘密鍵からハッシュ値を計算し、リクエストに付加されているハッシュ値と一致したら認証OKとする。

例2: パラメータを組み合わせてハッシュ化してリクエストに付加する

以下のようなパラメータ付きリクエストを想定する。

それぞれのパラメータはユーザ固有とし、リクエストにはユーザ名は付加しないものとする。すると、ハッシュは以下のように計算できる。

そうして得られたこのハッシュ値を以下のようにリクエストに付加する。

最初の例と同様に、リクエストを受け取ったサーバーは独自に保持する秘密鍵からハッシュ値を計算し、リクエストに付加されているハッシュ値と一致するかどうか確認する。

補足事項

クライアント側とサーバー側でハッシュ計算方法が共通になりさえすれば、ハッシュ計算方法はアレンジしても問題ない。上の例ではリクエストに付加する値だけをハッシュの元値として使ったけど、URLの部分文字列(たとえば “param1=<param1値>&param2=<param2値>”) をそのままハッシュの元値として使ってもかまわない。

sha1でバイナリのダイジェストを使いたい場合はbase64でエンコードしてからリクエストに追加すれば良い。

参考サイト

PHP: sha1 – Manual
http://php.net/manual/ja/function.sha1.php
sha1 — 文字列の sha1 ハッシュを計算する

PHP: base64_encode – Manual
http://php.net/manual/ja/function.base64-encode.php
base64_encode — MIME base64 方式でデータをエンコードする

[PHP] レスポンスタイムを計測する監視スクリプト

仕事先が契約しているデータセンターのウェブサーバーがもう笑うしかないぐらい不安定。僅かなアクセスしか無いにも関わらず、気が付くとサーバーは落ちてるし、502、503、504のエラーが返るのも日常茶飯事。24時間通算してどれぐらい不具合が起きているのだろうか?と気になったので監視スクリプトを作ってみた。

監視スクリプトの仕様

  • 監視スクリプトはターゲットのサーバーとは別の安定したサーバーに置く。
  • 監視スクリプトはPHPで書いてcronで5分ごとに実行させる。
  • ターゲットのサーバーから特定URLをGETするのに要した時間をログに書き出す。

ネットを調べるとpingコマンド利用のサーバー死活監視スクリプトが多数ヒットしたけど、僕が必要なのはブラウザにページが表示されるまでの体感時間なので、HTTPのGETのレスポンスタイムを計測するのが現実的かと。サーバーが完全に死んでいたらタイムアウトになるだろうし。

監視スクリプト

以下のようなスクリプトを作成した。HTTPリクエストの実行にはPEARのHTTP_Requestを利用。logsというサブディレクトリを作成しておくと、日付を名前にしたCSVファイルが自動生成されて、スクリプトが実行されるたびにログが追記される仕組み。監視するURLは複数指定可能にした。

実行例

以下にログファイルの抜粋を例として示す。各行先頭から処理時間(秒)、実行日時、ステータスコード、URLの順にカンマ区切りで記録される。ステータスが200以外のときはレスポンスヘッダの配列ダンプも追記される。

[PHP] アクセスログを記録するクラスを書いてみた

Apacheの生ログとは別に、特定のPHPファイルのアクセスログだけを記録したいことがある。そんなことが簡単に出来るように、いろいろ使い回しが利きそうな汎用のアクセスログクラスを書いてみた。

PHPソース

使い方

アクセスログを記録したいPHPファイルの冒頭にインクルードするだけ。

ログ出力例

[PHP] トラブルに強い問い合わせフォームを作る

前回のエントリで問い合わせフォームのトラブルについて書いた。トラブルフリーな問い合わせフォームは無いけれど、トラブルの影響を最小限に食い止めるための方策はいろいろある。すなわち「問い合わせをもらっていたのに知らなかった」という状況を避ける対処法。僕が気をつけているのは以下のこと。

1. mb_send_mail()の戻り値は必ずチェック

mb_send_mail()はmail()のラッパクラスで、mail()はphp.iniでパス指定されたsendmailコマンドを呼んでいる。sendmailコマンドがリクエストを正しく受け入れると正常ステータスを返すのだけど、実際にメールを送信するのはMTAの役割なので、メールが送られたかどうかをステータスから判断することはできない。だけど、少なくともリクエストは正しく受け入れられないとメールは送られないので、関数の戻り値は必ずチェックしてエラーはユーザに通知する。ユーザに「エラーで送られなかったんだ」と知らしめるために。

2. メール送り先は複数メールアドレスで、異なるドメインも含めて。

受信メールサーバーの不具合や、メール受信者のミスなどを想定し、メールの控えを複数のメールアドレスに送信しておく。全てのメールアドレスが同じドメインだとサーバー不具合で全不通になるので、異なるドメインのメールアドレスも混ぜておく。たとえばGmailとか。

3. バウンスメールを喪失しないようにenvelop senderを必ず指定。

受信メールサーバーの不具合などでメールがバウンスしてきたときに確実に通知を受け取れるように、sendmailコマンドの -f オプションを使ってenvelop senderを必ず指定する。もし指定しないのなら、どのメールアドレスがenvelop senderになるのかホスティングプロバイダのデフォルト設定を確認した上で、そのメールアドレスへの着信を常時チェックするようにする。

4. フォーム投稿をサーバー上のログファイルに記録して頻繁に目を通す。

メールは信頼できないものと割り切って、全ての投稿をサーバー上のログファイルに欠かさず記録する。たとえ不具合によりメールが一切機能しなくても、ログを見れば問い合わせがあったことと問い合わせ内容を知ることができる。ログファイルはウェブサーバーから見れないところに置くこと、破損しないように排他制御を行うこと、確実に書き込むために書き込みエラー時にはリトライを行うこと、そして必ず頻繁に管理者が目を通すこと、など基本的な点は遵守すること。僕は管理しているサイトのアクセスログを毎日朝一で閲覧するので、そのときにこのログにも必ず目を通すようにしている。

スクリプト例

[PHP] メールが拒否される原因は発信元ホスト名にあった件

開発中の会員システムにおいて、自動送信した会員向けメールが「Undeliverable」で戻ってくる問題が発覚。会員向けメール全てが戻ってくるわけではなく、特定ドメイン宛てだけが常に戻ってくる模様。試しに、同じメールアドレスへメールソフトから手動でメールを送ったら問題なく届く。サーバーの sendmail 設定の問題か?

戻ってきたエラーメールには以下の記述がある。

アクセス拒否の原因は「不適切なHELOネーム」。RFCによれば、HELOコマンドにはSMTPクライアントの完全修飾ドメイン名(TLDの指定を含めた完全なドメイン名)か、もしそれが利用できない場合にはIPアドレスを指定しなければならないとのこと。

RFC 2821 – I-D Tag:
http://tools.ietf.org/html/rfc2821#section-4.1.1.1

The argument field contains the fully-qualified domain name of the SMTP client if one is available. In situations in which the SMTP client system does not have a meaningful domain name (e.g., when its address is dynamically allocated and no reverse mapping record is available), the client SHOULD send an address literal (see section 4.1.3), optionally followed by information that will help to identify the client system.

参考: HELO/EHLOコマンドとその応答|メール配信技術用語
http://www.cuenote.jp/documents/smtp/000197.html

何か追加情報が無いかと、戻ってきたメールに添付されている「Delivery report」を開くと、以下の記述あり。

すぐ目に付いたのは先頭行の www。これはローカルなサーバー名に過ぎず、完全なドメイン名とは言えない。もしかしてHELOコマンドにwwwをクライアントホスト名として指定しているの?確信は無いものの念のため管理者に問い合わせたらビンゴ。PHPのモジュールPHPMailerの設定に不備があったとのこと。困ったものだ。

[PHP] NuSOAPでWebサービスを使う

ある会員管理のウェブアプリと連携したウェブサイトを構築する必要があり、そのウェブアプリではSOAPによるWebサービスを提供している。これまでWebサービスというと実装が楽という点でJSONを使うことが多かったので、SOAP通信に関してはいまいち不慣れ。どうしたものかとウェブアプリの説明を読み進むと、NuSOAPというツールキットを使ったサンプルが掲載されていた。NuSOAPを使うことで上位の実装はかなり簡単になりそう。というわけでNuSOAPの覚え書き。

公式サイト

SourceForge.net: NuSOAP – SOAP Toolkit for PHP – Project Web Hosting – Open Source Software
http://nusoap.sourceforge.net/

チュートリアル

以下のサイトにNuSOAPを使ったわかりやすいサンプルあり。以下に簡単なサンプルのコードだけ引用しておく。後半にはWSDLを使った実践的なサンプルもあるので、実際のコーディングにはかなり参考になる。

Simple Object Access Protocol
http://www.scottnichol.com/soap/index.html

Hello World サーバー側サンプル

Hello World クライアント側サンプル

一般的なSOAP通信方法

NuSOAPを使わない一般的な実装は以下サイトが参考になる。

【PHP】PHPでSOAP通信を行う方法
http://estpolis.com/2011/08/834.html

[PHP] Smartyプラグインの作り方

以前のバージョンのSmartyでは{php}ステートメントを使ってテンプレート内でPHPコードを実行できたけど、新しいバージョンでは出来なくなっている模様。本家サイトのマニュアルには以下のように明記されている。

IMPORTANT NOTICE
{php} tags are deprecated from Smarty, and should not be used. Put your PHP logic in PHP scripts or plugin functions instead.

対策はSmartyプラグインを作るしかない。以下にプラグイン作成の手順をまとめておく。

Smartyプラグインの作成手順

1. プラグインの名前を決める。

例えばログインしているユーザ名を取得するプラグインを作るとして名前をmynameとする。

2. するとプラグイン関数の名前が決まるのでPHPのコードを書く。

$paramsはテンプレートから渡ってくる引数で、例えばテンプレート中で{myname var1=’test’ var2=’123′}と呼び出されると、params[‘var1’]=’test1′, params[‘var2’]=’123′ というように配列が渡ってくる。パラメータが指定されずに呼び出される場合も想定してisset(params[‘var1’]) といったチェックを行うこと。

プラグイン関数からの戻り値が必要ない場合にはreturnステートメントを書かなくてもいいけど、処理が正常終了したか真偽を返すといいかも。引数の$smarty変数の参照を利用すれば、例えば$smarty->assign(‘result’, $answer)というようにプラグイン関数内でテンプレート変数をセットすることも可能。

3. 作成したプラグインファイルを <Smartyパス>/libs/pluginsディレクトリに配置する。

Smartyの設定で異なるプラグインディレクトリを設定している場合にはそれに従う。

4. テンプレートからプラグインを呼び出す。

参考サイト

Chapter 18. Extending Smarty With Plugins | Smarty
http://www.smarty.net/docs/en/plugins.tpl

[PHP] どのコーディング規約で開発すべきか

フレームワークを使わないでスクラッチからコードを書くときは、自分でコーディング規約を決める必要がある。どうしたものかとネットを検索したらぴったりの記事を発見したのでリンクしておく。この記事では以下の結論で結んでいる。

利用しているフレームワークの規約がないなら、 PSR-2(日本語) に従っておけば、間違いない!

どのコーディング規約で開発すべき? PHPコーディング規約まとめ – Qiita
http://qiita.com/hshimo/items/04be1f432240c58300f4

PSR-2 というのは、プロジェクトの開発者で組織するPHP-FIGというグループが話し合って決めたコーディングスタイルガイドらしい。

PSR-2 コーディングガイド(日本語)|北海道札幌市のシステム開発会社インフィニットループ
http://www.infiniteloop.co.jp/docs/psr/psr-2-coding-style-guide.html

PHP-FIGのウェブサイトは以下。オープンソースの著名なプロジェクトの開発者がメンバーとして名前を連ねている。

PHP-FIG — PHP Framework Interop Group
http://www.php-fig.org/

最初の記事中にもいろいろリンクされているけど、著名なプロジェクトはそれぞれ独自のコーディング規約を定めている。以下参考まで。

変数の命名規則 – PHPリファレンス
http://alphasis.info/2012/05/php-variable-name/

命名規約 – Zend Framework PHP 標準コーディング規約 – Zend Framework
http://framework.zend.com/manual/1.12/ja/coding-standard.naming-conventions.html

コーディング規約 — CakePHP Cookbook 2.x ドキュメント
http://book.cakephp.org/2.0/ja/contributing/cakephp-coding-conventions.html

[PHP] セッションの基本的な使い方

基本的な操作を簡単にまとめておく。

セッションの開始

session_start関数をコールする。

セッションがまだ開始されていない場合は、セッションIDが割り当てられて新しいセッションが開始される。具体的には、サーバーで生成されたセッションIDがブラウザに送られ、”PHPSESSID”という名前でクッキーに保存される。サーバー側では、セッション変数を保存するための作業ファイルが作成される。

既にセッションが開始されている場合は、クッキーに保存されているセッションIDのセッションが利用できるようになる。

“PHPSESSID” という名前はPHPにおけるデフォルト設定である。サーバーによっては、異なる名前に変更されているかもしれない (php.ini を編集することで変更できる)。プログラム中で “PHPSESSID” の文字列を直接指定する代わりに、session_name関数を使うことで、環境に依存しないプログラムが書けるだろう。 サーバー上に作成される作業ファイルにはセッションIDの名前が付いている。僕のXAMPP環境では、セッションID “ddsuj7031ir4eqk7h6oivs9215″ に関して、C:xampptmpsess_ddsuj7031ir4eqk7h6oivs9215 というファイルが作成されていた。新しいセッションが始まったばかりの状態では中身は空である (ファイルサイズはゼロ)。

セッション変数の読み書き

セッション変数はグローバル配列 $_SESSION 経由でアクセスする。

サーバー上の作業ファイルを覗いてみると、セッション変数が以下のように格納されていた。

セッション変数やセッションの削除

特定のセッション変数を削除したければ、$_SESSION 配列からその要素を削除すればよい。

全てのセッション変数を削除したければ、$_SESSION配列そのものを空にしてやればよい。

セッションを終了してデータを残骸無しに削除したければ、以下の全てを削除するべきである。

  • クッキー (セッションID)
  • グローバル変数 ($_SESSION配列)
  • セッションデータ (サーバーの作業ファイル)

セッションIDの再生成

サーバーに格納されているセッションデータは、セッションIDが漏れることで第三者にセッションを乗っ取られる危険性を秘めている (セッションハイジャックと呼ばれる)。そこで、同じセッションIDを長時間使うことを避け、頻繁にセッションIDを変更することが推奨される。 これを行うためにsession_regenerate_id関数が用意されている。この関数をコールすることで、現在のセッションのデータを保持したまま、セッションIDを新しくすることができる。

セッションハイジャックのリスクに関しては、また別の機会に考察してみたい。

参考サイト

PHP: Sessions – Manual
http://php.net/manual/ja/book.session.php