PHPでAtom FeedのXMLを作成する

## やってみたこと
先日XMLを作成しなければならないことがあり、開発。
Atom Feedの形式にする必要があり、ベタで全て書いて行くのもと思った中、簡易的に作成できるライブラリがGitHubで公開されていたので使用してみましたので、メモとして残します。

## 導入ライブラリ
FeedWriter

GitHubにて公開されているライブラリで、Atom以外にもRSS1.0、RSS2.0にも対応しています。
基本的にはソースDLしてきて、DLしたソースをrequireすることで使用できるようになります。

参考：FeedWriter(https://github.com/mibe/FeedWriter)

## サンプルコード

```
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Atom Feed 作成してみる</title>
  <link href="https://hogehoge.net/page1" rel="alternate" type="text/html"/>
  <link href="https://hogehoge.net/page1/xml/atom.xml" rel="self" type="application/atom+xml"/>
  <subtitle>Atom Feed 作成してみるためのメモ書きを残すものです。</subtitle>
  <id>urn:uuid:1666c104-1f34-503a-94af-59b1bf7e29ec</id>
  <entry>
    <category>サンプル</category>
    <title>サンプルコード作成</title>
    <link>https://hogehoge.net/page2</link>
    <txt>ライブラリをDLし、サンプルコードを作成してみる。うまくできるかな？</txt>
    <published>2018-04-04T14:19:00+09:00</published>
    <updated>2018-04-06T00:28:22+09:00</updated>
    <thumbnail>https://hogehoge.net/page1/sample.jpg</thumbnail>
    <author>
      <name>mura-no<name/>
    </author>
    <content type="html">https://hogehoge.net/page2</content>
    <id>urn:uuid:2a21053e-da98-3142-1e80-69cbf9f1a7e5</id>
  </entry>
</feed>
```
phpのDOMDocumentを使用して、XMLも作成できますが、その際に<feed>タグ、<link>タグなどをベタ書きすることはしたくないですよね。
また日付の形式を変更しなければならないとか、色々と汎用的に使えなくなりそうだったので、どうしようと考えてた時にみつけたのが「FeedWriter」でした。
ではデモをしてみます。


## 作成してみる
### インスタンス作成

```
        // ライブラリ読み込み
        require_once APPLICATION_PATH . '/FeedWriter/Item.php';
        require_once APPLICATION_PATH . '/FeedWriter/Feed.php';
        require_once APPLICATION_PATH . '/FeedWriter/ATOM.php';

        // デフォルトのタイムゾーンをセット
        // date_default_timezone_set( "Asia/Tokyo" ) ;

        $feed = new FeedWriter\ATOM();

```

ライブラリを読み込み、インスタンスを作成する。
今回タイムゾーンはここでは書いておりません。
設定したい方は「デフォルトのタイムゾーンをセット」のコメントアウト部分を外すことで可能です。

## チャンネル（サイト）情報の登録

```
        $feed = new FeedWriter\ATOM();
        $feed->setTitle('Atom Feed 作成してみる');
        $feed->setAtomLink('https://hogehoge.net/page1', 'alternate', 'text/html');
        $feed->setAtomLink('https://hogehoge.net', 'self', 'application/atom+xml');

        // チャンネル情報のサブタイトルを追加
        $feed->setChannelElement('subtitle', 'Atom Feed 作成してみるためのメモ書きを残すものです。');

```
setChannelElementメソッドが提供されているため、チャンネル情報内に追加したいタグがある場合、第1引数にタグ名称。
第2引数に値を入れることでセットできます。


## アイテム情報初期化+登録

```
// 作成したいアイテム分ループする
foreach ($rows as $k => $v) {

    // アイテム情報初期化
    $item = $feed->createNewItem();

    // アイテム情報セット
    // タイトル、更新日時セット
    $item->setTitle($v['caption']);
    $item->setDate($v['modified']);

    // カスタム情報セット
    $item->addElement('category', $v['category_name']);
    $item->addElement('link', $v['url']);
    $item->addElement('txt', mb_strimwidth(str_replace("\r\n", '', strip_tags($v['description'])), 0, 90, '...'));
    $item->addElement('thumbnail', 'https://hogehoge.net' . $v['img_path']);

    // 更新日とは別にpublishedというタグを追加
    $item->setDate($v['opened'], 'published');

    // 作成者情報セット
    $item->setAuthor($v['user_name']);
    $item->setContent($v['url']);

    // アイテム追加
    $feed->addItem($item);
}
```
基本的なアイテムに関してはsetXxxxメソッドが用意されています。
createNewItem()でアイテム初期化をすることで\<entry>が作成されます。
カスタマイズで必要となるアイテムはaddElementメソッドが提供されていますので、第1引数にタグ名称、第2引数に値を入れることで追加が可能。

今回はアイテム数分、\<entry>を作成する必要があったため、ループでアイテム数分の\<entry>を作成する処理を書いています。

＜日付について＞
setDate()することで、\<updated>が作成されます。このメソッド中では「date(\DATE_ATOM, \$date)」と日付を渡すことでAtom形式にそった日付を返却してくれます。
今回、\<updated>とは別に\<published>という日付が必要だったのですが、上手く出力できなかったため、setDate()に第2引数（\$key=null）を追加し、\<updated>以外でもAtom形式の日付を取得できるようにしました（本当は提供されてそうですが…）


修正後のItem.php

```
    public function setDate($date, $key = null)
    {
        if (!is_numeric($date)) {
            if ($date instanceof DateTime)
                $date = $date->getTimestamp();
            else {
                $date = strtotime($date);

                if ($date === FALSE)
                    throw new \InvalidArgumentException('The given date string was not parseable.');
            }
        } elseif ($date < 0)
            throw new \InvalidArgumentException('The given date is not an UNIX timestamp.');

        if ($this->version == Feed::ATOM) {
            $tag    = empty($key) ? 'updated' : $key;
            $value  = date(\DATE_ATOM, $date);
        } elseif ($this->version == Feed::RSS2) {
            $tag    = empty($key) ? 'pubDate' : $key;
            $value  = date(\DATE_RSS, $date);
        } else {
            $tag    = empty($key) ? 'dc:date' : $key;
            $value  = date("Y-m-d", $date);
        }

        return $this->addElement($tag, $value);
    }
```

呼び出し元

```
    // 更新日とは別にpublishedというタグを追加
    $item->setDate($v['opened'], 'published');
```



## フィード生成＋ファイル出力

```
    // フィード生成
    $xml = $feed->generateFeed();

    // ファイル出力（保存）
    $filePath = '/var/www/xml/feed/atom.xml';
    file_put_contents($filePath, $xml);

```


## 完成
このような形で上記で書かせてもらいましたサンプルコードができました！
今回は記載していませんが、バッチ処理で決まった時間にXMLを作成しており、引数でファイル名や、出力するアイテムの件数、出力したいカテゴリの内容などある程度汎用的なものにできたと思います。

感想としては***「予想以上に簡単にできた！」***です。

RSS1.0、RSS2.0も同様だと思いますので、XML作成で困っている方がいれば一度試していただくと良いかもしれません。