Archive for the ‘アプリ開発’ Category

PDTで*.php以外の拡張子を持つファイルを関連付ける

PDTに限ったことではなく、Eclipse全般で言えることですが。

例えば拡張子に.includeを持つファイルをPHPファイルとして認識させたいならこんな感じです。

  1. メニューから ウィンドウ -> 設定を選択
  2. 左のペインから 一般 -> コンテンツ・タイプ を選択
  3. 右上の「コンテンツ・タイプ」ペインから テキスト -> PHP コンテンツ・タイプ を選択
  4. 右下の「ファイルの関連付け」ペイン横にある「追加」を押して「*.include」をコンテンツ・タイプとして入力し「OK」を押す
  5. 「OK」を押して設定ウィンドウを閉じる

以上です。
なお、関連付けを行う前に開いていた.includeファイルがある場合は、いったん閉じてから再度開く必要があります。

 

InfoPathの日付形式はyyyy-MM-ddです

InfoPathでは、フィールドに入力されたデータは、そのフィールドの「データ型」に設定された表示形式に変換された上で表示されます。例えば日付型(date)に設定されたフィールドに「2010/03/12」と入力すると「2010年3月12日」と表示されます。また、データ型に適合しないデータが入力されると、該当フィールドが赤枠で表示され「日付のみ指定してください」といったデータの修正を促すメッセージが表示されます。

これはこれで便利な機能なのですが、実際のXMLフィールドに格納される生データの書式がわからないという落とし穴も生じます。これが問題になるのはVSTAを使ってVBやC#で記述されたプログラムからフィールドに直接データを入力しようとする場合で、例えば日付データ型で表示形式が「yyyy/MM/dd」と定義されたフィールドに「2010/03/12」というデータを入れると「日付のみ指定してください」というエラーメッセージが表示されてしまいます。原因はInfoPathでは日付データを ISO形式(yy-MM-dd)という形式で格納するため、これ以外のフォーマットのデータは全てエラーとして認識してしまうのです。

そのフィールドの「見た目」を表示形式ではなく、InfoPathで定義されているデータ型の記述フォーマットに厳密に適合した形式で入力してあげる必要がある、というのがミソです。まあ文字列型に数字入れてもエラーにはならないので、ハマるとしたら確実に日付関連のフォーマットぐらいでしょうか。私は見事にそれにハマったわけですが。

フォーマットさえ分かれば、後はこんな↓感じで簡単なコードだけで変換する事が出来ます。

日付の場合:
string todaysDate = DateTime.Today.ToString(“yyyy-MM-dd”);

日付と時刻の場合:
string todaysDateAndTime = DateTime.Today.ToString(“yyyy-MM-ddThh:mm:ss”);

下記のドキュメントにDateの形式に関する情報がちょろっと確認できます

Date のメンバ
http://msdn.microsoft.com/ja-jp/library/microsoft.office.interop.InfoPath.semitrust.date_members.aspx

ただし上記のMicrosoftの公式ドキュメントにもあるように、この形式は共通言語仕様(CLS)には準拠していないため、たとえばこのデータを別システム(DBなど)に渡そうとする場合は再度データフォーマットの変換が必要になります。特に日付と時刻の型(YYYY-MM-DDThh:mm:ss)は、そのままMicrosfot SQL Serverのdatetime型フィールドに突っ込むとエラーになる(涙)ので、こんな感じで書式を再変換してやる必要があります

SQLServerのdatetime型に変換する
string todaysDateAndTime = DateTime.Parse(todaysDateAndTime).ToString();

しっかしInfoPath情報少ないですね、、、

 

ネットワークドライブにSQL Server エージェントのタスクはアクセスできない

結論から言うとUNC (\\サーバ名\フォルダ名)でアクセスしなさい、という事です。

もくろんでいたのは、別システムがsambaの共有ディレクトリ上に定期的に吐きだすテキストデータを、SSISパッケージ化したタスクで取得&データベースに取り込むというシナリオでした。その際にsambaの共有ディレクトリをQ:ドライブにマップして「Q:\hogehoge\Datafile.txt」という形式でアクセスしてやれと。

ところがSSISタスクを作成し手動で実行すると問題なく動作するのに、SQL Serverエージェントの定期ジョブに登録&実行するとエラーで停止してしまうんです。その際に表示されるエラーはこんな感じで、なんというか、決して親切ではないですね。

開始: xx:xx:xx
エラー: 20xx-xx-xx xx:xx:xx.xx
コード: 0xC002F304
ソース: ファイル システム タスク ファイル システム タスク
説明: エラーが次のエラー メッセージで発生しました: “パスの一部が見つかりません。”。 エラー終了
DTExec: パッケージの実行から返されました DTSER_FAILURE (1)。

試行錯誤の末、ローカル上に置いたファイルであれば問題ない事が判明。ネットワーク経由でのファイルアクセスが原因だという事になり、アクセス権限やらSSISパッケージのProtectionLevelの変更やら、いろいろ試したけど全部ダメ。

結局ネットワークドライブがそもそも使えねんじゃね?という結論にたどり着きました。
Windows Server 2000の関連資料としてこんな記述を見つけましたが、Windows Server 2008でも同じ現象が起きます。

ネットワーク ドライブの削除、再利用ができない
http://support.microsoft.com/kb/417903/ja

上記資料の「原因」の欄にある「SMB (Server Message Block)セッションは、ログオンとユーザー アカウント毎に管理されます。」というのがすべてを物語っています。つまりドライブレターの割り当ては、アカウント固有というわけではなくアカウントかつそのセッション固有のものである、と。

探してみたら同じような事しようとしてハマった人を発見。
フォーラムとはいえ、回答者によって「できる」「できない」で真っ二つに分かれているのがなんとも。
今回の件でいえば「ちゃっぴ」さんが正解です。

ネットワークドライブへのバックアップ
http://social.msdn.microsoft.com/Forums/ja-JP/sqlserverja/thread/d81f3dbc-9235-45e4-9743-2f854e5647d9

 

PHP5.3.xは鬼門か?

何も考えずPHP5.3.xに上げたら動かないモジュール続発。
どうやら寝た子を起こしてしまったらしい。(泣)

現在認識している「問題のあるモジュール」は以下の3つ
・mecab
・php_perl
・apc (Alternative PHP Cache)

とりあえずmecab以外はパッチ当てたりベータ入れたりしてその場をしのいで見たものの、開発環境が残念な感じになってしまったのも事実。

mecabもmecab本体とPHPにパッチ当てれば動くようにななるんだが、、、

とりあえず様子をみるか。

 

date.timezoneを使えと怒られた

PHPを5.3.xに上げた後、いつも流してるスクリプトでdate()関数呼び出したらこんな警告が出た。回避するには/etc/php.iniでdate.timezoneでタイムゾーンを明示するか、事前にdate_default_timezone_set()を呼ばなければならないらしい。

PHP Warning: date(): It is not safe to rely on the system’s timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier.

オレ訳:
システムのタイムゾーン設定に依存するのは確実ではありません。date.timezoneを設定するかdate_default_timezone_set()を設定する必要があります。いずれかを設定後も引き続きこの警告が出る場合は、タイムゾーンIDが間違っている可能性が高いです。

調べたら日本のタイムゾーンIDは「Asia/Tokyo」なので、

# vi /etc/php.ini

[Date]
; Defines the default timezone used by the date functions
date.timezone = Asia/Tokyo

と設定するか、プログラム内でdate()関数を呼び出す前に

date_default_timezone_set(‘Asia/Tokyo’);

と宣言してあげてください。

ちなみにPHPでサポートしているタイムゾーンのリストはこちらで確認できます。

付録 H. サポートされるタイムゾーンのリスト

ちなみに影響範囲はPHP5.1.x以降らしいので、5.3.x固有の問題というわけではなさそう。単に俺が今まで気づかなかっただけか?

 

PHPで重複起動を防ぐ

cronで定期的に回すバッチなど、複数同時に呼び出したくない処理は、起動時に自分以外の誰かが既に突っ走ってないかチェックしてやる

$self = basename($_SERVER['SCRIPT_NAME']); //自身のファイル名取得
exec("ps aux | awk '{print $12}' | grep $self", $retVal); //プロセスを調べる
if(count($retVal) > 2) { //3つ以上の場合は誰か突っ走ってる
	echo "プロセスが既に起動中しています。処理を中止します。\n";
	exit(0);
}

$selfにチェックしたいファイル名を入れてやれば、自分自身以外のプログラムの起動チェックも可能。

 

file_get_contents()で値が取れないとき

リクエストそのものが問題なく終了しているのなら、サーバーが何らかのエラーメッセージを返している可能性が高い。とりあえずget_headers()使って様子を見よう。

$url = 'http://www.tokiwasou.com';
print_r(get_headers($url));

(結果)
Array
(
[0] => HTTP/1.1 200 OK
[1] => Date: Thu, 11 Jun 2009 15:17:09 GMT
[2] => Server: Apache
[3] => Content-Length: 717
[4] => Connection: close
[5] => Content-Type: text/html
)

問題があれば0行目にHTTPのエラーコードが格納されているのでそれを確認する。

ちなみにURLの後ろにオプションのフラグを立てれば、連想配列で結果を返してくれる。
こういう細かい心配りがPHPはうれしい。

$url = 'http://www.tokiwasou.com';
print_r(get_headers($url, 1));

(結果)
Array
(
[0] => HTTP/1.1 200 OK
[Date] => Thu, 11 Jun 2009 15:17:23 GMT
[Server] => Apache
[Content-Length] => 717
[Connection] => close
[Content-Type] => text/html
)

大抵はコンテンツがリダイレクト先にあって301とか302が返ってきているか、500(Internal Server Error)が返されているはず。レアなところだと401(Gone:イッちまった)が返される場合もある。それはもうキミのせいじゃない。男らしくすっぱりあきらめよう。

 

PHPでプロキシを使いたい(stream_context_create編)

Proxy経由でfopen()やfile_get_contents() を使う場合は、stream_context_create()を使ってプロキシ経由のストリームコンテキストを作成してやる。

そいでそいつをfopen()やらfile_get_contents()に渡してあげればOK。

(1) Proxy用オプション設定(必要最低限Ver.)

$sc_opts = array(
‘http’=>array(
‘proxy’ => ‘tcp://192.168.0.XX:8888‘,
‘request_fulluri’ => True
)

(2) ストリームコンテキスト作成

$sc= stream_context_create($sc_opts);

(3) コンテンツ取得

$contents = file_get_contents(‘http://www.tokiwasou.com‘, TRUE, $sc);

(4) 結果表示

var_dump($contents);

ただし上記設定はProxy経由で通信するために最低限必要なオプションを指定しているだけなので、例えばユーザーエージェントで表示するページを変えているサイトとかだと、期待したコンテンツを取得できない(あるいはアクセスそのものが拒否されてしまう)ことがある。

あるいはコンテンツが複数回のリダイレクトの果てにようやくたどり着けるような、若干危険な香り漂うサイトとかだと、リダイレクト指定をちゃんとしてやらないと途中で泣きながら帰ってきてしまうケースもある。というかあった。

完全な解決策とはいきませんが、とりあえずこれだけ指定してやれば大丈夫だろ的なオプション設定を作ったので、どうぞ。

お約束ですがプロキシのIP/ポート番号とか取得先URLとかは自分の環境に合わせて適宜修正して下さいな。

各設定の詳細が知りたい勉強熱心な方は、文末に載せておいた参考サイトがおすすめです。勉強になります。

# Proxy用オプション設定(暫定最強Ver.)

$sc_opts = array(
‘http’=>array(
‘method’=> ‘GET’,
‘header’=>
“Host: www.tokiwasou.com\r\n” .
“Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n”.
“Accept-Language: ja,en-us;q=0.7,en;q=0.3\r\n”.
“Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7\r\n”.
“Keep-Alive: 300\r\n”,
‘user_agent’ => ‘Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.0.10)’,
‘proxy’ => ‘tcp://192.168.0.XX:8888‘,
‘request_fulluri’ => True,
‘max_refirects’ => 20
)

参考になったサイト:
PHP.net(file_get_contents)

HTTPとHTTPS (コンテキストのオプションが載ってる)

 

Proxyを設定する

環境変数を参照してプロキシの有無を判別するアプリケーションならばこれでOK。

————————————————————

# vi /etc/profile.d/proxy.sh

export http_proxy=http://192.168.0.xx:8888/
export ftp_proxy=http://192.168.0.xx:8888/
export HTTP_PROXY=http://192.168.0.xx:8888/
export FTP_PROXY=http://192.168.0.xx:8888/

————————————————————

# vi /etc/profile.d/proxy.csh

setenv http_proxy http://192.168.0.xx:8888/
setenv ftp_proxy http://192.168.0.xx:8888/
setenv HTTP_PROXY http://192.168.0.xx:8888/
setenv FTP_PROXY http://192.168.0.xx:8888/

————————————————————

言い換えればhttp_proxyまたはHTTP_PROXYを参照しないアプリケーションでは意味がないです。念のため。

赤字の部分は自分の環境向けに適宜読みかえてね。

ちなみにproxy.csh は C Shell 用。標準のBourne Shellしか使わないのなら、proxy.shだけ設定してあげればOK。(だよね?)

参考サイト
Linux/環境変数の設定