前回pharアーカイブを読み込みましたが、今回はそのpharアーカイブを作成してみます。
Pharクラスにはなんかすごい数のメソッドが詰まっていて一見よくわからないのですが、実際使うメソッドはほとんどありません。
Phar::getModifiedとかどんなときに使うんだよって話ですよ。
まず必要なのはphp.iniの設定変更。
pharの作成を公開サーバで行う必要は皆無なので、デフォルトでは禁止されています。
phar.readonly = 0
phar.require_hash = 0
を設定することでpharアーカイブの作成が可能になります。
http://php.net/manual/ja/phar.configuration.php#ini.phar.readonly
では作成。
実に簡単ですね。
完成したものが、前回使用したpharファイルになります。
いったん作ったpharアーカイブは、普通のファイルと同じようにファイル操作関数で操作が可能です。
Phar::addFile()の第二引数を指定すると、指定した別名で保存できます。
呼び出しすときも別名での指定となります。
またPhar::addFile()のかわりに、$phar['TestClass4.php']のように配列でも保存可能です。
今回はテスト用のどうでもいいデータでしたが、実際に活用されているものとしてはgo-pear.pharがあります。
まあこれXAMPPに入ってるやつは動かないんですけど。
画像はこちらのフリー素材を使用させていただきました。
http://www.lovecandied.com/
Pharクラスにはなんかすごい数のメソッドが詰まっていて一見よくわからないのですが、実際使うメソッドはほとんどありません。
Phar::getModifiedとかどんなときに使うんだよって話ですよ。
まず必要なのはphp.iniの設定変更。
pharの作成を公開サーバで行う必要は皆無なので、デフォルトでは禁止されています。
phar.readonly = 0
phar.require_hash = 0
を設定することでpharアーカイブの作成が可能になります。
http://php.net/manual/ja/phar.configuration.php#ini.phar.readonly
では作成。
<?php $phar = new Phar('TestClasses.phar', 0); $phar->addFile('TestClass1.php'); $phar->addFile('TestClass2.php'); $phar->addFile('TestClass3.php', 'subdir/filename.php'); //別名で保存 $phar['TestClass4.php'] = file_get_contents('TestClass4.php'); //配列形式でも保存可能 $phar->addFile('mushroom.gif'); //PHP以外も保存可能ファイル'TestClasses.phar'に5ファイルを詰め込みました。
実に簡単ですね。
完成したものが、前回使用したpharファイルになります。
いったん作ったpharアーカイブは、普通のファイルと同じようにファイル操作関数で操作が可能です。
<?php require_once('phar://TestClasses.phar/TestClass1.php'); require_once('phar://TestClasses.phar/TestClass2.php'); require_once('phar://TestClasses.phar/subdir/filename.php'); require_once('phar://TestClasses.phar/TestClass4.php'); print(file_get_contents('phar://TestClasses.phar/mushroom.gif'));画像ファイルをrequire_once()したりすると当然エラーになります。
Phar::addFile()の第二引数を指定すると、指定した別名で保存できます。
呼び出しすときも別名での指定となります。
またPhar::addFile()のかわりに、$phar['TestClass4.php']のように配列でも保存可能です。
今回はテスト用のどうでもいいデータでしたが、実際に活用されているものとしてはgo-pear.pharがあります。
まあこれXAMPPに入ってるやつは動かないんですけど。
画像はこちらのフリー素材を使用させていただきました。
http://www.lovecandied.com/
PR
pharの使用法の記事は、わざとわかりにくく書いてるんじゃないかって疑いたくなるほど何言ってるのかわかりません。
http://php.net/manual/ja/phar.using.intro.php
あとpharでググってもマニュアルのコピペサイトと「PHP5.3ではpharという機能が使えるぜ!」みたいな記事ばっかりで、実際に使ってみたという話が全然出てきません。
どういうことなんですかね。
仕方ないのでここで実際に使ってみます。
pharとは、要するに単に複数のファイルをひとつにまとめられるというだけのことみたいです。
概念としては、zipみたいに複数のファイルをひとつに固めておいて、使うときに特定のファイルだけ取り出すというイメージでしょうか。
というか実際にpharとzipの変換とかもできたりします。
とりあえず使ってみましょう。
こちらに簡単なpharファイルを用意しました。
中には5つのファイルがありますが、4つはこんな中身のないクラスです。
TestClass1.php
ではこれを読み込んでみます。
'phar://'はpharストリームラッパーで、これからpharファイルを指定することを示しています。
次の'TestClasses.phar'がpharファイル名、最後の'TestClass1.php'がpharアーカイブ中の具体的にインクルードしたいファイル名となります。
このように、pharアーカイブは一見ひとつのファイルですが実質的に複数のファイルとして取り扱うことができ、ファイルをまとめて配布したいときなどに便利です。
ちなみに中を見ればわかりますが基本的に単なるテキストなので、プログラムの隠蔽には使えません。
ところでpharってなんて読むんですかね。ぴゃー?
http://php.net/manual/ja/phar.using.intro.php
あとpharでググってもマニュアルのコピペサイトと「PHP5.3ではpharという機能が使えるぜ!」みたいな記事ばっかりで、実際に使ってみたという話が全然出てきません。
どういうことなんですかね。
仕方ないのでここで実際に使ってみます。
pharとは、要するに単に複数のファイルをひとつにまとめられるというだけのことみたいです。
概念としては、zipみたいに複数のファイルをひとつに固めておいて、使うときに特定のファイルだけ取り出すというイメージでしょうか。
というか実際にpharとzipの変換とかもできたりします。
とりあえず使ってみましょう。
こちらに簡単なpharファイルを用意しました。
中には5つのファイルがありますが、4つはこんな中身のないクラスです。
TestClass1.php
<?php public static function getClassName(){ function getClassName(){ return __CLASS__; } }もうひとつは画像ファイルです。
ではこれを読み込んでみます。
<?php require_once('phar://TestClasses.phar/TestClass1.php'); require_once('phar://TestClasses.phar/TestClass2.php'); print(TestClass1::getClassName());//"TestClass1" print(TestClass2::getClassName());//"TestClass2"非常に簡単です。
'phar://'はpharストリームラッパーで、これからpharファイルを指定することを示しています。
次の'TestClasses.phar'がpharファイル名、最後の'TestClass1.php'がpharアーカイブ中の具体的にインクルードしたいファイル名となります。
このように、pharアーカイブは一見ひとつのファイルですが実質的に複数のファイルとして取り扱うことができ、ファイルをまとめて配布したいときなどに便利です。
ちなみに中を見ればわかりますが基本的に単なるテキストなので、プログラムの隠蔽には使えません。
ところでpharってなんて読むんですかね。ぴゃー?
PHPでHTMLのパースって、実はいまいち使い勝手のいいものがないんですよね。
DOMはいったんDOMElementにしてしまうと元のHTMLに戻すのが何故か大変、SimpleXMLはアバウトなHTMLを読み込んでくれない、XMLパーサは使い方がやたらややこしい、と一長一短というか一短一短です。
そもそも何れもXML用であってHTML用ではないので当たり前かもしれませんが。
HTMLの誤りを修正するためのツールとしてTidyというものが存在しますが、どういうわけかPHPから利用することが可能です。
で、これがHTMLのパーサとしてもわりと優秀です。
さすがにDOMDocument::getElementById()ほどの便利メソッドはありませんが、比較的簡単にHTMLを掘っていくことが可能です。
とりあえず作成。
よくわからないのでサンプルのまま使用しています。
あとはTidyオブジェクトに対してHTMLタグの入れ子の順番を指定するだけで、非常に簡単に本文を取得できます。
とはいえ、例では番号を直接指定しているのですが実はあまりよくない方法です。
何故ってchild[1]やchild[4]は、単にノード内の要素を上から順に数えた値なので、タグをひとつ追加されるだけでずれてしまうのです。
本当はXPathやCSSパスで指定するのが安全なのですが、残念ながらそのような機能はないようです。
ということで子ノードを順に取得してひとつひとつ確認していくしかありませんが、ところが何故かTidyはforeachやcount()が使えません。
従ってたとえば子ノードのうち特定のタグを取得したい場合は以下のように書く必要があります。
あとUndefined propertyのNoticeが出るのも微妙。
ふと思ったが、TidyでXHTML化してSimpleXMLに突っ込んだ方が早くない?という気がしてきた。
DOMはいったんDOMElementにしてしまうと元のHTMLに戻すのが何故か大変、SimpleXMLはアバウトなHTMLを読み込んでくれない、XMLパーサは使い方がやたらややこしい、と一長一短というか一短一短です。
そもそも何れもXML用であってHTML用ではないので当たり前かもしれませんが。
HTMLの誤りを修正するためのツールとしてTidyというものが存在しますが、どういうわけかPHPから利用することが可能です。
で、これがHTMLのパーサとしてもわりと優秀です。
さすがにDOMDocument::getElementById()ほどの便利メソッドはありませんが、比較的簡単にHTMLを掘っていくことが可能です。
とりあえず作成。
<?php //HTMLを取得 $html = file_get_contents('http://yuubiseiharukana.blog.shinobi.jp/Entry/501/'); //Tidy $tidyConfig = array('indent' => true, 'output-xhtml' => true, 'wrap' => 200); $tidy = new tidy(); $tidy->parseString($html, $tidyConfig, 'utf8'); //<body>を取得 $tidyBody = $tidy->body(); //本文を取得 //CSS上のパス:html body div#whole div#contents div#main div div.entry_table div.entry_text $tidyText = $tidyBody->child[1]->child[4]->child[1]->child[1]->child[0]->child[1]; print($tidyText);Tidyのコンフィグはここらへんに一覧がありますが、いまいちなんなのかよくわかりません。
よくわからないのでサンプルのまま使用しています。
あとはTidyオブジェクトに対してHTMLタグの入れ子の順番を指定するだけで、非常に簡単に本文を取得できます。
とはいえ、例では番号を直接指定しているのですが実はあまりよくない方法です。
何故ってchild[1]やchild[4]は、単にノード内の要素を上から順に数えた値なので、タグをひとつ追加されるだけでずれてしまうのです。
本当はXPathやCSSパスで指定するのが安全なのですが、残念ながらそのような機能はないようです。
ということで子ノードを順に取得してひとつひとつ確認していくしかありませんが、ところが何故かTidyはforeachやcount()が使えません。
従ってたとえば子ノードのうち特定のタグを取得したい場合は以下のように書く必要があります。
<?php //Countable、Traversable count($tidyBody); //必ず1になる foreach($tidyBody as $key=>$val){ //$tidyBody[0]のプロパティのループになってしまう } //<div id="whole">を取得 $loop = 0; while(1){ $tmp = $tidyBody->child[$loop]; $loop++; if(!$tmp){break;} if($tmp->id !== TIDY_TAG_DIV){continue;} if($tmp->attribute['id'] !== 'whole'){continue;} $tidyWhole = $tmp; break; } //<div id="contents">を拾う $loop = 0; while(1){ $tmp = $tidyWhole->child[$loop]; $loop++; if(!$tmp){break;} if($tmp->id !== TIDY_TAG_DIV){continue;} if($tmp->attribute['id'] !== 'contents'){continue;} $tidyContents = $tmp; break; }ちょっとどうなんだって気がする。
あとUndefined propertyのNoticeが出るのも微妙。
ふと思ったが、TidyでXHTML化してSimpleXMLに突っ込んだ方が早くない?という気がしてきた。
<?php require_once('Services/JSON.php'); $data = '{"hoge" : 12345678901234567890}'; $json = new Services_JSON(); $decode = $json->decode($data); var_dump($decode->hoge);float(1.2345678901235E+19)
値が数値となっているJSONデータをServices_JSONでデコードすると結果が浮動小数となり、元のJSONデータが無くなってしまいます。
仕方ないので無理矢理文字列型でデコードできないのかと思えばできません。
数値をパースしているのはServices/JSON.phpの600行目あたり、if (is_numeric($str))の分岐内です。
処理はどうなっているかというと
if (is_numeric($str)) { return ((float)$str == (integer)$str) ? (integer)$str : (float)$str; }と見事に割り込む余地が無く、integerに入りきればint型、長い数値は自動的にfloat型になってしまいます。
数値だとしても正確な値がほしいんだよ、という場合は無理矢理継承するかソースを直接編集するしかありません。
Services/JSON.php
//メソッドひとつ追加 function setDecodeMode($decodeMode = false){ if(isset($this->decodeMode) && $this->decodeMode){ $this->decodeMode = $decodeMode; } } //Services_JSON::decode()の中を一部修正 if (is_numeric($str)) { //デコードモードが指定してあれば if($this->decodeMode){ if($this->decodeMode === SERVICES_JSON_IN_STR){ return $str; } } // Return float or int, as appropriate return ((float)$str == (integer)$str) ? (integer)$str : (float)$str; }setDecodeMode(SERVICES_JSON_IN_STR)を呼べば数値型の場合でも文字列で返ってきます。
<?php require_once('Services/JSON.php'); $data = '{"hoge" : 12345678901234567890}'; $json = new Services_JSON(); $json->setDecodeMode(SERVICES_JSON_IN_STR); $decode = $json->decode($data); var_dump($decode->hoge);実はSERVICES_JSON_IN_STRとかは元々Services_JSON内で別の意味で使用されている定数なので、あまりこういう使い方はするべきではないのですが、まあいいや。
ていうか、いいかげんPearもクラス定数使うべき。
実行すると、
string(20) "12345678901234567890"
となり無事に元の値を復元できました。
この場合元の"hoge"が数値型だったという情報は失われてしまうのですが、型が失われるのと値が失われるのとどちらがいいと言われたら前者ではないかと。
// Lookie-loo, it's a number
// This would work on its own, but I'm trying to be
// good about returning integers where appropriate:
とか書いてあるので中の人も微妙って思ってるのかね?
前回MongoDBをインストールしましたので、次はPHPから利用してみます。
まず必要なのはphpinfo()。
見るのはPHPのバージョンとCompiler欄。
何に使うかってインストールに必要です。
http://jp2.php.net/manual/ja/mongo.installation.php#mongo.installation.windows
何故かやたら細かくバージョンが分かれてるんですよね。
https://github.com/mongodb/mongo-php-driver/downloads
Gitから執筆時点での最新版、mongo-1.1.4.zipをダウンロードしました。
解凍するとバージョンがいっぱい出てきますが、手元のCompilerは「MSVC6 (Visual C++ 6.0)」となっていたので、「mongo-1.1.4-php5.3vc6ts」を選択しました。
中にあるphp_mongo.dllをエクステンションディレクトリにコピー。
次にphp.iniを編集。
extension=php_mongo.dll
って追加するだけです。
最後にApacheを再起動。
これだけでMongoDBが使用できるようになります。
早速実験。
インサートのところでありえない渡し方をしていますが、結果どうなるかというと。
・ユーザやパスワードを指定していないのにいきなり接続できた
・CREATE DATABASE testしてないのにいきなりtestデータベースを取得できた
・CREATE TABLE collect_testしてないのにいきなりcollect_testテーブルにインサートできた
・型が違うデータを同じ列に挿入できた
・それどころかレコード毎に列が合っていないでーたも同じテーブルに挿入できる
なんなんだこれは。
今までのRDBMSの常識が全く通用しません。
使用上の注意点といえば、やはりCREATE TABLE文などがない点でしょうか。
これによって任意のデータを登録しやすくなったかわりに、後先考えずに使っていると半端なくカオスなデータベースになってしまいそうです。
実際上で作ったcollect_testテーブルは、早くも使い物にならなそうな構造になっています。
NoSQLを使うにあたってはRDBMS以上の自制が必要になりそうな気がしないでもない。
で、これアクセス制御とかどうすればいいんですかね?
まず必要なのはphpinfo()。
見るのはPHPのバージョンとCompiler欄。
何に使うかってインストールに必要です。
http://jp2.php.net/manual/ja/mongo.installation.php#mongo.installation.windows
何故かやたら細かくバージョンが分かれてるんですよね。
https://github.com/mongodb/mongo-php-driver/downloads
Gitから執筆時点での最新版、mongo-1.1.4.zipをダウンロードしました。
解凍するとバージョンがいっぱい出てきますが、手元のCompilerは「MSVC6 (Visual C++ 6.0)」となっていたので、「mongo-1.1.4-php5.3vc6ts」を選択しました。
中にあるphp_mongo.dllをエクステンションディレクトリにコピー。
次にphp.iniを編集。
extension=php_mongo.dll
って追加するだけです。
最後にApacheを再起動。
これだけでMongoDBが使用できるようになります。
早速実験。
<?php //接続 //Mongo $mongo = new Mongo(); //データベース $db = $mongo->selectDB("test"); //コレクション $collection = $db->selectCollection("collect_test"); //インサート $collection->insert(array('a'=>'hoge', 'b'=>'fuga')); $collection->insert(array('a'=>1, 'c'=>array('foo','bar','baz'))); //セレクト $cursol = $collection->find(); foreach($cursol as $key=>$val){ var_dump( $val ); }コレクションはRDBMSでいうところのテーブルみたいです。
インサートのところでありえない渡し方をしていますが、結果どうなるかというと。
array(3) { ["_id"]=> object(MongoId)#7 (1) { ["$id"]=> string(24) "4e0401a5488316e413000004" } ["a"]=> string(4) "hoge" ["b"]=> string(4) "fuga" } array(3) { ["_id"]=> object(MongoId)#8 (1) { ["$id"]=> string(24) "4e0401a5488316e413000005" } ["a"]=> int(1) ["c"]=> array(3) { [0]=> string(3) "foo" [1]=> string(3) "bar" [2]=> string(3) "baz" } }普通に取得できました。
・ユーザやパスワードを指定していないのにいきなり接続できた
・CREATE DATABASE testしてないのにいきなりtestデータベースを取得できた
・CREATE TABLE collect_testしてないのにいきなりcollect_testテーブルにインサートできた
・型が違うデータを同じ列に挿入できた
・それどころかレコード毎に列が合っていないでーたも同じテーブルに挿入できる
なんなんだこれは。
今までのRDBMSの常識が全く通用しません。
使用上の注意点といえば、やはりCREATE TABLE文などがない点でしょうか。
これによって任意のデータを登録しやすくなったかわりに、後先考えずに使っていると半端なくカオスなデータベースになってしまいそうです。
実際上で作ったcollect_testテーブルは、早くも使い物にならなそうな構造になっています。
NoSQLを使うにあたってはRDBMS以上の自制が必要になりそうな気がしないでもない。
で、これアクセス制御とかどうすればいいんですかね?
MongoDBってなんだ?
http://ja.wikipedia.org/wiki/MongoDB
> MongoDBは従来の関係データベース管理システムでは簡単に解決できない多量のトランザクション処理が要求されない問題に対し設計されている。
意味逆じゃないか?
いつものことながらWikipediaの表現は全く要領を得ませんが、MongoDBはNoSQLです。
NoSQLとは、RDBMSが持っているACID特性を一部犠牲にして、性能やスケーラビリティを確保したデータベースらしいです。
サーバを追加してちょっと設定をいじるだけでふたつのサーバにデータが分散して登録されるとか、DBやテーブルを作らないでいきなりインサートできるとか、そんなようなこれまでのRDBMSではありえない使い方ができるみたい。
CREATE DATABASEするまえに容量までも指定しとかないといけないOracleなどとは対極の存在と言えましょう。
ということでローカルPCのXAMPPにNoSQLを導入してもスケーラビリティの恩恵とかこれっぽっちもないような気がするんですが、まあとりあえず試してみます。
公式サイトからダウンロード、解凍、C:\xampp\mongodbに移動。
DBを設置するフォルダ「C:\xampp\mongodb\data\db」を作成。
コマンドプロンプトから以下を実行
>mongod --install --logpath D:\xampp\mongodb\log.txt --dbpath D:\xampp\mongodb\data\db
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
お?
ググったらC++ランタイムエラーだとか。
でもインストールし直しても変わらず。
よく見てみたらパスが存在しないDドライブだったとかいう。
エラーメッセージだけでは全然わからないので気をつけよう。
>mongod --install --logpath C:\xampp\mongodb\log.txt --dbpath C:\xampp\mongodb\data\db
all output going to: C:\xampp\mongodb\log.txt
Creating service MongoDB.
Service creation successful.
Service can be started from the command line via 'net start "MongoDB"'.
インストールできました。
サービスを起動
>net start "MongoDB"
起動したら、せっかくなのでコマンドをバッチファイルにしておきましょう。
といっても単にコマンドをmongostart.batとかの名前で保存するだけです。
あとはスタートメニューにでも入れておけば好きなときに起動できます。
サービスが起動したら接続してみます。
>mongo
MongoDB shell version: 1.8.2
connecting to: test
>
何もしてないのにあっさり繋がりおった。
ユーザ作成とか権限とかはどうなってんの?
http://ja.wikipedia.org/wiki/MongoDB
> MongoDBは従来の関係データベース管理システムでは簡単に解決できない多量のトランザクション処理が要求されない問題に対し設計されている。
意味逆じゃないか?
いつものことながらWikipediaの表現は全く要領を得ませんが、MongoDBはNoSQLです。
NoSQLとは、RDBMSが持っているACID特性を一部犠牲にして、性能やスケーラビリティを確保したデータベースらしいです。
サーバを追加してちょっと設定をいじるだけでふたつのサーバにデータが分散して登録されるとか、DBやテーブルを作らないでいきなりインサートできるとか、そんなようなこれまでのRDBMSではありえない使い方ができるみたい。
CREATE DATABASEするまえに容量までも指定しとかないといけないOracleなどとは対極の存在と言えましょう。
ということでローカルPCのXAMPPにNoSQLを導入してもスケーラビリティの恩恵とかこれっぽっちもないような気がするんですが、まあとりあえず試してみます。
公式サイトからダウンロード、解凍、C:\xampp\mongodbに移動。
DBを設置するフォルダ「C:\xampp\mongodb\data\db」を作成。
コマンドプロンプトから以下を実行
>mongod --install --logpath D:\xampp\mongodb\log.txt --dbpath D:\xampp\mongodb\data\db
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
お?
ググったらC++ランタイムエラーだとか。
でもインストールし直しても変わらず。
よく見てみたらパスが存在しないDドライブだったとかいう。
エラーメッセージだけでは全然わからないので気をつけよう。
>mongod --install --logpath C:\xampp\mongodb\log.txt --dbpath C:\xampp\mongodb\data\db
all output going to: C:\xampp\mongodb\log.txt
Creating service MongoDB.
Service creation successful.
Service can be started from the command line via 'net start "MongoDB"'.
インストールできました。
サービスを起動
>net start "MongoDB"
起動したら、せっかくなのでコマンドをバッチファイルにしておきましょう。
といっても単にコマンドをmongostart.batとかの名前で保存するだけです。
あとはスタートメニューにでも入れておけば好きなときに起動できます。
サービスが起動したら接続してみます。
>mongo
MongoDB shell version: 1.8.2
connecting to: test
>
何もしてないのにあっさり繋がりおった。
ユーザ作成とか権限とかはどうなってんの?
PHPでHTMLのパースって、実はいまいち使い勝手のいいものがないんですよね。
DOMはいったんDOMElementにしてしまうと元のHTMLに戻すのが何故か大変、SimpleXMLはアバウトなHTMLを読み込んでくれない、XMLパーサは使い方がやたらややこしい、と一長一短というか一短一短です。
そもそも何れもXML用であってHTML用ではないので当たり前かもしれませんが。
HTMLの誤りを修正するためのツールとしてTidyというものが存在しますが、どういうわけかPHPから利用することが可能です。
で、これがHTMLのパーサとしてもわりと優秀です。
さすがにDOMDocument::getElementById()ほどの便利メソッドはありませんが、比較的簡単にHTMLを掘っていくことが可能です。
とりあえず作成。
<?php //HTMLを取得 $html = file_get_contents('http://yuubiseiharukana.blog.shinobi.jp/Entry/501/'); //Tidy $tidyConfig = array('indent' => true, 'output-xhtml' => true, 'wrap' => 200); $tidy = new tidy(); $tidy->parseString($html, $tidyConfig, 'utf8'); //<body>を取得 $tidyBody = $tidy->body(); //本文を取得 //CSS上のパス:html body div#whole div#contents div#main div div.entry_table div.entry_text $tidyText = $tidyBody->child[1]->child[4]->child[1]->child[1]->child[0]->child[1]; print($tidyText);Tidyのコンフィグはここらへんに一覧がありますが、いまいちなんなのかよくわかりません。
よくわからないのでサンプルのまま使用しています。
あとはTidyオブジェクトに対してHTMLタグの入れ子の順番を指定するだけで、非常に簡単に本文を取得できます。
とはいえ、例では番号を直接指定しているのですが実はあまりよくない方法です。
何故ってchild[1]やchild[4]は、単にノード内の要素を上から順に数えた値なので、タグをひとつ追加されるだけでずれてしまうのです。
本当はXPathやCSSパスで指定するのが安全なのですが、残念ながらそのような機能はないようです。
ということで子ノードを順に取得してひとつひとつ確認していくしかありませんが、ところが何故かTidyはforeachやcount()が使えません。
従ってたとえば子ノードのうち特定のタグを取得したい場合は以下のように書く必要があります。
<?php //Countable、Traversable count($tidyBody); //必ず1になる foreach($tidyBody as $key=>$val){ //$tidyBody[0]のプロパティのループになってしまう } //<div id="whole">を取得 $loop = 0; while(1){ $tmp = $tidyBody->child[$loop]; $loop++; if(!$tmp){break;} if($tmp->id !== TIDY_TAG_DIV){continue;} if($tmp->attribute['id'] !== 'whole'){continue;} $tidyWhole = $tmp; break; } //<div id="contents">を拾う $loop = 0; while(1){ $tmp = $tidyWhole->child[$loop]; $loop++; if(!$tmp){break;} if($tmp->id !== TIDY_TAG_DIV){continue;} if($tmp->attribute['id'] !== 'contents'){continue;} $tidyContents = $tmp; break; }ちょっとどうなんだって気がする。
あとUndefined propertyのNoticeが出るのも微妙。
というかTidyでXHTML化してSimpleXMLに突っ込んだ方が早くない?という気がしてきた。
先日Services_Shitarabaを1.0.3から1.0.4にバージョンアップしました。
といってもほぼ内部的変更だけで、これまでのソースとはほぼ互換性があります。
もし1.0.3までを利用していた場合、一箇所だけ修正が必要です。
コンストラクタをファクトリメソッドに変更してください。
それ以外は全く同じに使えるはずです。
new Services_Shitaraba($bbsGenre, $bbsNum, $bbsPassword);
↓
Services_Shitaraba::factory($bbsGenre, $bbsNum, $bbsPassword);
修正内容。
・奇妙な作りだったのを素直な感じに修正
・ファクトリメソッドと抽象クラスでしたらば以外のBBSにも対応
中身は誰かが書いてくれるにちがいない。
・ホスト名で削除機能を実現
管理画面のHTMLをパースするためにtidyを使用しています。
すぐに動作できるサンプルソースを用意しました。
http://blog.cnobi.jp/v1/blog/user/8f14c19eb01af9c2e80c7f2ad07dc8ca/1307544614
config.phpに設定を書けば動作するはずです。
Smartyのパーミッションとかは自分で調べてください。
中は物凄い適当なのであまり見ないでください。
以下の機能があります。
・1000行ったスレを削除
・NGワードで削除
・ホスト名で削除
したらばBBSの管理画面では面倒な、一括削除を簡単に行うことに特化しています。
しかし昔のソースを見ていたら色々ツッコミどころが多いですね。
setGerneってなんだよ。
今回のも本当はスレッド単位、レス単位でクラス化して楽に扱いたいとかそもそも命名をやりなおしたいとかあったんですが、面倒だったのでそのままです。
ていうか今のソースもServices_Bloggingからコピペした跡とか残ってるしこれはひどい。
といってもほぼ内部的変更だけで、これまでのソースとはほぼ互換性があります。
もし1.0.3までを利用していた場合、一箇所だけ修正が必要です。
コンストラクタをファクトリメソッドに変更してください。
それ以外は全く同じに使えるはずです。
new Services_Shitaraba($bbsGenre, $bbsNum, $bbsPassword);
↓
Services_Shitaraba::factory($bbsGenre, $bbsNum, $bbsPassword);
修正内容。
・奇妙な作りだったのを素直な感じに修正
・ファクトリメソッドと抽象クラスでしたらば以外のBBSにも対応
中身は誰かが書いてくれるにちがいない。
・ホスト名で削除機能を実現
管理画面のHTMLをパースするためにtidyを使用しています。
すぐに動作できるサンプルソースを用意しました。
http://blog.cnobi.jp/v1/blog/user/8f14c19eb01af9c2e80c7f2ad07dc8ca/1307544614
config.phpに設定を書けば動作するはずです。
Smartyのパーミッションとかは自分で調べてください。
中は物凄い適当なのであまり見ないでください。
以下の機能があります。
・1000行ったスレを削除
・NGワードで削除
・ホスト名で削除
したらばBBSの管理画面では面倒な、一括削除を簡単に行うことに特化しています。
しかし昔のソースを見ていたら色々ツッコミどころが多いですね。
setGerneってなんだよ。
今回のも本当はスレッド単位、レス単位でクラス化して楽に扱いたいとかそもそも命名をやりなおしたいとかあったんですが、面倒だったのでそのままです。
ていうか今のソースもServices_Bloggingからコピペした跡とか残ってるしこれはひどい。
物凄い今更ですが、↓こちらで括弧の数勝負というのをやっていたのを発見しました。
http://e-arrows.sakura.ne.jp/2010/08/is-lisp-really-has-too-many-parenthesis.html
残念ながら我らがPHPのコードがありませんので、底辺ぺちぱーの端くれとして挑戦してみましょう。
プログラムの仕様は以下の通り。
・指定数までの階乗(factorial)を表示するプログラム
・指定数はコマンドライン引数で指定できる
・20を指定したときの表示は以下
・引数が指定されなかったときは1の階乗
・括弧として(){}[]をカウント
ん?「指定数はコマンドライン引数で指定できる」?
大丈夫!PHPだってコマンドラインで実行できるんです!
まあ普通そんなの使うくらいならPerlやシェルスクリプトで書きますけどね。
まずは素直に書いてみる。
括弧の数は20個となりました。
では実行。
> C:\xampp\php\php.exe ./factorial.php 20
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
11! = 39916800
12! = 479001600
13! = 6227020800
14! = 87178291200
15! = 1307674368000
16! = 20922789888000
17! = 3.55687428096E+14
18! = 6.402373705728E+15
19! = 1.2164510040883E+17
20! = 2.4329020081766E+18
はう!32ビット整数値の上限を超えてしまった!
いやー64ビットOSだったらこれで動くはずなんだけどなー
まあGMPとか使っての検証はまた今度ということで、とりあえずこのまま進めてみます。
表記上不要な要素とかを減らしてみます。
これがエラーの発生しない最低限の書き方だと思います。
ではエラーとか気にせずとりあえず動けばいいやレベルの書き方で。
括弧の数は…6個か、あまり減ってないな。
PHPは使い方が平易って部分を追求してるので、括弧の定義変更みたいな裏技チックな書き方があんまりできないんですよね。
まあ、できるけど私が知らないだけって可能性もありますが。
引数のデフォルト1ってのが厄介で、このせいでどうしても括弧二つでは足りないんですよねえ。
どうしてもデフォルトが0になってしまうので。
ん、ではこういうのはどうだろう。
なに?なんか卑怯?
まあそんな気がしないでもない。
これ以上減らすのはさすがにPHPの言語仕様上無理なんじゃないかなあと思います。
とか言ってさっくり減らされたりしたら恥ずかしいですが。
http://e-arrows.sakura.ne.jp/2010/08/is-lisp-really-has-too-many-parenthesis.html
残念ながら我らがPHPのコードがありませんので、底辺ぺちぱーの端くれとして挑戦してみましょう。
プログラムの仕様は以下の通り。
・指定数までの階乗(factorial)を表示するプログラム
・指定数はコマンドライン引数で指定できる
・20を指定したときの表示は以下
・引数が指定されなかったときは1の階乗
・括弧として(){}[]をカウント
ん?「指定数はコマンドライン引数で指定できる」?
大丈夫!PHPだってコマンドラインで実行できるんです!
まあ普通そんなの使うくらいならPerlやシェルスクリプトで書きますけどね。
まずは素直に書いてみる。
//引数 if(isset($argv[1])){ $count = (int)$argv[1]; }else{ $count = 1; } //階乗 $factorial = 1; for($loop=1; $loop<=$count; $loop++){ $factorial = $factorial * $loop; print($loop . "! = " . $factorial . "\n"); }簡単ですね。
括弧の数は20個となりました。
では実行。
> C:\xampp\php\php.exe ./factorial.php 20
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
11! = 39916800
12! = 479001600
13! = 6227020800
14! = 87178291200
15! = 1307674368000
16! = 20922789888000
17! = 3.55687428096E+14
18! = 6.402373705728E+15
19! = 1.2164510040883E+17
20! = 2.4329020081766E+18
はう!32ビット整数値の上限を超えてしまった!
いやー64ビットOSだったらこれで動くはずなんだけどなー
まあGMPとか使っての検証はまた今度ということで、とりあえずこのまま進めてみます。
表記上不要な要素とかを減らしてみます。
//引数 $count = empty($argv[1]) ? 1 : (int)$argv[1]; //階乗 $factorial = 1; for($loop=1; $loop<=$count; $loop++): $factorial = $factorial * $loop; print $loop . "! = " . $factorial . "\n"; endfor;括弧は半分の10個まで減りました。
これがエラーの発生しない最低限の書き方だと思います。
ではエラーとか気にせずとりあえず動けばいいやレベルの書き方で。
//引数 $count = $argv[1] ? $argv[1] : 1; //階乗 $factorial = 1; for($loop=1; $loop<=$count; $loop++) print $loop . "! = " . $factorial = $factorial * $loop . "\n";引数が無い場合Undefined offsetのNoticeが発生しますがとりあえず動きます。
括弧の数は…6個か、あまり減ってないな。
PHPは使い方が平易って部分を追求してるので、括弧の定義変更みたいな裏技チックな書き方があんまりできないんですよね。
まあ、できるけど私が知らないだけって可能性もありますが。
引数のデフォルト1ってのが厄介で、このせいでどうしても括弧二つでは足りないんですよねえ。
どうしてもデフォルトが0になってしまうので。
ん、ではこういうのはどうだろう。
$factorial = 1; print "1! = 1\n"; for($loop=2; $loop<=$argv[1]; $loop++) print $loop . "! = " . $factorial = $factorial * $loop . "\n";括弧が4つまで減ったよ!
なに?なんか卑怯?
まあそんな気がしないでもない。
これ以上減らすのはさすがにPHPの言語仕様上無理なんじゃないかなあと思います。
とか言ってさっくり減らされたりしたら恥ずかしいですが。
前回__autoload()を紹介しましたが、__autoload()はひとつしか登録できません。
spl_autoload_register()を使うとオートローディングの実装を複数登録できます。
通常はPearライブラリだけ入れておくくらいでいいのではないでしょうか。
spl_autoload_register()を使うとオートローディングの実装を複数登録できます。
<?php //オートロード実装その1 function autoload1($className){ require_once('Zend/Loader.php'); try{ @Zend_Loader::loadClass($className); }catch(Exception $e){} } //オートロード実装その2 function autoload2($className){ class AutoLoad2{} } //spl_autoload_register spl_autoload_register('autoload1'); spl_autoload_register('autoload2'); //インクルードしていないクラスをインスタンス化 new Zend_Filter_Word_DashToSeparator('/'); //autoload2内で定義されているクラスをインスタンス化 new AutoLoad2(); //現在登録されているオートロード実装を確認 spl_autoload_functions();オートロードを駆使すれば毎回インクルードを行わずにすむようにはなりますが、駆使するほどたくさん登録すると却ってわかりにくくなりそうです。
通常はPearライブラリだけ入れておくくらいでいいのではないでしょうか。