AB型技術系 主に備忘録

ほぼプログラム関連の備忘録

意外と簡単にphpでRSS2.0とAtomのフィードを生成できた

自前で構築中のサイトの記事作成時にRSS2.0とAtomのフィードを生成する機能を追加してみました。

使ったのはPHP Universal Feed Generatorというライブラリ

github.com

記事データ
実際はデータベース等から取得して連想配列に格納します

<?php

$item1 = array();
$item2 = array();

$item1["title"] = "タイトル2";
$item1["link"] = "http://otaku.kansai-fan.com/content/20170502";
$item1["date"] = strtotime(date('Y-m-d G:i:s', time()));
$item1["author"] = "投稿者2";
$item1["description"] = "投稿内容2";

$item2["title"] = "タイトル1";
$item2["link"] = "http://otaku.kansai-fan.com/content/20170501";
$item2["date"] = strtotime(date('Y-m-d G:i:s', time()));
$item2["author"] = "投稿者1";
$item2["description"] = "投稿内容1";

$list = array();
$list["20170502100000"] = $item1;
$list["20170502090000"] = $item2;

?>

RSS2.0

<?php

// ライブラリの読み込み
use \FeedWriter\RSS2;

// チャンネル情報の登録
$feed = new RSS2;
$feed->setTitle("とあるオタクの備忘録");
$feed->setLink("http://otaku.kansai-fan.com/");
$feed->setDate(new DateTime());
$feed->setDescription("プレイ中のゲームのプレイ日記等");
$feed->setImage("http://otaku.kansai-fan.com/" , "とあるオタクの備忘録", "http://otaku.kansai-fan.com/image/logo.png" );
$feed->setChannelElement( "language" , "ja-JP" );
$feed->setChannelElement( "pubDate" , date(\DATE_RSS , time()));
$feed->setChannelElement( "category" , "Blog" );

// 記事の分だけ、フィードのアイテムを追加する
foreach ($list as $key => $value){
    $data = $value;
    $item = $feed->createNewItem();
    $item->setTitle($data["title"]);
    $item->setLink($data["link"]);
    $item->setDate($data["date"]);
    $item->setAuthor($data["author"]);
    $item->setDescription($data["description"]);
    $item->setId($data["link"], true) ;
    $feed->addItem($item);
}

// xml形式の文字列を出力
$xml = $feed->generateFeed();

// ファイルの保存場所を設定
$file = "/home/aaa/bbb/rss2.xml";

// ファイルの保存を実行
@file_put_contents($file, $xml);

?>

Atom

<?php

// ライブラリの読み込み
use \FeedWriter\ATOM;

// チャンネル情報を登録
$feed = new ATOM;
$feed->setTitle("とあるオタクの備忘録");
$feed->setLink("http://otaku.kansai-fan.com/");
$feed->setDate(new DateTime());

// 記事の分だけ、フィードのアイテムを追加する
foreach ($list as $key => $value){
    $data = $value;
    $item = $feed->createNewItem();
    $item->setTitle($data["title"]);
    $item->setLink($data["link"]);
    $item->setDate($data["date"]);
    $item->setAuthor($data["author"]);
    $item->setDescription($data["description"]);
    $feed->addItem($item);
}

// xml形式の文字列を出力
$xml = $feed->generateFeed();

// ファイルの保存場所を設定
$file = "/home/aaa/bbb/atom.xml";

// ファイルの保存を実行
@file_put_contents($file, $xml) ;

?>

RSS2.0とAtomで設定する項目が少し異なるくらいで処理はほぼ変わりません。
RSS1.0は今回作成してませんが、ほぼ同じ流れで簡単に作成できそうです。

気を付けないといけないのは日付関連の項目
値が不正だとgenerateFeedメソッドでエラーが発生します

実際に出力したものがこちら

RSS2.0

ATOM


PHPのfile関数でcsvを読み込んだ時に改行コードが含まれていた

下記のようなcsvファイルを読み込んでweb上に表示させる画面を作りました

20170410,タイトル1
20170411,タイトル2
20170412,タイトル3
20170413,タイトル4
20170414,タイトル5

まずfile関数で1行ずつ配列に格納しました

<?php

$array = file("/history.csv");

// $array[0]は"20170410,タイトル1"
// $array[1]は"20170411,タイトル2"

?>

後はこの配列をループさせて画面に表示するだけ

<?php

$array = file("/history.csv");

$content .= "<ul>";

for($i = 0;$i < count($array);$i++){
    $str = split(",", $array[$i]);
    $dt = date('Y&#47;m&#47;d',  strtotime($str[0]));
    $content .= "<li>".$dt."&nbsp;".$str[1]."</li>";	
}

$content .= "</ul>";

echo $content;

?>

画面で表示した結果

2017/04/10 タイトル1
2017/04/11 タイトル2
2017/04/12 タイトル3
2017/04/13 タイトル4
2017/04/14 タイトル5

ここまではよかったのですが、この配列にデータを追加してその内容でcsvファイルを更新ようとした時問題が発生しました。

<?php

$array = file("/history.csv");

array_push($array, "20170415,タイトル6");

$file = fopen("/history.csv", "w");

for($i = 0;$i < count($array);$i++){
    @fwrite($file, $array[$i]."\r\n");
}

fclose($file);

?>

出力したcsvファイル

2017/04/10 タイトル1

2017/04/11 タイトル2

2017/04/12 タイトル3

2017/04/13 タイトル4

2017/04/14 タイトル5

2017/04/15 タイトル6

何故か改行が増えてる・・・
改行コードは1個だけしか付加してないのに

原因はfile関数でcsvファイルを読み込む時に改行コードまで読み込んでいたから
読み込んだ時に下記のようになっていたということです

<?php

$array = file("/history.csv");

// $array[0]は"20170410,タイトル1\r\r"
// $array[1]は"20170411,タイトル2\r\r"

?>

file関数オプションにFILE_IGNORE_NEW_LINESを追加することで改行コードは付加されずに読み込まれます

<?php

$array = file("/history.csv", FILE_IGNORE_NEW_LINES);

// $array[0]は"20170410,タイトル1"
// $array[1]は"20170411,タイトル2"

?>

修正後に出力したcsvファイル

2017/04/10 タイトル1
2017/04/11 タイトル2
2017/04/12 タイトル3
2017/04/13 タイトル4
2017/04/14 タイトル5
2017/04/15 タイトル6


max-widthでスマホサイトの画像サイズを自動調整

PCサイトの画像をスマホで表示すると横幅が大きすぎてはみでてしまいました。

f:id:freelancer13:20170407025530j:plain

最初からスマホでも見れるように画像の大きさを調整しておけばよかったです・・・

既に多くの画像でページを作成しているのでリサイズするのは面倒です

そこでスマホサイト用のCSSに以下の記述を追加しました

#contents img {
	max-width: 100%;
	height: auto;
}

これで#contents内の画像の横幅をスマホの横幅におさまるようにサイズを調整してくれます
高さも自動で調整してくれます

f:id:freelancer13:20170407025537j:plain

いいかんじにおさまったのではないでしょうか


正規表現で特定の文字列を含まない行、含む行を抽出

先日とある会社のシステムのリリースがありました。

かなり大規模なシステムなので予想通りリリース後
いろいろと問題が発生し、ログ解析する日々です。

ログのフォーマット

2017/03/22 12:00:03 INFO START testAction1.do
2017/03/22 12:00:06 INFO END testAction1.do
2017/03/22 12:00:08 INFO START xxxAction2.do
2017/03/22 12:00:10 FATAL java.lang.NullPointerException
   ・
   ・
   ・
javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
   ・
   ・
   ・
2017/03/22 12:03:08 INFO START xxxAction3.do
2017/03/22 12:00:10 WARN xxx.ValidateException
   ・
   ・
   ・
javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

Javaで構築されたシステムで、予期しない例外が発生するとログに内容が出力されます。

ログから"Exception"を含む行を秀丸エディタgrepして目的の行を検索するのですが、使用しているフレームワークが出力する例外(ValidateException)が大量に出力されて数千件ヒットしてしまいます。

そこで正規表現を使って"Exception"を含む行かつ
"ValidateException"を含まない行を抽出できるようにしました

^(?!.*ValidateException).*(?=Exception).*$

^(?!.*ValidateException).*$で"ValidateException"を含まない行がマッチします。

そこに(?=Exception)を合わせることによって
"ValidateException"を含まない行、
かつ"Exception"を含む行がマッチします。

これで検索結果がかなり絞られましたが、他にもチェック不要な例外(TimeOutException)が
あることが判明

この例外も除外したいので"ValidateException"と"TimeOutException"を含まない行に条件を変更しました

^(?!.*(ValidateException|TimeOutException)).*(?=Exception).*$

除外したい文字列を()で囲んで|で区切るだけです。

これでログ解析の効率がかなりアップしましたよ。


HTTP_USER_AGENTを利用してcssを切り替える

PC用のcssスマホ用のcssを用意してデバイスによって読み込むcssを切り替えたいと思います。

最初に考えたのがlink要素の「media」属性を利用すること
メディアクエリというやつです

PC用

<link rel="stylesheet" type="text/css" media="screen and 
(min-width:901px)" href="css/style_pc.css"/>

画面サイズが901px以上の場合、style_pc.cssを読み込みます

スマホ

<link rel="stylesheet" type="text/css" media="screen and
 (max-width:900px)" href="css/style_smp.css"/>

画面サイズが900px以下の場合、style_smp.cssを読み込みます

しかし、この方法だと画面サイズが判断されるのでPCで画面サイズを小さくした場合style_smp.cssを読み込んでしまいます

最近はスマホでも画面サイズが大きいのでスマホがstyle_pc.cssを読み込む可能性もあります

そこでユーザーエージェントを利用してcssを切り替えることにしました

<?php
$ua=$_SERVER['HTTP_USER_AGENT'];
$isSmp = ((strpos($ua,'iPhone')!==false)||
  (strpos($ua,'iPad')!==false)||
  (strpos($ua,'Android')!==false));
?>

$_SERVER['HTTP_USER_AGENT']でユーザが利用しているOS情報等が取得できます

strposでユーザーエージェントにiPhoneiPadAndroidという文字列が存在するかチェックし
存在すればスマホと判断します

<?php if ($isSmp) {?>
<link rel="stylesheet" type="text/css" href="/css/style_smp.css" />
<?php } else {?>
<link rel="stylesheet" type="text/css" href="/css/style_pc.css" />
<?php }?>

$isSmpの値でスマホかどうか判別し読み込むcssを切り替えます

この状態でmax-device-withやmax-with等使ってスマホ用のレイアウトを
スマホタブレットに切り替えたりPC用のレイアウトを解像度が低いPCと高いPCに
切り替えたりするのもいいかなと思ってます