PHPのRFCの更新情報を配信するRSS (Atom)フィードを作りました。
https://php-rfc-feed.muno92.dev/feed.xml
(フィード生成側のソースコード)
好きなRSSリーダーで購読すれば、RFCのステータス更新がフィードとして配信されるようになります。

背景
PHPの言語機能はRFCとして提案され、議論・投票を経て投票数の3分の2の賛成を得た提案が採択されます。
このRFCをウォッチするツールとして、かつてはPHP RFC BotというX (Twitter)の非公式Botアカウントがありました。
しかし、PHP RFC BotはX (Twitter)のAPI体系が変わった辺りから更新が止まっています。
↓最後の投稿
#PHP RFC moved to In Voting Phase: Make unserialize() emit a warning for trailing data https://t.co/8ioueZwLAH
— PHP RFC Bot (@PHPRFCBot) 2023年4月12日
RFCの投票状況をグラフで分かりやすくまとめてくれているPHP RFC Watch (便利!)もAtom Feedを配信しているものの、配信対象は投票状況のため、新しいRFCの提出などは検知できません。
「PHP RFC Bot、更新止まっちゃったなあ・・・」と思ったまましばらく過ごしていたのですが、この間Pipe operator v3のRFCが出たことを知って「もっと早く気づきたかった・・・!」と思い、無ければ作ればいいか!とこのフィードを作りました。
RSSフィードにしたのは、一度はRSSフィードを作ってみたくなったのと、RSSフィードなら購読する側が好きなリーダーを選べるからです。
仕組み
フィードを生成・公開している仕組みはシンプルです。
- 約1時間おきに https://wiki.php.net/rfc をスクレイピング
- RFCの新規提出やステータス変更があればDB (SQLite)に保存
- DBから直近のRFC更新を抽出しAtom Feed (XML)を生成
- ホスティングサービス (Cloudflare Pages)に生成したXMLファイルをポン置き
個人開発をする時、DBに何を使うかは悩みどころですが、今回は公開情報しか保存しないのでSQLiteのdbファイルをそのままリポジトリにコミットして保存先として使うことにしました。
SQLite、ちゃんと使うのはほぼ初めてだったのですが、特に困ること無く使えました。
また、リポジトリにそのままコミットしたことで特定時点に巻き戻したい時にGitのコミットを巻き戻すだけで済むようになったのも思わぬ便利ポイントでした。
実現したいのは「必要最小限のPHP RFC Bot代替」なので、フィードにはRFCのステータス・タイトルだけを載せています。
それ以上の情報はリンクからRFCのページに移動して読む想定です。
まあ、RFCを追っている人ならDraftやUnder Discussionのエントリが流れてきたら新しいRFCが提出されたことは分かると思うので・・・
実装
スクレイピングやフィードの生成を行うプログラムはSymfonyで実装しています。
PHPに関するツールなのでエコシステムを考えるとPHPで実装しておいた方が良いかなと思ったのと、チュートリアル (The Fast Track)をやったきりのSymfonyを使ってみたいな、と思ったので。
最初は「楽しく書く」を目的に自分で実装しようとしていたのですが、時間を取れなかったりしてモチベが他の事に移りかけたこともあり、途中からはとりあえず完成させる事をゴールとしてClaude Codeに外注していました。
(個人的に、趣味開発だとモチベを維持するのが難しくなる時もあるので、そういった面でもAI便利だなと思ったり)
Claude Codeが書いてきたコードや設定をググって「Symfony Consoleってプログレスバー出力できるんだ」「DoctrineだとEntityを下にマイグレーション自動生成できるのか。宣言的に書けて良いな (これはFast Trackに書いてあったのですっかり抜け落ちてた・・・)」と、色々と教えて貰いながら作っていました。
実行環境
プログラムの実行環境はGitHub Actionsを使い、以下のようなワークフローを組んでいます。
- masterブランチにプロダクションコードをPushするたびにDockerイメージをビルド
- ghcr.ioにPush
- GitHub Actionsのスケジュール実行でコンテナを動かし、スクレイピングとフィードの生成を実行
コンテナを使わずともhosted-runnerならそのまま動くような気がしなくもないですが、OSのバージョンが変わった時などに挙動が変わったりしたら嫌なのでコンテナに閉じ込めています。
閉じ込めたのは良いのですが、少し困ったことも。
必要最小限の構成でDockerイメージをビルドするようにしたため、gitコマンドやnpmコマンドが無く、更新されたdbファイルのコミットやCloudflare Pagesへのデプロイが出来ない・・・
そのため、それらは別のジョブに分けて実行する事にしました。
また、GitHub Actionsのスケジュール実行は便利なのでよく使うのですが、注意事項もあるので記載しておきます。
実行開始時間が多少遅れたり、場合によっては遅れるどころか実行されないこともある
The schedule event can be delayed during periods of high loads of GitHub Actions workflow runs. High load times include the start of every hour. If the load is sufficiently high enough, some queued jobs may be dropped. To decrease the chance of delay, schedule your workflow to run at a different time of the hour.
https://docs.github.com/en/actions/reference/events-that-trigger-workflows
- 毎時0分に実行を設定して、30分位に実行されたこともありました。なので実行開始時間の正確性を求める場合には向いていないです
パブリックリポジトリの場合、60日間何もアクティビティが無いと実行が停止されます
To prevent unnecessary workflow runs, scheduled workflows may be disabled automatically. When a public repository is forked, scheduled workflows are disabled by default. In a public repository, scheduled workflows are automatically disabled when no repository activity has occurred in 60 days.
スクレイピングする際の考慮
スクレイピングを行うプログラムを作るに当たって、3点考慮事項がありました。
対象サイトがスクレイピング (など) を禁止していないか
Webサイトによってはスクレイピングなど、ボットによるアクセスを禁止している場合があります。
そのため、robots.txtを確認する必要があります。
※robots.txtについての説明はCloudflareの記事が分かりやすいです
www.php.netにはrobots.txtがあるものの、wiki.php.netには無し。
そのため、 wiki.php.net/rfc は禁止対象には含まれていなさそうです。
対象サイトへの負荷
対象サイトがスクレイピングを禁止していなかったとしても、何も考えずに実装すると短時間に大量のリクエストを投げて対象サイトに負荷をかけてしまうおそれがあります。
今回のプログラムの場合、 https://wiki.php.net/rfc から各RFC (現時点で800以上) へのリンクを抽出してそれぞれのページを取得しています。
初期実装時点ではは全てのRFCページを直列に取得し、8分ほど。
ざっくり1分で約100件、1秒1〜2リクエスト位。
これ位なら問題ないかなと思っています。
なので、今後プログラムの実行時間を短縮していくとしたら並行・並列化など一定時間当たりのリクエストを増やすのではなく、不要なリクエストを減らしていく方向で対応しようと考えています。
(現時点でもImplementedになったRFCはスキップするようにしています)
自動テストでパースするHTMLをどこから取得するか
スクレイピングを行うプログラムのテストコードを書く際、パース対象のHTMLを指定する方法として
- Web上のHTMLを取得してきて指定
- リポジトリ内に設置したHTMLファイルを指定
の2パターン考えられます。
今回のリポジトリでは当初1番の方法を選び、wiki.php.net からダウンロードしてきたHTMLファイルをリポジトリ内に置いてテストデータとして使っていました。
これは、Web上から取得してきた時にHTMLの内容が変わってテスト結果が影響を受けてしまったり、テストの実行時間が長くなったりしてしまう事を嫌っての判断です。
(前者の理由が強め)
ただ、wiki.php.netからダウンロードしてきたHTMLをリポジトリに保存した場合、著作権を侵害してないのだろうか?と気になり、2番の形に変えています。
https://wiki.php.net/rfc のフッターにリンクされているCopyrightページにはWebsite・PHP Manual2つのCopyrightが記載されており、Websiteについては以下のようになっています。
The code, text, PHP logo, and graphical elements on this website and the mirror websites (the "Site") are Copyright © 2001-2025 the PHP Group. All rights reserved.
Except as otherwise indicated elsewhere on this Site, you are free to view, download and print the documents and information available on this Site subject to the following conditions:
- You may not remove any copyright or other proprietary notices contained in the documents and information on this Site.
- The rights granted to you constitute a license and not a transfer of title.
- The rights specified above to view, download and print the documents and information available on this Site are not applicable to the graphical elements, design or layout of this Site. These elements of the Site are protected by trade dress and other laws and may not be copied or imitated in whole or in part.
For more information on the PHP Group and the PHP project, please see the PHP homepage.
「自由にCopyして良い」といった記述は見当たりません。
次に、「引用」のような例外規定が無いか調べてみたところ、平成30年にデジタルデータの活用がしやすいように著作権法が改正されていることを知りました。
読んでみたものの、「HTMLをダウンロードしてリポジトリに置き続けるのはどうなんだ?頒布と見なせるのでは・・・?」「仮にプライベートリポジトリにした所で、複製がNGならどっちにしろ駄目だな」と自分の中で確信が持てず・・・
そのため、テストコードはWeb上から取得してきたHTMLを解析させる形にしました。
多少テストの厳密性が下がっていますが、まあ、モヤモヤしているよりは良いので。
その副次的な効果として、wiki.php.netのページ構成が変わった場合に実行時ではなくテスト時に検知できるようになったな、と思いました。
(手元に持ったHTMLと最新のHTML、両方でテストしておくのが良いんでしょうね)
RSSリーダーによる挙動の違い
今回、以下4つのアプリ・サービスで動作確認を行ったのですが、細かい差異が2つ見つかりました。
- Feedly
- Inoreader
- SlackのRSSアプリ
- DiscordのMonitoRSS
まず、Inoreaderでは初回読み込み時にRFCの並び順が意図しないものになりました。
Atom形式のフィードでは各エントリが公開日時 (published)、更新日時 (updated)を持ちます。
ただ、Inoreaderはそのどちらも並び順に使用せず、受信日時でエントリを並べるようです。

そのため、初回の読み込み時だけは並び順に違和感を持たれるかもしれませんが、その後の更新については更新順で表示されていくと思います。
そして、Atomフィードだと各エントリにlinkを設定できるのですが、MonitoRSSではタイトルにリンクが設定されませんでした。
そのため、本文としてもURLを送るようにしています。

他にも違う・不都合な動きをするRSSリーダーがありましたらGitHubのリポジトリからご報告頂けると大変助かります 🙏
おわり
以上、つらつらと書いていたら脈絡のない文章になってしまった気がしますが、お読み頂きありがとうございました。
と書いていて、RFCのコンテンツ・HTML構造もページによって様々だったことを思い出しました。
そこまで書いていると話が広がりすぎるので詳しくは書きませんが・・・。気になった方は探してみてください。
1つだけ挙げると、どんなステータスが登録されているのかDBをSELECTしてみると面白いですよ。
(最初の方に書いたようにdbファイルはリポジトリにコミットしているので簡単に見れます!)
RSSフィード、作ってみると意外と簡単で、また、リーダーによって挙動が違うのが面白かったです。
そして、今回のプログラムがある程度形になってから今までのテスト運用期間だけでも色々とRFCの更新を知れたので、自分としては十分満足です。
みなさんも、もし良ければ使って貰えると嬉しいです!