サンプル・プログラムの実行例
| バージョン | 安定版 リリース |
最新 バージョン |
最新リリース日 | アクティブ サポート終了 |
セキュリティ サポート終了 |
|---|---|---|---|---|---|
| 8.5 | 2025-11-20 | 8.5.9 | 2026-07-30 | 2027-12-31 | 2029-12-31 |
| 8.4 | 2024-11-21 | 8.4.24 | 2026-07-30 | 2026-12-31 | 2028-12-31 |
| 8.3 | 2023-11-23 | 8.3.33 | 2026-07-30 | 2025-12-31 | 2027-12-31 |
| 8.2 | 2022-12-08 | 8.2.33 | 2026-07-30 | 2024-12-31 | 2026-12-31 |
| 8.1 | 2021-11-25 | 8.4.34 | 2025-12-18 | 2023-11-25 | 2025-12-31 |
| 8.0 | 2020-11-26 | 8.0.30 | 2023-08-03 | 2022-11-26 | 2023-11-26 |
| 7.4 | 2019-11-28 | 7.4.33 | 2022-11-03 | 2021-11-28 | 2022-11-28 |
| 7.3 | 2018-12-06 | 7.3.33 | 2021-11-18 | 2020-12-06 | 2021-12-06 |
| 7.2 | 2017-11-30 | 7.2.34 | 2020-10-01 | 2019-11-30 | 2020-11-30 |
| 7.1 | 2016-12-01 | 7.1.33 | 2019-10-24 | 2018-12-01 | 2019-12-01 |
| 7.0 | 2015-12-03 | 7.0.33 | 2018-12-06 | 2017-12-03 | 2019-12-03 |
| 5.6 | 2014-08-28 | 5.6.40 | 2019-01-10 | 2017-01-19 | 2018-12-31 |
| 5.5 | 2013-06-20 | 5.5.38 | 2016-07-21 | 2015-07-10 | 2016-07-10 |
| 5.4 | 2012-03-01 | 5.4.45 | 2015-09-03 | 2014-09-14 | 2015-09-14 |
| 5.3 | 2009-06-30 | 5.3.29 | 2014-08-14 | - | 2014-08-14 |
| 5.2 | 2006-11-02 | 5.2.17 | 2011-01-06 | - | 2011-01-06 |
| 5.1 | 2005-11-24 | 5.1.6 | 2006-08-24 | - | 2006-08-24 |
| 5.0 | 2004-07-13 | 5.0.5 | 2005-09-05 | - | 2005-09-05 |
| 4.4 | 2005-07-11 | 4.4.9 | 2008-08-07 | - | 2007-08-07 |
| 4.3 | 2002-12-27 | 4.3.11 | 2005-03-31 | - | 2005-03-31 |
| 4.2 | 2002-04-22 | 4.2.3 | 2002-09-06 | - | 2002-09-06 |
| 4.1 | 2001-12-10 | 4.1.2 | 2002-02-27 | - | 2002-03-12 |
| 4.0 | 2000-05-22 | 4.0.6 | 2001-06-23 | - | 2001-06-23 |
| 3.0 | 1998-06-06 | 3.0.18 | 2000-10-20 | - | 2000-01-20 |
| 2.0 | 1997-11-01 | - | - | - | - |
| 1.0 | 1995-06-08 | - | - | - | - |
目次
サンプル・プログラムのダウンロード
| lifecyclePHP.php | サンプル・プログラム本体 |
| phpVersion.xml | PHPのライフサイクルを格納したデータベース・ファイル |
| pahooInputData.php | データ入力に関わる関数群。 使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。 |
| pahooScraping.php | スクレイピング処理に関わるクラス pahooScraping。 スクレイピング処理に関わるクラスの使い方は「PHPで作るRSSビューア」を参照。include_path が通ったディレクトリに配置すること。 |
| pahooCache.php | キャッシュ処理に関わるクラス pahooCache。 キャッシュ処理に関わるクラスの使い方は「PHPで天気予報を求める」を参照。include_path が通ったディレクトリに配置すること。 |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 1.0.1 | 2026/03/30 | 修正: → |
| 1.0.0 | 2026/03/22 | 初版 |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 2.0.1 | 2025/08/11 | getParam() bug-fix |
| 2.0.0 | 2025/08/11 | pahooLoadEnv() 追加 |
| 1.9.0 | 2025/07/26 | getParam() 引数に$trim追加 |
| 1.8.1 | 2025/03/15 | validRegexPattern() debug |
| 1.8.0 | 2024/11/12 | validRegexPattern() 追加 |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 1.2.1 | 2024/10/31 | __construct() 文字化け対策 |
| 1.2.0 | 2024/09/29 | getValueFistrXPath() 属性値でない指定に対応 |
| 1.1.0 | 2023/10/15 | getValueFistrXPath() 追加 |
| 1.0.1 | 2023/09/29 | __construct() bug-fix |
| 1.0.0 | 2023/09/18 | 初版 |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 1.3.0 | 2025/12/06 | PHP8.5対応:curl_closeを使わない |
| 1.2.0 | 2025/09/06 | cLoad() HTTPヘッダを送信できるようにした |
| 1.1.3 | 2025/08/10 | var→public |
| 1.1.2 | 2023/07/22 | bug-fix |
| 1.1.1 | 2023/02/11 | コメント追記 |
サンプル・プログラムの流れ
準備:pahooInputData 関数群
また、各種クラウドサービスに登録したときに取得するアカウント情報、アプリケーションパスワードなどを登録した .pahooEnv ファイルから読み込む関数 pahooLoadEnv を備えている。こちらについては、「各種クラウド連携サービス(WebAPI)の登録方法」をご覧いただきたい。
準備:pahooCache クラス
pahooCache.php
13: class pahooCache {
14: const LIFE_CACHE = (2 * 60); // キャッシュ保持時間(デフォルト;分)
15: const DEF_DIRCACHE = './pcache/'; // キャッシュ・ディレクトリ(デフォルト)
16:
17: public $lifeCache; // キャッシュ保持時間(分)(0:キャッシュしない)
18: public $dirCache; // キャッシュ用ディレクトリ
19: public $httpHeader; // HTTPヘッダ(空文字の時は何も送らない)
20: public $error; // エラーフラグ
21: public $errmsg; // エラーメッセージ
22: public $debug; // デバッグ用ファイル名
23:
24: /**
25: * コンストラクタ
26: * 参考サイト https://www.pahoo.org/e-soul/webtech/php06/php06-72-01.shtm
27: * @param int $life キャッシュ保持時間(分)(省略可能)
28: * @param string $dir キャッシュ・ディレクトリ(省略可能)
29: * @param array $httpHeader httpヘッダに渡す配列(省略可能)
30: * USER AGENT偽装に用いることを想定
31: * (例)
32: * array(
33: * 'User-Agent: Mozilla/5.0(Windows NT 10.0; Win64; x64) pahooAppy/pahoo.org AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36',
34: * 'Accept-Language: ja-JP'
35: * );
36: * @return なし
37: */
38: function __construct($life=self::LIFE_CACHE, $dir=self::DEF_DIRCACHE, $httpHeader=NULL) {
39: if ($life < 0) {
40: $life = 0;
41: }
42: if (preg_match('/\/$/ui', $dir) == 0) {
43: $dir = $dir . '/';
44: }
45: $this->error = FALSE;
46: $this->errmsg = '';
47: $this->debug = '';
48: $this->lifeCache = $life;
49: $this->dirCache = $dir;
50: $this->httpHeader = $httpHeader;
51:
52: // PHP5以上であることを調べる.
53: if (! $this->isphp5over()) {
54: $this->error = TRUE;
55: $this->errmsg = '動作にはPHP5以上が必要です';
56: return;
57: }
58:
59: // キャッシュ・ディレクトリが無ければ作成する.
60: if (! is_dir($this->dirCache)) {
61: $res = mkdir($this->dirCache, 0744);
62: if ($res == FALSE) {
63: $this->error = TRUE;
64: $this->errmsg = 'キャッシュ・ディレクトリ "' . $this->$dirCache . '" の作成に失敗しました';
65: return;
66: }
67: }
68: }
そこで、頻繁に変更がないデータについては、一度取り込んだら、こちら側のサーバのローカルストレージにキャッシュしておく仕組みを用意した。それが pahooCacheクラス である。同梱のクラス・ファイル "pahooCache.php" は include_path が通ったディレクトリに配置してほしい。他のプログラムでも pahooCacheクラス を利用するが、常に最新のクラス・ファイルを1つ配置すればよい。
pahooCacheクラス の注意ポイントは、キャッシュ時間(単位:分)とキャッシュを保存するディレクトリをコンストラクタで指定している点だ。これらはプログラムによって変わるものである。インスタンス化するときの値は、pahooCacheクラス を利用するメイン・プログラムの方で解説する。
サイトによっては、User-Agent などを必要とすることがあるだろう。そこで、第3引数に HTTPヘッダ として送信するデータを配列で渡すことができるようにした。配列の構造はコメントを参照していただきたい。
PHPのクラスについては「PHPでクラスを使ってテキストの読みやすさを調べる」を参照されたい。
準備:pahooScraping クラス
pahooScraping.php
13: class pahooScraping {
14: private $dom; //DOMDocument
15: private $xpath; //DOMXPath
16: private $language; //使用言語(初期値は日本語)
17: private $error; //エラー・メッセージ
18: private $errorTable; //エラー番号 => エラー・メッセージ 一覧
19:
20: /**
21: * コンストラクタ
22: * @param string $contents 解析対象テキスト(UTF-8)
23: * @return object /NULL:PHP8未満のため実行できない.
24: */
25: function __construct($contents) {
26: //PHPのバージョンをチェックする.
27: if (! $this->isphp8over()) {
28: $this->error = 111;
29: throw new Exception($this->getErrorMessage());
30: return NULL;
31: }
32: //プロパティの初期値を設定する.
33: try {
34: // Unicodeの範囲を指定
35: $map = [ 0x80, 0xFFFF, 0, 0xFFFF ];
36: // DOMの文字化け対策:UTF-8文字を数値エンティティに変換
37: $contents = mb_encode_numericentity($contents, $map, 'UTF-8');
38: // DOMDocument クラス
39: $this->dom = new DOMDocument();
40: libxml_use_internal_errors(TRUE);
41: @$this->dom->loadHTML($contents);
42: libxml_clear_errors();
43: $this->xpath = new DOMXPath($this->dom);
44: } catch (Exception $e) {
45: $this->error = 121;
46: throw new Exception($e->getMessage());
47: return FALSE;
48: }
49:
50: //エラー番号 => エラー・メッセージ 一覧
51: $this->errorTable = array(
52: 111 => array('PHP8以上が必要です', 'PHP8 or higher is required'),
53: 121 => array('DOMエラーです', 'DOM error'),
54: 999 => array('不明のエラー', 'unknown error'),
55: );
56: }
PHPのクラスについては「PHPでクラスを使ってテキストの読みやすさを調べる」を参照されたい。
解説:各種定数など
変更不可となっている定数以外は、自由に変更できる。
lifecyclePHP.php
66: // 各種定数(START) ===========================================================
67:
68: // 表示幅(ピクセル)
69: define('WIDTH', 640);
70:
71: // ライフサイクルを保存するXMLデータ・ファイル名(フルパス)
72: define('FILE_XML_VERSION', './phpVersion.xml');
73:
74: // サポート終了のCSSスタイル(クラス)
75: define('CLASS_END', 'end');
76:
77: // 間もなくサポート終了のCSSスタイル(クラス)
78: define('CLASS_NEAREND', 'near-end');
79:
80: // 間もなくサポート終了までの期間(日)
81: define('LIFE_NEAR', 180);
82:
83: // キャッシュ保持時間(分) 0:キャッシュしない
84: // PHP公式サイトへのアクセス負荷軽減のため.
85: define('LIFE_CACHE_DATA', (60 * 24));
86:
87: // キャッシュ・ディレクトリ
88: // 書き込み可能で,外部からアクセスされないディレクトリを指定してください.
89: define('DIR_CACHE_DATA', './pcacheLifecycle/');
90:
91: // 公式サイトアクセスのためのUSER AGENT
92: $HttpHeaders = array(
93: 'user_agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36'
94: );
95:
96: // 各種定数(END) ===============================================================
解説:XMLデータファイル
lifecyclePHP.php
173: /**
174: * XMLファイルからライフサイクルを読み込む.
175: * @param string $fname XMLファイル名
176: * @param array $items ライフサイクルを格納する配列
177: * @return int 取得数/FALSE:失敗
178: */
179: function loadXMLfile($fname, &$items) {
180: global $FlagUpdate;
181:
182: $xml = @simplexml_load_file($fname);
183:
184: // エラー・チェック
185: if ($xml == FALSE) return FALSE;
186: if (! isset($xml->product)) return FALSE;
187:
188: $cnt = 0;
189: foreach ($xml->product as $product) {
190: $branchVersion = (string)$product->branchVersion;
191: $initialRelease = (string)$product->initialRelease;
192: $latestVersion = (string)$product->latestVersion;
193: $latestRelease = (string)$product->latestRelease;
194: $activeEnd = (string)$product->activeEnd;
195: $securityEnd = (string)$product->securityEnd;
196: $items[$branchVersion]['initialRelease'] = $initialRelease;
197: $items[$branchVersion]['latestVersion'] = $latestVersion;
198: $items[$branchVersion]['latestRelease'] = $latestRelease;
199: $items[$branchVersion]['activeEnd'] = $activeEnd;
200: $items[$branchVersion]['securityEnd'] = $securityEnd;
201: $cnt++;
202: }
203:
204: $FlagUpdate = FALSE;
205: return $cnt;
206: }
lifecyclePHP.php
208: /**
209: * XMLファイルにライフサイクルを書き戻す.
210: * @param string $fname XMLファイル名
211: * @param array $items ライフサイクルを格納する配列
212: * @return int 保存したバイト数/0:更新無し/FALSE:保存失敗
213: */
214: function saveXMLfile($fname, $items) {
215: global $FlagUpdate;
216:
217: // XMLデータベースが更新されていなければスキップ
218: if (! $FlagUpdate) return 0;
219:
220: // 配列をキー値で降順ソート
221: krsort($items);
222:
223: // 現在の日時
224: $now = date('Y-m-d H:i:s');
225:
226: $xml =<<< EOT
227: <?xml version="1.0" encoding="utf-8" ?>
228: <!-- PHPライフサイクル -->
229: <atit:database xmlns:atit="urn:incidentList">
230: <update>{$now}</update>
231:
232: EOT;
233: foreach ($items as $branchVersion=>$item) {
234: $xml .=<<< EOT
235: <product>
236: <branchVersion>{$branchVersion}</branchVersion>
237: <initialRelease>{$item['initialRelease']}</initialRelease>
238: <latestVersion>{$item['latestVersion']}</latestVersion>
239: <latestRelease>{$item['latestRelease']}</latestRelease>
240: <activeEnd>{$item['activeEnd']}</activeEnd>
241: <securityEnd>{$item['securityEnd']}</securityEnd>
242: </product>
243:
244: EOT;
245: }
246:
247: $xml .=<<< EOT
248: </atit:database>
249:
250: EOT;
251: $n = @file_put_contents($fname, $xml);
252:
253: return $n;
254: }
なお、XMLデータファイルは、下記に示すような構造をしている。
解説:ブランチバージョンを取得する
lifecyclePHP.php
273: /**
274: * 公式サイトを参照し,ブランチバージョンを取得する.
275: * @param array $items ライフサイクルを格納する配列
276: * @param string $errmsg エラー・メッセージを格納する(正常なら空文字)
277: * @return int 更新件数/FALSE:更新失敗
278: */
279: function getLifecycle(&$items, &$errmsg) {
280: global $HttpHeaders, $FlagUpdate;
281:
282: // 公式サイト
283: $url = 'https://www.php.net/supported-versions.php';
284:
285: // インスタンスを生成する.
286: $pcc = new pahooCache(LIFE_CACHE_DATA, DIR_CACHE_DATA, $HttpHeaders);
287:
288: // コンテンツを読み込む
289: $errmsg = '';
290: $contents = $pcc->cLoad($url);
291: if ($contents === FALSE) {
292: $errmsg = $pcc->geterror();
293: // インスタンスを解放する.
294: $pcc = NULL;
295: return FALSE;
296: }
297: // インスタンスを解放する.
298: $pcc = NULL;
299:
300: // インスタンスを生成する.
301: $psc = new pahooScraping($contents);
302:
303: // 対象テーブルの行を取得
304: // テーブル tbody 内の tr
305: $rows = $psc->queryXPath('//table[contains(@class,"standard")]//tr[td]');
306:
307: $cnt = 0;
308: foreach ($rows as $row) {
309: $cols = $row->getElementsByTagName('td');
310: if ($cols->length < 5) {
311: continue;
312: }
313:
314: // Branch(例:8.3)
315: $branchVersion = trim($cols->item(0)->textContent);
316: // バージョン行だけ残す.
317: if (! preg_match('/^\d+\.\d+$/', $branchVersion)) {
318: continue;
319: }
320:
321: // 日付(yyyy-mm-dd 形式)
322: $initialRelease = normalizeDate($cols->item(1)->textContent);
323: $activeEnd = normalizeDate($cols->item(3)->textContent);
324: $securityEnd = normalizeDate($cols->item(5)->textContent);
325:
326: // 既存の $items を更新
327: foreach ($items as $branch=>$item) {
328: // ブランチバージョンがあればスキップ
329: if ($branchVersion === $branch) break;
330: // ブランチを登録する.
331: $items[$branchVersion]['initialRelease'] = $initialRelease;
332: $items[$branchVersion]['activeEnd'] = $activeEnd;
333: $items[$branchVersion]['securityEnd'] = $securityEnd;
334: }
335: }
336:
337: // インスタンスを解放する.
338: $psc = NULL;
339:
340: return TRUE;
341: }
キャッシュシステム pahooCache を利用し、公式サイトへのアクセスは定数 LIFE_CACHE_DATA の間隔(デフォルトでは1日1回)に制限することで、アクセス負荷をかけないようにしている。
Supported Versions のHTML構造(DOM構造)を解析すると、ブランチバージョンの一覧は、class="standard" の table タグに書かれていることが分かる。そこで、pahooScrapingクラスと、XPath式を利用し、行(trタグ)に分解してスクレイピングしていく。
各行の中の情報(ブランチバージョン、安定版リリース年月日、アクティブサポート年月日、セキュリティサポート年月日)を取得するには、XPath式よりも DOM を使った方が簡便にできるので、そうしている。
ブランチバージョンを取得するには、正規表現を用いている。
安定版リリース年月日、アクティブサポート年月日、セキュリティサポート年月日は、tdタグで囲まれた中を丸ごと取り込んでいるが、年月日表記が英語であるため、後述するユーザー定義関数 normalizeDate を使って、yyyy-mm-dd形式に変換する。
取得した情報は、ブランチバージョンをキーにした連想配列 $items に格納していく。ここで、すでにブランチバージョンが登録済み(XMLデータファイルから読み込まれた)ものについては、配列の内容を更新せずにスキップする。配列の内容を更新したときだけ、グローバル変数 $FlagUpdate を TRUE にする。
lifecyclePHP.php
257: /**
258: * 年月日を表す文字列を yyyy-mm-dd 形式に正規化する.
259: * @param string $str items 年月日を表す文字列
260: * @return int 正規化した文字列/NULL:変換に失敗
261: */
262: function normalizeDate($str) {
263: $str = trim($str);
264: if ($str === '' || stripos($str, 'ago') !== FALSE) {
265: // "3 years ago" などを除去
266: $str = preg_replace('/\d+.*ago/', '', $str);
267: }
268: $time = strtotime($str);
269:
270: return $time ? date('Y-m-d', $time) : NULL;
271: }
解説:各ブランチの最新バージョン情報を取得する
lifecyclePHP.php
343: /**
344: * 公式サイトを参照し,各ブランチの最新バージョン情報を取得する.
345: * @param array $items ライフサイクルを格納する配列
346: * @param string $errmsg エラー・メッセージを格納する(正常なら空文字)
347: * @return bool array(最新バージョン, リリース年月日)/FALSE:取得失敗
348: */
349: function getLatestVersion(&$items ,&$errmsg) {
350: global $HttpHeaders, $FlagUpdate;
351:
352: // 公式サイト
353: $url = 'https://www.php.net/';
354:
355: // インスタンスを生成する.
356: $pcc = new pahooCache(LIFE_CACHE_DATA, DIR_CACHE_DATA, $HttpHeaders);
357:
358: // コンテンツを読み込む
359: $errmsg = '';
360: $contents = $pcc->cLoad($url);
361: if ($contents === FALSE) {
362: $errmsg = $pcc->geterror();
363: // インスタンスを解放する.
364: $pcc = NULL;
365: return FALSE;
366: }
367: // インスタンスを解放する.
368: $pcc = NULL;
369:
370: // インスタンスを生成する.
371: $psc = new pahooScraping($contents);
372:
373: // <a>タグの値に "Released" が含まれているノードを取得する.
374: $nodes = $psc->queryXPath("//a[contains(text(),'PHP') and contains(text(),'Released')]");
375: foreach ($nodes as $node) {
376: // 例: PHP 8.5.4 Released!
377: $text = trim($node->textContent);
378:
379: // id属性(例: php-8.5.4)
380: $id = $node->getAttribute("id");
381:
382: // バージョン情報
383: if (preg_match('/PHP\s+(\d+\.\d+)(\.*\d*)/', $text, $m)) {
384: $branchVersion = $m[1];
385: $latestVersion = $branchVersion . $m[2];
386: }
387: // リリース日
388: if (preg_match('/^(\d{4}-\d{2}-\d{2})/', $id, $d)) {
389: $latestRelease = $d[1];
390: }
391:
392: // 既存の $items を更新
393: foreach ($items as $branch=>&$item) {
394: if ($branchVersion === $branch) {
395: // 最新バージョンが登録済みならスキップする.
396: if (isset($item['latestVersion'])
397: && $item['latestVersion'] >= $latestVersion) break;
398: // 最新バージョンを登録する.
399: $item['latestVersion'] = $latestVersion;
400: $item['latestRelease'] = $latestRelease;
401: $FlagUpdate = TRUE; // データベース更新フラグを立てる
402: }
403: }
404: }
405: unset($item);
406: // インスタンスを解放する.
407: $psc = NULL;
408:
409: return TRUE;
410: }
前述の関数 getLifecycle 同様、キャッシュシステム pahooCache を利用し、公式サイトへのアクセス負荷を低減している。
リリースの見出しは、"PHP" および "Released" を必ず含む a タグであることから、これを XPath式で拾って、正規表現で必要な上方を取り出している。
取得した情報は、ブランチバージョンをキーにした連想配列 $items に格納していく。前述の関数 getLifecycle 同様、最新バージョンが登録済み(XMLデータファイルから読み込まれた)ものについては、配列の内容を更新せずにスキップする。配列の内容を更新したときだけ、グローバル変数 $FlagUpdate を TRUE にする。
解説:PHPのライフサイクル情報をHTMLのtableに変換する
lifecyclePHP.php
412: /**
413: * 日付のステータスを求める
414: * @param string $yyyymmdd 日付 yyyy-mm-dd形式
415: * @return int 0:日付以外,1:ライフ中,2:間もなくライフ終了,3:ライフ終了
416: */
417: function getStatus($yyyymmdd) {
418: //本日Date
419: $today = new DateTime();
420: $dtformat = '/[0-9]{4}\-[0-9]{2}\-[0-9]{2}/';
421:
422: //日付かどうか
423: if (preg_match($dtformat, $yyyymmdd, $arr) == 0) return 0;
424: $yyyymmdd = $arr[0];
425:
426: //日付のステータス
427: $dt = new DateTime($yyyymmdd);
428: $diff = $today->diff($dt)->format('%R%a');
429: if ($diff <= 0) {
430: $ret = 3;
431: } else if ($diff <= LIFE_NEAR) {
432: $ret = 2;
433: } else {
434: $ret = 1;
435: }
436:
437: return $ret;
438: }
lifecyclePHP.php
440: /**
441: * PHPのライフサイクル情報をHTMLのtableに変換する.
442: * @param array $items ライフサイクルを格納する配列
443: * @return string 変換後HTML/FALSE:ファイルが無い、変換に失敗
444: */
445: function phpLifecycle2html($items) {
446: // 日付スタイル
447: $classes = array(
448: '',
449: '',
450: CLASS_NEAREND,
451: CLASS_END
452: );
453:
454: // 現在の年月日
455: $today = new DateTime();
456: $dtformat = '/[0-9]{4}\-[0-9]{2}\-[0-9]{2]/';
457:
458: // 出力用HTML
459: $width1 = (int)(WIDTH * 0.188);
460: $width2 = (int)(WIDTH * 0.125);
461: $html =<<< EOT
462: <table class="lifecyslePHP">
463: <tr>
464: <th style="width:{$width2}px;text-align:center;">バージョン</th>
465: <th style="width:{$width1}px;text-align:center;">安定版<br>リリース</th>
466: <th style="width:{$width2}px;text-align:center;">最新<br>バージョン</th>
467: <th style="width:{$width1}px;text-align:center;">最新リリース日</th>
468: <th style="width:{$width1}px;text-align:center;">アクティブ<br>サポート終了</th>
469: <th style="width:{$width1}px;text-align:center;">セキュリティ<br>サポート終了</th>
470: </tr>
471:
472: EOT;
473: // 各バージョン
474: foreach ($items as $branchVerrsion=>$item) {
475: //日付ステータス
476: $st1 = getStatus((string)$item['initialRelease']);
477: $st2 = getStatus((string)$item['latestRelease']);
478: $st3 = getStatus((string)$item['activeEnd']);
479: $st4 = getStatus((string)$item['securityEnd']);
480: if ($st4 == 0) {
481: if ($st3 == 0) {
482: if ($st2 == 0) {
483: $st4 = $st3 = $st2 = $st1;
484: } else {
485: $st4 = $st3 = $st1 = $st2;
486: }
487: } else {
488: $st1 = $st2 = $st4 = $st3;
489: }
490: } else {
491: if ($st3 == 0) $st3 = $st4;
492: $st1 = $st2 = $st4;
493: }
494:
495: //日付スタイル
496: $ss1 = $classes[$st1];
497: $ss2 = $classes[$st2];
498: $ss3 = $classes[$st3];
499: $ss4 = $classes[$st4];
500:
501: //HTML変換
502: $html .=<<< EOT
503: <tr>
504: <td class="{$ss1}">{$branchVerrsion}</td>
505: <td class="{$ss1}">{$item['initialRelease']}</td>
506: <td class="{$ss2}">{$item['latestVersion']}</td>
507: <td class="{$ss2}">{$item['latestRelease']}</td>
508: <td class="{$ss3}">{$item['activeEnd']}</td>
509: <td class="{$ss4}">{$item['securityEnd']}</td>
510: </tr>
511:
512: EOT;
513: }
514:
515: $html .=<<< EOT
516: </table>
517:
518: EOT;
519: return $html;
520: }
日付のカラムについては、ユーザー関数 getStatus を使って、定数 LIFE_NEAR で指定した期間より小さければ、間もなく終了するという意味で、スタイルを定数 CLASS_NEAREND で指定したクラスにする。現在を過ぎていれば、終了したという意味で、スタイルを定数 CLASS_END で指定したクラスにする(デフォルトではグレーアウト)。
デフォルトのスタイルは次に示すようになっている。URLパラメータで onlyTable を指定したときはスタイルが出力されないので、ユーザーサイト側でこれらのスタイルを用意する必要がある。
lifecyclePHP.php
116: <script>
117: document.addEventListener('DOMContentLoaded', function() {
118: const tables = document.querySelectorAll('table.lifecyslePHP');
119: tables.forEach(function(table) {
120: table.style.borderColor = '#FFFFFF';
121: table.style.fontSize = 'small';
122: table.style.borderCollapse = 'collapse';
123: table.style.padding = '2px';
124: table.style.borderSpacing = '2px';
125:
126: // ヘッダー (th) のスタイル
127: const headers = table.querySelectorAll('th');
128: headers.forEach(function(th) {
129: th.style.backgroundColor = '#FFBB00';
130: th.style.textAlign = 'center';
131: th.style.border = 'solid 1px gray';
132: });
133:
134: // 行の縞模様 (tr)
135: const rows = table.querySelectorAll('tr');
136: rows.forEach(function(row, index) {
137: row.style.backgroundColor = (index % 2 === 0) ? '#FFDD88' : '#FFFFFF';
138: });
139:
140: // セル (td) のスタイル
141: const cells = table.querySelectorAll('td');
142: cells.forEach(function(td) {
143: td.style.padding = '4px';
144: td.style.border = 'solid 1px gray';
145: td.style.textAlign = 'center';
146: });
147: });
148: });
149: </script>
150: <style>
151: table.lifecyslePHP .near-end {
152: background-color: #FFCCCC;
153: }
154: table.lifecyslePHP .end {
155: background-color: #AAAAAA;
156: }
157: </style>
解説:メイン・プログラム
lifecyclePHP.php
592: // メイン・プログラム ======================================================
593:
594: // パラメータの取得
595: $flagTable = isButton('onlyTable');
596: $flagMe = ! isButton('noMyVersion');
597: $items = array();
598: $errmsg = '';
599:
600: // XMLファイルからライフサイクルを読み込む.
601: $res = loadXMLfile(FILE_XML_VERSION, $items);
602: if ($res === FALSE) {
603: $errmsg = FILE_XML_VERSION .' を読み込めません';
604: }
605:
606: // 公式サイトからブランチバージョン情報を取得する.
607: if ($errmsg === '') {
608: $res = getLifecycle($items, $errmsg);
609: if ($res === FALSE) {
610: $errmsg = '公式サイトからライフサイクル情報を取得できません';
611: }
612: }
613:
614: // 公式サイトから最新バージョン情報を取得する.
615: if ($errmsg === '') {
616: $res = getLatestVersion($items, $errmsg);
617: if ($res === FALSE) {
618: $errmsg = '公式サイトから最新バージョン情報を取得できません';
619: }
620: }
621:
622: // XMLファイルにライフサイクルを書き戻す.
623: if ($errmsg === '') {
624: $res = saveXMLfile(FILE_XML_VERSION, $items);
625: if ($res === FALSE) {
626: $errmsg = FILE_XML_VERSION .' に書き込めません';
627: }
628: }
629:
630: // 表示HTMLを作成する.
631: $table = phpLifecycle2html($items);
632: $HtmlBody = makeCommonBody($flagMe, $table, $errmsg);
633:
634: // 画面に表示する.
635: if (! $flagTable) {
636: echo $HtmlHeader;
637: echo $HtmlBody;
638: echo $HtmlFooter;
639: // テーブルのみ表示
640: } else {
641: echo $table;
642: }
643:
644: /*
参考サイト
- Supported Versions:PHP公式

そこで今回は、PHPを使って、公式サイトの Supported Versions や最新パッチバージョンのリリース情報をスクレイピングし、ライフサイクルを一覧に整理して表示するプログラムを作ってみます。公式サイトにアクセス不可を書けないようキャッシュシステムを利用し、また、自分自身のバージョンも表示させるようにする。取得した情報は、ローカル(サーバ)にある XMLデータファイルに保存する。