PHP で DOM をいじくって href や src の内容を書き換えてみる
2017年3月17日 14:14
しばらくブログをまーったく書いていなかったので、久しぶりにちょいと技術的なメモを。
今回は、とある HTML ファイル(HTMLのパーシャル)をメインの PHP に取り込んで表示させるっていう必要に迫られたんですが、その HTML の中身は直接編集不可…という罰ゲームっぽいものになっておりますw
で、直接編集不可な上に、a タグの href やら img タグの src なんかに記載されている相対パスにベース URL をくっつけるという…何やら魔術的なことを行うと・・・w
js でちょちょっとやろうかとも思ったんですが、パーシャルなので PHP 側で取り込む時にどーにかしたいなと…色々調べた結果、ありました!「PHP の DOM 拡張」。
何はともあれ…書いてみる。
// DOMDocument クラスのインスタンス作成 $dom = new DOMDocument(); // 何はともあれ取り込みたいファイルの内容を取得 $doc = mb_convert_encoding(file_get_contents('test.html'), 'UTF-8', 'sjis-win, sjis'); // 中身の HTML をパース @$dom->loadHTML($doc); // a タグを全て取得 $anchors = $dom->getElementsByTagName('a'); // 取得したタグの href 属性値を書き換える foreach ($anchors as $anchor) { $value = $anchor->getAttribute('href'); if (preg_match('/^https?/', $value)) continue; $anchor->setAttribute('href', 'http://shape-design.jp/' . $value); } // HTML はパーシャルで欲しいのでいらないもの削除 $html = preg_replace('/^<!DOCTYPE.+?>/', '', str_replace( array('<html>', '</html>', '<body>', '</body>'), array('', '', '', ''), $dom->saveHTML() ) ); // 表示(ホントはこれを PHP 側に取り込む) echo $html;
こいつは便利じゃないかーっ!
ちなみに、文字化けしちゃう場合は以下のようにしてエンティティ化すればよいとか…。
$doc = mb_convert_encoding(file_get_contents('test.html'), 'HTML-ENTITIES', 'sjis-win, sjis');
そんなわけで、いろいろなタグやら属性値に対応できるように関数化してできあがったのがこちらっ!
※細かいこと言えば、ページ内リンクとかその他色々どーするんだよって話ですが…。
※ちなみに、この記事向けに書き直したので、このままで動くかは不明です(汗
$base_url = 'http://shape-design.jp/'; echo convert_paths(); /** * 全てのタグ属性のファイルパスを絶対 URL に変換 * * @return string */ function convert_paths() { $dom = new DOMDocument(); $dom->preserveWhiteSpace = false; $dom->formatOutput = true; @$dom->loadHTML(mb_convert_encoding( file_get_contents($base_url . 'test.html'), 'HTML-ENTITIES', 'sjis-win, sjis' )); $tag_anchors = $dom->getElementsByTagName('a'); convert_path($tag_anchors, 'href'); $tag_images = $dom->getElementsByTagName('img'); convert_path($tag_images, 'src'); $tag_forms = $dom->getElementsByTagName('form'); convert_path($tag_forms, 'action'); // パーシャルな HTML のみに整形 $html = preg_replace('/^<!DOCTYPE.+?>/', '', str_replace( array('<html>', '</html>', '<body>', '</body>'), array('', '', '', ''), $dom->saveHTML() ) ); return $html; } /** * タグ属性のファイルパスを絶対 URL に変換 * * @param object $nodes * @param string $attribute * @return object */ function convert_path($nodes, $attribute) { foreach ($nodes as $node) { $value = $node->getAttribute($attribute); if (preg_match('/^https?/', $value)) continue; $node->setAttribute($attribute, $base_url . $value); } return $nodes; }
ちなみに、サーバーにインストールされている PHP に DOM 拡張モジュールが必要なので、使用する際は phpinfo() なんかで要確認ってとこでしょうか。
そんなわけで、今回はここまで!ではまたっ!←いつになるやら(苦笑
※このスクリプトにおいて、動作の検証は行っていませんので、責任は一切負いません。
※参考 URL
PHP: DOMDocument - Manual
絶対URLから相対URLに変換する処理(PHP)|teratail
PHP5のDOM拡張モジュールでHTMLをパースする | Firegoby
【PHP】DOMDocument::loadHTML()で日本語が化けるときの対処法と残った謎 - Fatal Error: Unexpected BLOG
preg_match でバックスラッシュをマッチング
2012年2月4日 14:30
こんにちは。
久しぶりにPHP関数でハマってあたふたしてた掛尾です(汗
先日、とあるところでバックスラッシュをマッチングさせようと preg_match を使用した時のこと…。
$subject = 'test\test.test'; preg_match( '/([^\\]*)\\([^\\]*)$/', $subject, $matches ); var_dump( $matches );
こんな感じのコードを書いたんですが…。
期待してた結果は
array(3) { [0]=> string(14) "test\test.test" [1]=> string(4) "test" [2]=> string(9) "test.test" }
これだったんですが、なんと・・・エラーでたじゃないですかっ!
Warning: preg_match() [function.preg-match]: Compilation failed: missing terminating ] for character class at offset 16
結果は『NULL』。
何故なんだろう…と思って検索、検索・・・。 ありました。
注意: シングルクォートあるいはダブルクォートで囲まれた PHP の 文字列 の中では、バックスラッシュは特別な意味を表します。 そのため、正規表現 \\ を使用して \ とマッチさせたい場合は PHP のコード内では "\\\\" あるいは '\\\\' と記述する必要があります。
なるほど・・・。
正規表現の『\\』を文字列としての『\\』にマッチさせる的な考え方なんだろうか…。
なんか考え出すと頭がパニックになりそうだったので、マニュアル通りにしてみました。
$subject = 'test\test.test'; preg_match( '/([^\\\\]*)\\\\([^\\\\]*)$/', $subject, $matches ); var_dump( $matches );
これで期待通りの結果が得られました。
ちなみに、正規表現パターン内のバックスラッシュを、全部3つずつにしても正しい結果が得られたんですが・・・何故なんでしょうか・・・。
なんか不思議な迷路に迷い込みそうなので、またの機会に考えることにしましょう(苦笑
Smarty::fetch() 隠れ第4引数?
2010年2月10日 23:54
こんばんは。
そろそろ花粉の季節なんでしょうか…。
軽い鼻炎持ちなので今日は一日中くしゃみ・鼻水が止まらなかった掛尾です。
さてさて、大層なタイトルを付けてみました。
おそらく内部的にしか使ってないってことなんでしょうけど、マニュアルには載ってなかったです。
まぁ知らなきゃ知らないで困りはしないと思うんですが、fetchメソッドを使いつつ、どうしてもdebuggingも有効にしたいと思っていろいろ調べてみたら、どうやらfetchメソッドの第4引数にtrueを与えてやると使えるようです。
デフォルトではfalseになっているのですが、これをtrueにできればdebuggingが有効になるみたいです。
前にもどこかで書いたとおり、displayメソッドはfetchメソッドをラップしているので、displayメソッドを確認すると、第4引数がやはりtrueになっていました。
今のところは問題ないのですが、なにか不具合が起こるようであればまた追記しようと思います。(コンパイルやキャッシュの時に問題があるんでしょうか…)
ちなみにdisplayメソッドには第4引数はありません。
まぁ普通にdebuggingの設定が適用されるから、当たり前と言えば当たり前ですね(汗
うーん・・・いろいろ奥が深いですね。
PHPの参照渡し(雑言)
2010年1月9日 11:24
PHPを使い始めて随分となりますが、ものすごい勘違いを先ほど発見してしまった・・・(汗
PHP5での話なんですが…
悲しいかなずっと使う機会がなくPHP4でばかり書いてたので(多分いいわけ)
たとえばですが…
よく説明に使われてるもので次のようなのがあります。
$a = 5; $b = $a; $a = 10; echo $b;
これ、出力は当然「5」になるんですが、何気にPHP5では「10」になると思ってました↓↓
実際にこんな使い方はしないし、書く時はちゃんと「=&」で参照を明示的に書いてるので実務では問題ないのですが…。
で、発見というかちゃんと認識したってことで…
PHP5でオブジェクトを変数に代入する場合は、自動的に参照渡しになる!
そういうわけでした。
それだけなのですが、、、新年早々に頭をかち割られるくらい自分に驚愕したので記事にしてみました(大げさ・笑)
PHPとSmartyでRSSを自動生成
2008年10月15日 19:37
こんばんは。
最近、ActionScript2.0&3.0の勉強に必死な掛尾です。
冒頭にActionScriptとか書いておきながら、今回はPHPとSmartyでRSSを自動生成させる方法について書いてみようと思います。
何はともあれ、RSSの元になるデータがないと話にならないので、例によってデータを取得してきた所から始めます。
RSSの生成には、「サイト(ブログ)タイトル」「サイト(ブログ)URL」「サイト(ブログ)概要」「管理者メールアドレス」「記事タイトル」「記事URL」「記事概要」「登録日時」程度の情報を用意します。
なくても大丈夫なものも含まれていますが、今回はこの8つの情報からRSS2.0用のファイルを作ります。
まず、PHPのスクリプトを以下のような感じで書きます。
変数には、DBもしくはデータファイルから取得したデータが10件程度(あまり多すぎるのもどうかと思いますし・汗)入っているものとします。
/* $rss_master配列に含まれる情報(例:$rss_master['site_title']) site_title : サイトタイトル; site_url : サイトURL; site_description: サイト概要; admin_mail_addr : 管理者メールアドレス; $rss_data配列に含まれる情報(例:$rss_data[0]['topic_title']) topic_title : 記事タイトル; topic_url : 記事URL; topic_description: 記事概要; post_datetime : 登録日時; $smarty: Smartyインスタンス */ // 取得したデータをSmarty変数へ割り当てる $smarty->assign( 'rss_master', $rss_master ); $smarty->assign( 'rss_data' , $rss_data ); // RSS生成用のSmartyテンプレートを取得 $template = $smarty->fetch( 'rss.tpl' ); // 生成されたSmartyテンプレートをファイルとして書き出し $filePath = '/rss/rss.xml'; $handle = fopen( $filePath, 'w' ); if( $handle ){ // テンプレートを丸ごとUTF-8に変換(自作のクラスです) fputs( $handle, $conversion->convert2utf8( $template ) ); fclose( $handle ); }
PHPスクリプトは以上です。
Smartyは単に表示させるだけでなく、テンプレートをデータとして取得する事もできます。
[余談]
「$smarty->display」は、Smartyエンジンの内部的には「fetch→echo」となってるんだそうです。(ネタ元忘・汗)
(2008-12-31追記)Smarty.class.phpのdisplayメソッドはfetchメソッドをラップしているだけなのを確認しました。
[/余談]
次に、肝心のRSS用のテンプレートファイルを作成します。
RSSファイルは他の文字コードに対応していないRSSリーダーがないとは限らないので、可能な限りUTF-8にしておきます。
また、HTMLタグは取ってしまうか、エスケープしておきます。
<?xml version="1.0" encoding="utf-8"?> <rss version="2.0"> <channel> <title>{$rss_master.site_title}</title> <link>{$rss_master.site_url}</link> <description>{$rss_master.site_description}</description> <language>ja</language> <docs>http://blogs.law.harvard.edu/tech/rss</docs> <copyright>{$rss_master.site_title}</copyright> <webMaster>{$rss_master.master_mail_addr}</webMaster> {foreach item="item_list" from=$rss_data name="item_list"} <item> <title>{$item_list.topic_title|strip_tags:false}</title> <link>{$item_list.topic_url}</link> <description>{$item_list.topic_description|strip_tags:false|jp_truncate:"200"|escape:"html"}</description> <pubDate>{$item_list.post_datetime|my_date_format:"r"}</pubDate> </item> {/foreach} </channel> </rss>
以上を実行すると、rssディレクトリ内にrss.xmlというRSSファイルが出来上がります。
後は、サイトの好きなところに、<a href="rss/rss.xml">RSS購読</a>のように書けば、RSSリーダーへ登録ができます。
RSS1.0や、Atomも情報を送るテンプレートを変えれば作成する事ができます。
ブログ以外にも、メールマガジンのようにインフォメーションや、新着情報を作ったりする事もできますね(*^_^*)
一般の方には、RSSという物自体がなんだかよく知らないって方もまだまだ少なくないと小耳に挟んだりしますが・・・そこら辺は気にせずに(^-^;
では、今回はこの辺で。
※参考URL:www.kanzaki.com: RSS(RDF Site Summary)によるサイト情報の要約と公開
※このスクリプトにおいて、動作の検証は行っていませんので、責任は一切負いません。
徳島県徳島市名東町
tel/fax 088-635-5859
営業時間 平日 10:00〜18:00
※打ち合わせ等で不在の場合は、お電話でのお問い合わせに対応できない場合がありますので、ご了承ください。
徳島・香川・愛媛・高知および大阪・京都を中心にご依頼を承ります。その他の地域でも、メール・お電話・FAX・Skype等での作業が可能であれば柔軟に対応いたします。