PHPで Windows用週間天気予報プログラムを作る

(1/1)
PHP で天気予報を求める(その 2)」で、livedoor 天気情報 を使った週間天気予報プログラムを作った。今回は、これを Windows用アプリとして移植する。

2020 年(令和 2 年)7 月 31 日をもって、livedoor 天気サービスが終了することがアナウンスされている。それ以降、ここで紹介するプログラムは動作しなくなる。

サンプル・プログラム

PHPでWindowsアプリ開発:週間天気予報

準備

今回は画像ファイルと複雑な XML 処理があるため、入念な事前準備が必要になる。

まず画像の方だが、WinBinderFreeImage をコールする。ダウンロードサイトからバージョン 3.17.0 をダウンロードし、"freeimage.dll" を ext ディレクトリに配置する。

次に XML 処理だが、"PEAR.php" が必要になる。
ダウンロードサイト から最新版をダウンロードし、"weeklyweatherwin.phpw" と同じディレクトリに配置する。
これ以外に、「PHP で Google API を利用する Windows アプリを開発」で使った PEAR: XML_Unserializer および XML_Serializer が必要である。

PEAR 関連ファイルは、"weeklyweatherwin.phpw" と同じディレクトリから  require_once  できるように、それぞれのソースに若干修正を加えている。
また、"weeklyweatherwin.phpw" と同じフォルダに、WinBinder用にカスタマイズした "php.ini" を配置し、 require_once  が確実に作用するようにした。

プログラムの解説:画像ファイルの扱い

天気予報アイコン
PHP で天気予報を求める(その 2)」では、左図のように天気予報のあらわす画像ファイルを表示した。この画像ファイルはネット上にあるのだが、WinBinder で画像コントロールを扱う関数 wb_create_imageFreeImage を利用しており、画像ファイルを読み込む関数 FreeImage_Load が URL ロードに対応していない。そこで画像ファイルをローカルドライブに配置する必要がある。
ところが、bamcompile で EXE の中に画像ファイルを埋め込んでも、FreeImage を使ってロードすることができない。また、"freeimage.dll" もローカルに配置する必要がある。
そこで、bamcompile を使って画像ファイルや "freeimage.dll" を EXE ファイルに埋め込んでおき、初回実行時にローカルドライブに展開するためのユーザー関数 initialize を用意した。

0049: /**
0050:  * 各種ファイルをローカルに展開する
0051: */
0052: function initialize() {
0053:     global $Setting;
0054: 
0055:     //FreeImage
0056:     if (! file_exists('ext/freeimage.dll')) {
0057:         $dll = file_get_contents('ext/freeimage.dll');
0058:         @mkdir('ext');
0059:         $outfp = fopen('ext/freeimage.dll', 'wb');
0060:         fwrite($outfp$dll);
0061:         fclose($outfp);
0062:     }
0063: 
0064:     //天気予報アイコン
0065:     $path = 'icon';
0066:     for ($i = 1; $i <= 30; $i++) {
0067:         $fname = $i . '.gif';
0068:         if (! file_exists($path . '/' . $fname)) {
0069:             $icon = file_get_contents($path . '/' . $fname);
0070:             @mkdir($path);
0071:             $outfp = fopen($path . '/' . $fname, 'wb');
0072:             fwrite($outfp$icon);
0073:             fclose($outfp);
0074:         }
0075:     }
0076: 
0077:     //アプリケーション・アイコン
0078:     $path = 'icon';
0079:     if (! file_exists(ICONFILE)) {
0080:         $icon = file_get_contents(ICONFILE);
0081:         @mkdir($path);
0082:         $outfp = fopen(ICONFILE, 'wb');
0083:         fwrite($outfp$icon);
0084:         fclose($outfp);
0085:     }
0086: 
0087:     //設定ファイルの読み込み
0088:     $Setting['pref'] = 0;
0089:     $Setting['city'] = 0;
0090:     if (file_exists(INIFILE)) {
0091:         $options = array('parseAttributes' => TRUE);
0092:         $xml = new XML_Unserializer($options);
0093:         $xml_data = @file_get_contents(INIFILE);
0094:         $xml->unserialize($xml_dataFALSE);
0095:         $Setting = $xml->getUnserializedData();
0096:         unset($xml);
0097:     }
0098: }

関数  file_get_contents  や  fopen  は EXE ファイルに埋め込まれているファイルを読み込むので、これを利用して、ローカルドライブに該当ファイルがないときだけディレクトリを作成し、個々のファイルをセーブする。

この他、EXE に埋め込んだアプリケーション・アイコン "ICONFILE" をローカルにセーブしている。これは、アプリケーション・アイコンとして WinBinder関数 wb_set_image で読み込むための前処理である。

また、前回選択した都道府県名、都市名を設定ファイル "INIFILE" から読み込むようにしている。
"INIFILE" は XML 形式としており、PEAR::XML_Unserializer によってグローバル変数 $setting へ保存する。

プログラムの解説:ForecastMap(全国地点定義表)の取得

0165: /**
0166:  * livedoor天気予報RSSのURLを取得する
0167:  * @param int $city 地域別に定義されたID番号
0168:  * @return string URL / FALSE=失敗(引数不正)
0169: */
0170: function getWeeklyWeatherURL($city) {
0171:     return "http://weather.livedoor.com/forecast/rss/area/{$city}.xml";
0172: }
0173: 
0174: /**
0175:  * ForecastMap(全国地点定義表)を解釈する
0176:  * @param array  $forecasttable  ForecastMapを格納する配列
0177:  * @return int 配列に格納した都市数
0178: */
0179: function parseForecastMap(&$forecasttable) {
0180:     //XML読み込み
0181:     $options = array('parseAttributes' => TRUE);
0182:     $xml = new XML_Unserializer($options);
0183:     $xml_data = @file_get_contents(ForecastMap);
0184:     $xml->unserialize($xml_dataFALSE);
0185:     $arr = $xml->getUnserializedData();
0186: 
0187:     //レスポンス・チェック
0188:     if (! isset($arr['channel'])) {
0189:         unset($xml);
0190:         return 0;
0191:     }
0192: 
0193:     //必要な情報を配列へ
0194:     $cnt = 0;
0195:     foreach ($arr['channel']['ldWeather:source']['pref'] as $pref) {
0196:         $pref_title = utf8_internal($pref['title']);
0197:         foreach ($pref['city'] as $city) {
0198:             $city_title = utf8_internal($city['title']);
0199:             $forecasttable[$pref_title][$city_title] = (string)$city['id'];
0200:             $cnt++;
0201:         }
0202:     }
0203:     unset($xml);
0204: 
0205:     return $cnt;
0206: }

都道府県名と都市名を取得する方法は「PHP で天気予報を求める(その 2)」で述べたとおりだが、取得データが RSS 形式で、XML 属性情報を有している。これを XML_Unserializer で扱うには、XML_Unserializer OptionsparseAttributesTRUE にしてやる必要がある。
名前空間を意識する必要はなく、「PHP でコロンを含む XML要素名を扱う方法」で紹介したような方法は使わず、コロンも含んで要素名に展開される。

プログラムの解説:設定値の保存

0127: /**
0128:  * 設定値を保存する
0129:  * @param int $window 親ウィンドウID
0130:  * @return string URL / FALSE=失敗(引数不正)
0131: */
0132: function save_inidata($window) {
0133:     $pref = wb_get_selected(wb_get_control($windowIDC_PREF_COMBO));
0134:     $city = wb_get_selected(wb_get_control($windowIDC_CITY_COMBO));
0135: 
0136:     $options = array(
0137:         "indent"          => '    ',
0138:         "linebreak"       => "\n",
0139:         "typeHints"       => FALSE,
0140:         "addDecl"         => TRUE,
0141:         "encoding"        => 'UTF-8',
0142:         "rootName"        => 'weeklyweather',
0143:         "rootAttributes"  => array('version' => APPVERSION),
0144:         "defaultTagName"  => 'item',
0145:         "attributesArray" => '_attributes'
0146:     );
0147:     $serializer = new XML_Serializer($options);
0148:     $arr = array('pref' => $pref, 'city' => $city);
0149:     $res = $serializer->serialize($arr);
0150: 
0151:     //設定ファイル保存
0152:     if ($res) {
0153:         $xml = $serializer->getSerializedData();
0154:         if (! file_exists(INIFILE)) {
0155:             $path = 'ini';
0156:             @mkdir($path);
0157:         }
0158:         $outfp = fopen(INIFILE, 'wb');
0159:         fwrite($outfp$xml);
0160:         fclose($outfp);
0161:     }
0162:     unset($serializer);
0163: }

プログラム中で選択した都道府県名と都市名は、WinBinder関数 wb_get_selected によって選択番号を取得し、これを PEAR::PEAR::XML_Serializer を使って XML 形式に変換、設定ファイル "INIFILE" に保存する。

コンパイル

コンパイルは「PHP で bamcompile を使って EXE プログラムを生成」で紹介したのと同じ手順で、bamcompile.exe を使ってプロジェクトファイル "calcwin.bcp" をコンパイルする。
このとき、ファイルは下図のように配置されている必要がある。
weeklyweatherwin
│ weeklyweatherwin.bcp
└─weeklyweatherwin
   │  weeklyweatherwin.phpw
   │  weeklyweatherwin.rc
   │  application.ico
   │  php.ini
   │  PEAR.php
   │ Unserializer.php
   │ Parser.php
   │ Serializer.php
   │ Util.php
   │  php_mbstring.dll
   │  php_winbinder.dll
   ├─include
   │ │ wb_generic.inc.php
   │ │ wb_resources.inc.php
   │ │ wb_windows.inc.php
   │ │ winbinder.php
   │ └─fi
   │     freeimage.inc.php
   ├─ext
   │   freeimage.dll
   └─icon
       weeklyweatherwin.ico
       1.gif
       2.gif
       3.gif
      ‥‥
コマンドラインから "bamcompile weeklyweatherwin.bcp" を実行する。コンパイルが完了すると、"weeklyweatherwin.exe" が生成されている。
"weeklyweatherwin.exe" は、DLL 不要で、単独で動作する EXE プログラムである。ただし、前述の通り、初回起動時に ext と icon の 2 つのディレクトリを作成し、必要なファイルをセーブする。

参考サイト

(この項おわり)
header